]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
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> | |
fa7225c8 | 33 | #include <security_utilities/casts.h> |
d8f41ccd A |
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"; | |
d8f41ccd A |
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); | |
b54c578e A |
73 | unsigned long value = defaultValue; |
74 | sscanf(s.c_str(), "%lu", &value); | |
d8f41ccd A |
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]; | |
fa7225c8 | 99 | snprintf(buffer, sizeof(buffer), "%u\n", value); |
d8f41ccd A |
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 | ||
fa7225c8 | 138 | mLastSubservice = int_cast<ssize_t, uint32>(getFile(path(lastSSIDFile), 1)); |
d8f41ccd A |
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 | ||
fa7225c8 | 146 | secinfo("tokencache", "token cache rooted at %s (last ssid=%u, uid/gid=%d/%d)", |
d8f41ccd A |
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); | |
fa7225c8 A |
191 | if ((mSubservice = int_cast<unsigned long, uint32>(getFile(path(ssidFile), 0)))) { |
192 | secinfo("tokencache", "found token \"%s\" ssid=%u", tokenUid.c_str(), mSubservice); | |
d8f41ccd A |
193 | init(existing); |
194 | } else { | |
195 | mSubservice = cache.allocateSubservice(); // allocate new, unique ssid... | |
196 | putFile(path(ssidFile), mSubservice); // ... and save it in cache | |
fa7225c8 | 197 | secinfo("tokencache", "new token \"%s\" ssid=%u", tokenUid.c_str(), mSubservice); |
d8f41ccd A |
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), | |
fa7225c8 | 211 | "%s/temporary:%u", tokensDir, mSubservice); |
d8f41ccd A |
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 | |
fa7225c8 | 215 | secinfo("tokencache", "temporary token \"%s\" ssid=%u", rootForm, mSubservice); |
d8f41ccd A |
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) | |
fa7225c8 | 238 | secinfo("tokencache", "@@@ should delete the cache directory here..."); |
d8f41ccd A |
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 | } |