1/* 2 * Copyright (c) 2004-2006,2008 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// tokencache - persistent (on-disk) hardware token directory 27// 28// Here's the basic disk layout, rooted at /var/db/TokenCache (or $TOKENCACHE): 29// TBA 30// 31#include "tokencache.h" 32#include <security_utilities/unix++.h> 33#include <pwd.h> 34#include <grp.h> 35 36using namespace UnixPlusPlus; 37 38 39// 40// Here are the uid/gid values we assign to token daemons and their cache files 41// 42#define TOKEND_UID "tokend" 43#define TOKEND_GID "tokend" 44#define TOKEND_UID_FALLBACK uid_t(-2) 45#define TOKEND_GID_FALLBACK gid_t(-2) 46 47 48// 49// Fixed relative file paths 50// 51 52// relative to cache root (use cache->path()) 53static const char configDir[] = "config"; 54static const char lastSSIDFile[] = "config/lastSSID"; 55static const char tokensDir[] = "tokens"; 56 57// relative to token directory (use token->path()) 58static const char ssidFile[] = "SSID"; 59static const char workDir[] = "work"; 60static const char cacheDir[] = "cache"; 61 62 63// 64// Internal file I/O helpers. These read/write entire files. 65// Note that the defaulted read functions do NOT write the default 66// to disk; they work fine in read-only disk areas. 67// 68static unsigned long getFile(const string &path, unsigned long defaultValue) 69{ 70 try { 71 AutoFileDesc fd(path, O_RDONLY, FileDesc::modeMissingOk); 72 if (fd) { 73 string s; fd.readAll(s); 74 unsigned long value; sscanf(s.c_str(), "%lu", &value); 75 return value; 76 } 77 } catch (...) { 78 } 79 return defaultValue; 80} 81 82static string getFile(const string &path, const string &defaultValue) 83{ 84 try { 85 AutoFileDesc fd(path, O_RDONLY, FileDesc::modeMissingOk); 86 if (fd) { 87 string s; fd.readAll(s); 88 return s; 89 } 90 } catch (...) { 91 } 92 return defaultValue; 93} 94 95 96static void putFile(const string &path, uint32 value) 97{ 98 char buffer[64]; 99 snprintf(buffer, sizeof(buffer), "%ld\n", value); 100 AutoFileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(buffer); 101} 102 103static void putFile(const string &path, const string &value) 104{ 105 AutoFileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(value); 106} 107 108 109// 110// The "rooted tree" utility class 111// 112void Rooted::root(const string &r) 113{ 114 assert(mRoot.empty()); // can't re-set this 115 mRoot = r; 116} 117 118string Rooted::path(const char *sub) const 119{ 120 if (sub == NULL) 121 return mRoot; 122 return mRoot + "/" + sub; 123} 124 125 126// 127// Open a TokenCache. 128// If the cache does not exist at the path given, initialize it. 129// If that fails, throw an exception. 130// 131TokenCache::TokenCache(const char *where) 132 : Rooted(where), mLastSubservice(0) 133{ 134 makedir(root(), O_CREAT, 0711, securityd); 135 makedir(path(configDir), O_CREAT, 0700, securityd); 136 makedir(path(tokensDir), O_CREAT, 0711, securityd); 137 138 mLastSubservice = getFile(path(lastSSIDFile), 1); 139 140 // identify uid/gid for token daemons 141 struct passwd *pw = getpwnam(TOKEND_UID); 142 mTokendUid = pw ? pw->pw_uid : TOKEND_UID_FALLBACK; 143 struct group *gr = getgrnam(TOKEND_GID); 144 mTokendGid = gr ? gr->gr_gid : TOKEND_GID_FALLBACK; 145 146 secdebug("tokencache", "token cache rooted at %s (last ssid=%ld, uid/gid=%d/%d)", 147 root().c_str(), mLastSubservice, mTokendUid, mTokendGid); 148} 149 150TokenCache::~TokenCache() 151{ 152} 153 154 155// 156// Get a new, unused subservice id number. 157// Update the tracking file so we won't hand it out again (ever) within this cache. 158// 159uint32 TokenCache::allocateSubservice() 160{ 161 putFile(path(lastSSIDFile), ++mLastSubservice); 162 return mLastSubservice; 163} 164 165 166// 167// A slightly souped-up UnixPlusPlus::makedir 168// 169void TokenCache::makedir(const char *path, int flags, mode_t mode, Owner owner) 170{ 171 UnixPlusPlus::makedir(path, flags, mode); 172 switch(owner) { 173 case securityd: 174 // leave it alone; we own it alrady 175 break; 176 case tokend: 177 ::chown(path, tokendUid(), tokendGid()); 178 break; 179 } 180} 181 182 183// 184// Make a cache entry from a valid tokenUid. 185// This will locate an existing entry or make a new one. 186// 187TokenCache::Token::Token(TokenCache &c, const string &tokenUid) 188 : Rooted(c.path(string(tokensDir) + "/" + tokenUid)), cache(c) 189{ 190 cache.makedir(root(), O_CREAT, 0711, securityd); 191 if (mSubservice = getFile(path(ssidFile), 0)) { 192 secdebug("tokencache", "found token \"%s\" ssid=%ld", tokenUid.c_str(), mSubservice); 193 init(existing); 194 } else { 195 mSubservice = cache.allocateSubservice(); // allocate new, unique ssid... 196 putFile(path(ssidFile), mSubservice); // ... and save it in cache 197 secdebug("tokencache", "new token \"%s\" ssid=%ld", tokenUid.c_str(), mSubservice); 198 init(created); 199 } 200} 201 202 203// 204// Make a cache entry that is temporary and will never be reused. 205// 206TokenCache::Token::Token(TokenCache &c) 207 : cache(c) 208{ 209 mSubservice = cache.allocateSubservice(); // new, unique id 210 char rootForm[30]; snprintf(rootForm, sizeof(rootForm), 211 "%s/temporary:%ld", tokensDir, mSubservice); 212 root(cache.path(rootForm)); 213 cache.makedir(root(), O_CREAT | O_EXCL, 0711, securityd); 214 putFile(path(ssidFile), mSubservice); // ... and save it in cache 215 secdebug("tokencache", "temporary token \"%s\" ssid=%ld", rootForm, mSubservice); 216 init(temporary); 217} 218 219 220// 221// Common constructor setup code 222// 223void TokenCache::Token::init(Type type) 224{ 225 mType = type; 226 cache.makedir(workPath(), O_CREAT, 0700, tokend); 227 cache.makedir(cachePath(), O_CREAT, 0700, tokend); 228} 229 230 231// 232// The Token destructor might clean or preen a bit, but shouldn't take 233// too long (or too much effort). 234// 235TokenCache::Token::~Token() 236{ 237 if (type() == temporary) 238 secdebug("tokencache", "@@@ should delete the cache directory here..."); 239} 240 241 242// 243// Attributes of TokenCache::Tokens 244// 245string TokenCache::Token::workPath() const 246{ 247 return path("Work"); 248} 249 250string TokenCache::Token::cachePath() const 251{ 252 return path("Cache"); 253} 254 255 256string TokenCache::Token::printName() const 257{ 258 return getFile(path("PrintName"), ""); 259} 260 261void TokenCache::Token::printName(const string &name) 262{ 263 putFile(path("PrintName"), name); 264} 265