/* * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // // tokencache - persistent (on-disk) hardware token directory // // Here's the basic disk layout, rooted at /var/db/TokenCache (or $TOKENCACHE): // TBA // #include "tokencache.h" #include #include #include using namespace UnixPlusPlus; // // Here are the uid/gid values we assign to token daemons and their cache files // #define TOKEND_UID "tokend" #define TOKEND_GID "tokend" #define TOKEND_UID_FALLBACK uid_t(-2) #define TOKEND_GID_FALLBACK gid_t(-2) // // Fixed relative file paths // // relative to cache root (use cache->path()) static const char configDir[] = "config"; static const char lastSSIDFile[] = "config/lastSSID"; static const char tokensDir[] = "tokens"; // relative to token directory (use token->path()) static const char ssidFile[] = "SSID"; static const char workDir[] = "work"; static const char cacheDir[] = "cache"; // // Internal file I/O helpers. These read/write entire files. // Note that the defaulted read functions do NOT write the default // to disk; they work fine in read-only disk areas. // static uint32 getFile(const string &path, uint32 defaultValue) { try { FileDesc fd(path); string s; fd.readAll(s); uint32 value; sscanf(s.c_str(), "%ld", &value); return value; } catch (...) { return defaultValue; } } static string getFile(const string &path, const string &defaultValue) { try { FileDesc fd(path); 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), "%ld\n", value); FileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(buffer); } static void putFile(const string &path, const string &value) { FileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(value); } // // The "rooted tree" utility class // void Rooted::root(const string &r) { assert(mRoot.empty()); // can't re-set this mRoot = r; } string Rooted::path(const char *sub) const { if (sub == NULL) return mRoot; return mRoot + "/" + sub; } // // Open a TokenCache. // If the cache does not exist at the path given, initialize it. // If that fails, throw an exception. // 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); // get the path for the SSID file. Don't call getFile unless the file exists (avoids exception overhead) string idFilePath = path (lastSSIDFile); struct stat st; if (stat (idFilePath.c_str (), &st) == -1) { mLastSubservice = 1; } else { mLastSubservice = getFile(idFilePath, 1); } // identify uid/gid for token daemons 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; secdebug("tokencache", "token cache rooted at %s (last ssid=%ld, uid/gid=%d/%d)", root().c_str(), mLastSubservice, mTokendUid, mTokendGid); } TokenCache::~TokenCache() { } // // Get a new, unused subservice id number. // Update the tracking file so we won't hand it out again (ever) within this cache. // uint32 TokenCache::allocateSubservice() { putFile(path(lastSSIDFile), ++mLastSubservice); return mLastSubservice; } // // A slightly souped-up UnixPlusPlus::makedir // void TokenCache::makedir(const char *path, int flags, mode_t mode, Owner owner) { UnixPlusPlus::makedir(path, flags, mode); switch(owner) { case securityd: // leave it alone; we own it alrady break; case tokend: ::chown(path, tokendUid(), tokendGid()); break; } } // // Make a cache entry from a valid tokenUid. // This will locate an existing entry or make a new one. // 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 = getFile(path(ssidFile), 0)) { secdebug("tokencache", "found token \"%s\" ssid=%ld", tokenUid.c_str(), mSubservice); init(existing); } else { mSubservice = cache.allocateSubservice(); // allocate new, unique ssid... putFile(path(ssidFile), mSubservice); // ... and save it in cache secdebug("tokencache", "new token \"%s\" ssid=%ld", tokenUid.c_str(), mSubservice); init(created); } } // // Make a cache entry that is temporary and will never be reused. // TokenCache::Token::Token(TokenCache &c) : cache(c) { mSubservice = cache.allocateSubservice(); // new, unique id char rootForm[30]; snprintf(rootForm, sizeof(rootForm), "%s/temporary:%ld", tokensDir, mSubservice); root(cache.path(rootForm)); cache.makedir(root(), O_CREAT | O_EXCL, 0711, securityd); putFile(path(ssidFile), mSubservice); // ... and save it in cache secdebug("tokencache", "temporary token \"%s\" ssid=%ld", rootForm, mSubservice); init(temporary); } // // Common constructor setup code // void TokenCache::Token::init(Type type) { mType = type; cache.makedir(workPath(), O_CREAT, 0700, tokend); cache.makedir(cachePath(), O_CREAT, 0700, tokend); } // // The Token destructor might clean or preen a bit, but shouldn't take // too long (or too much effort). // TokenCache::Token::~Token() { if (type() == temporary) secdebug("tokencache", "@@@ should delete the cache directory here..."); } // // Attributes of TokenCache::Tokens // 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); }