#include "tokencache.h"
#include <security_utilities/unix++.h>
#include <security_utilities/casts.h>
#include <pwd.h>
#include <grp.h>
using namespace UnixPlusPlus;
#define TOKEND_UID "tokend"
#define TOKEND_GID "tokend"
#define TOKEND_UID_FALLBACK uid_t(-2)
#define TOKEND_GID_FALLBACK gid_t(-2)
static const char configDir[] = "config";
static const char lastSSIDFile[] = "config/lastSSID";
static const char tokensDir[] = "tokens";
static const char ssidFile[] = "SSID";
static unsigned long getFile(const string &path, unsigned long defaultValue)
{
try {
AutoFileDesc fd(path, O_RDONLY, FileDesc::modeMissingOk);
if (fd) {
string s; fd.readAll(s);
unsigned long value; sscanf(s.c_str(), "%lu", &value);
return value;
}
} catch (...) {
}
return defaultValue;
}
static string getFile(const string &path, const string &defaultValue)
{
try {
AutoFileDesc fd(path, O_RDONLY, FileDesc::modeMissingOk);
if (fd) {
string s; fd.readAll(s);
return s;
}
} catch (...) {
}
return defaultValue;
}
static void putFile(const string &path, uint32 value)
{
char buffer[64];
snprintf(buffer, sizeof(buffer), "%u\n", value);
AutoFileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(buffer);
}
static void putFile(const string &path, const string &value)
{
AutoFileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(value);
}
void Rooted::root(const string &r)
{
assert(mRoot.empty()); mRoot = r;
}
string Rooted::path(const char *sub) const
{
if (sub == NULL)
return mRoot;
return mRoot + "/" + sub;
}
TokenCache::TokenCache(const char *where)
: Rooted(where), mLastSubservice(0)
{
makedir(root(), O_CREAT, 0711, securityd);
makedir(path(configDir), O_CREAT, 0700, securityd);
makedir(path(tokensDir), O_CREAT, 0711, securityd);
mLastSubservice = int_cast<ssize_t, uint32>(getFile(path(lastSSIDFile), 1));
struct passwd *pw = getpwnam(TOKEND_UID);
mTokendUid = pw ? pw->pw_uid : TOKEND_UID_FALLBACK;
struct group *gr = getgrnam(TOKEND_GID);
mTokendGid = gr ? gr->gr_gid : TOKEND_GID_FALLBACK;
secinfo("tokencache", "token cache rooted at %s (last ssid=%u, uid/gid=%d/%d)",
root().c_str(), mLastSubservice, mTokendUid, mTokendGid);
}
TokenCache::~TokenCache()
{
}
uint32 TokenCache::allocateSubservice()
{
putFile(path(lastSSIDFile), ++mLastSubservice);
return mLastSubservice;
}
void TokenCache::makedir(const char *path, int flags, mode_t mode, Owner owner)
{
UnixPlusPlus::makedir(path, flags, mode);
switch(owner) {
case securityd:
break;
case tokend:
::chown(path, tokendUid(), tokendGid());
break;
}
}
TokenCache::Token::Token(TokenCache &c, const string &tokenUid)
: Rooted(c.path(string(tokensDir) + "/" + tokenUid)), cache(c)
{
cache.makedir(root(), O_CREAT, 0711, securityd);
if ((mSubservice = int_cast<unsigned long, uint32>(getFile(path(ssidFile), 0)))) {
secinfo("tokencache", "found token \"%s\" ssid=%u", tokenUid.c_str(), mSubservice);
init(existing);
} else {
mSubservice = cache.allocateSubservice(); putFile(path(ssidFile), mSubservice); secinfo("tokencache", "new token \"%s\" ssid=%u", tokenUid.c_str(), mSubservice);
init(created);
}
}
TokenCache::Token::Token(TokenCache &c)
: cache(c)
{
mSubservice = cache.allocateSubservice(); char rootForm[30]; snprintf(rootForm, sizeof(rootForm),
"%s/temporary:%u", tokensDir, mSubservice);
root(cache.path(rootForm));
cache.makedir(root(), O_CREAT | O_EXCL, 0711, securityd);
putFile(path(ssidFile), mSubservice); secinfo("tokencache", "temporary token \"%s\" ssid=%u", rootForm, mSubservice);
init(temporary);
}
void TokenCache::Token::init(Type type)
{
mType = type;
cache.makedir(workPath(), O_CREAT, 0700, tokend);
cache.makedir(cachePath(), O_CREAT, 0700, tokend);
}
TokenCache::Token::~Token()
{
if (type() == temporary)
secinfo("tokencache", "@@@ should delete the cache directory here...");
}
string TokenCache::Token::workPath() const
{
return path("Work");
}
string TokenCache::Token::cachePath() const
{
return path("Cache");
}
string TokenCache::Token::printName() const
{
return getFile(path("PrintName"), "");
}
void TokenCache::Token::printName(const string &name)
{
putFile(path("PrintName"), name);
}