tokencache.cpp
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 <security_utilities/casts.h> 34 #include <pwd.h> 35 #include <grp.h> 36 37 using namespace UnixPlusPlus; 38 39 40 // 41 // Here are the uid/gid values we assign to token daemons and their cache files 42 // 43 #define TOKEND_UID "tokend" 44 #define TOKEND_GID "tokend" 45 #define TOKEND_UID_FALLBACK uid_t(-2) 46 #define TOKEND_GID_FALLBACK gid_t(-2) 47 48 49 // 50 // Fixed relative file paths 51 // 52 53 // relative to cache root (use cache->path()) 54 static const char configDir[] = "config"; 55 static const char lastSSIDFile[] = "config/lastSSID"; 56 static const char tokensDir[] = "tokens"; 57 58 // relative to token directory (use token->path()) 59 static const char ssidFile[] = "SSID"; 60 61 62 // 63 // Internal file I/O helpers. These read/write entire files. 64 // Note that the defaulted read functions do NOT write the default 65 // to disk; they work fine in read-only disk areas. 66 // 67 static unsigned long getFile(const string &path, unsigned long defaultValue) 68 { 69 try { 70 AutoFileDesc fd(path, O_RDONLY, FileDesc::modeMissingOk); 71 if (fd) { 72 string s; fd.readAll(s); 73 unsigned long value = defaultValue; 74 sscanf(s.c_str(), "%lu", &value); 75 return value; 76 } 77 } catch (...) { 78 } 79 return defaultValue; 80 } 81 82 static 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 96 static void putFile(const string &path, uint32 value) 97 { 98 char buffer[64]; 99 snprintf(buffer, sizeof(buffer), "%u\n", value); 100 AutoFileDesc(path, O_WRONLY | O_CREAT | O_TRUNC).writeAll(buffer); 101 } 102 103 static 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 // 112 void Rooted::root(const string &r) 113 { 114 assert(mRoot.empty()); // can't re-set this 115 mRoot = r; 116 } 117 118 string 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 // 131 TokenCache::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 = int_cast<ssize_t, uint32>(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 secinfo("tokencache", "token cache rooted at %s (last ssid=%u, uid/gid=%d/%d)", 147 root().c_str(), mLastSubservice, mTokendUid, mTokendGid); 148 } 149 150 TokenCache::~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 // 159 uint32 TokenCache::allocateSubservice() 160 { 161 putFile(path(lastSSIDFile), ++mLastSubservice); 162 return mLastSubservice; 163 } 164 165 166 // 167 // A slightly souped-up UnixPlusPlus::makedir 168 // 169 void 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 // 187 TokenCache::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 = int_cast<unsigned long, uint32>(getFile(path(ssidFile), 0)))) { 192 secinfo("tokencache", "found token \"%s\" ssid=%u", 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 secinfo("tokencache", "new token \"%s\" ssid=%u", 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 // 206 TokenCache::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:%u", 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 secinfo("tokencache", "temporary token \"%s\" ssid=%u", rootForm, mSubservice); 216 init(temporary); 217 } 218 219 220 // 221 // Common constructor setup code 222 // 223 void 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 // 235 TokenCache::Token::~Token() 236 { 237 if (type() == temporary) 238 secinfo("tokencache", "@@@ should delete the cache directory here..."); 239 } 240 241 242 // 243 // Attributes of TokenCache::Tokens 244 // 245 string TokenCache::Token::workPath() const 246 { 247 return path("Work"); 248 } 249 250 string TokenCache::Token::cachePath() const 251 { 252 return path("Cache"); 253 } 254 255 256 string TokenCache::Token::printName() const 257 { 258 return getFile(path("PrintName"), ""); 259 } 260 261 void TokenCache::Token::printName(const string &name) 262 { 263 putFile(path("PrintName"), name); 264 }