/ securityd / src / tokencache.cpp
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  }