]> git.saurik.com Git - apple/securityd.git/blob - src/AuthorizationDBPlist.cpp
securityd-36975.tar.gz
[apple/securityd.git] / src / AuthorizationDBPlist.cpp
1 /*
2 * Copyright (c) 2003-2004 Apple Computer, 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 * AuthorizationDBPlist.cpp
24 * Security
25 *
26 */
27
28 #include "AuthorizationDBPlist.h"
29 #include <security_utilities/logging.h>
30
31 // mLock is held when the database is changed
32 // mReadWriteLock is held when the file on disk is changed
33 // during load(), save() and parseConfig() mLock is assumed
34
35 namespace Authorization {
36
37 AuthorizationDBPlist::AuthorizationDBPlist(const char *configFile) :
38 mFileName(configFile), mLastChecked(DBL_MIN)
39 {
40 memset(&mRulesFileMtimespec, 0, sizeof(mRulesFileMtimespec));
41 }
42
43 void AuthorizationDBPlist::sync(CFAbsoluteTime now)
44 {
45 if (mRules.empty()) {
46 StLock<Mutex> _(mLock);
47 load();
48 } else {
49 // Don't do anything if we checked the timestamp less than 5 seconds ago
50 if (mLastChecked > now - 5.0) {
51 secdebug("authdb", "no sync: last reload %.0f + 5 > %.0f",
52 mLastChecked, now);
53 return;
54 }
55
56 {
57 struct stat st;
58 {
59 StLock<Mutex> _(mReadWriteLock);
60 if (stat(mFileName.c_str(), &st)) {
61 Syslog::error("Stating rules file \"%s\": %s", mFileName.c_str(),
62 strerror(errno));
63 return;
64 }
65 }
66
67 if (memcmp(&st.st_mtimespec, &mRulesFileMtimespec, sizeof(mRulesFileMtimespec))) {
68 StLock<Mutex> _(mLock);
69 load();
70 }
71 }
72 }
73 }
74
75 void AuthorizationDBPlist::save()
76 {
77 if (!mConfig)
78 return;
79
80 StLock<Mutex> _(mReadWriteLock);
81
82 secdebug("authdb", "policy db changed, saving to disk.");
83 int fd = -1;
84 string tempFile = mFileName + ",";
85
86 for (;;) {
87 fd = open(tempFile.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0644);
88 if (fd == -1) {
89 if (errno == EEXIST) {
90 unlink(tempFile.c_str());
91 continue;
92 }
93 if (errno == EINTR)
94 continue;
95 else
96 break;
97 } else
98 break;
99 }
100
101 if (fd == -1) {
102 Syslog::error("Saving rules file \"%s\": %s", tempFile.c_str(),
103 strerror(errno));
104 return;
105 }
106
107 CFDataRef configXML = CFPropertyListCreateXMLData(NULL, mConfig);
108 if (!configXML)
109 return;
110
111 CFIndex configSize = CFDataGetLength(configXML);
112 ssize_t bytesWritten = write(fd, CFDataGetBytePtr(configXML), configSize);
113 CFRelease(configXML);
114
115 if (bytesWritten != configSize) {
116 if (bytesWritten == -1)
117 Syslog::error("Problem writing rules file \"%s\": (errno=%s)",
118 tempFile.c_str(), strerror(errno));
119 else
120 Syslog::error("Problem writing rules file \"%s\": "
121 "only wrote %lu out of %ld bytes",
122 tempFile.c_str(), bytesWritten, configSize);
123
124 close(fd);
125 unlink(tempFile.c_str());
126 }
127 else
128 {
129 close(fd);
130 if (rename(tempFile.c_str(), mFileName.c_str()))
131 unlink(tempFile.c_str());
132 else
133 mLastChecked = CFAbsoluteTimeGetCurrent(); // we have the copy that's on disk now, so don't go loading it right away
134 }
135 }
136
137 void AuthorizationDBPlist::load()
138 {
139 StLock<Mutex> _(mReadWriteLock);
140
141 secdebug("authdb", "(re)loading policy db from disk.");
142 int fd = open(mFileName.c_str(), O_RDONLY, 0);
143 if (fd == -1) {
144 Syslog::error("Problem opening rules file \"%s\": %s",
145 mFileName.c_str(), strerror(errno));
146 return;
147 }
148
149 struct stat st;
150 if (fstat(fd, &st)) {
151 int error = errno;
152 close(fd);
153 UnixError::throwMe(error);
154 }
155
156 mRulesFileMtimespec = st.st_mtimespec;
157 off_t fileSize = st.st_size;
158 CFMutableDataRef xmlData = CFDataCreateMutable(NULL, fileSize);
159 CFDataSetLength(xmlData, fileSize);
160 void *buffer = CFDataGetMutableBytePtr(xmlData);
161 ssize_t bytesRead = read(fd, buffer, fileSize);
162 if (bytesRead != fileSize) {
163 if (bytesRead == -1) {
164 Syslog::error("Problem reading rules file \"%s\": %s",
165 mFileName.c_str(), strerror(errno));
166 CFRelease(xmlData);
167 return;
168 }
169 Syslog::error("Problem reading rules file \"%s\": "
170 "only read %ul out of %ul bytes",
171 bytesRead, fileSize, mFileName.c_str());
172 CFRelease(xmlData);
173 return;
174 }
175
176 CFStringRef errorString;
177 CFDictionaryRef configPlist = reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(NULL, xmlData, kCFPropertyListMutableContainersAndLeaves, &errorString));
178
179 if (!configPlist) {
180 char buffer[512];
181 const char *error = CFStringGetCStringPtr(errorString,
182 kCFStringEncodingUTF8);
183 if (error == NULL) {
184 if (CFStringGetCString(errorString, buffer, 512,
185 kCFStringEncodingUTF8))
186 error = buffer;
187 }
188
189 Syslog::error("Parsing rules file \"%s\": %s",
190 mFileName.c_str(), error);
191 if (errorString)
192 CFRelease(errorString);
193
194 CFRelease(xmlData);
195 return;
196 }
197
198 if (CFGetTypeID(configPlist) != CFDictionaryGetTypeID()) {
199
200 Syslog::error("Rules file \"%s\": is not a dictionary",
201 mFileName.c_str());
202
203 CFRelease(xmlData);
204 CFRelease(configPlist);
205 return;
206 }
207
208 parseConfig(configPlist);
209
210 CFRelease(xmlData);
211 CFRelease(configPlist);
212
213 close(fd);
214
215 // If all went well, we have the copy that's on disk now, so don't go loading it right away
216 mLastChecked = CFAbsoluteTimeGetCurrent();
217 }
218
219 void AuthorizationDBPlist::parseConfig(CFDictionaryRef config)
220 {
221 CFStringRef rightsKey = CFSTR("rights");
222 CFStringRef rulesKey = CFSTR("rules");
223 CFMutableDictionaryRef newRights = NULL;
224 CFMutableDictionaryRef newRules = NULL;
225
226 if (!config)
227 {
228 Syslog::alert("Failed to parse config, no config");
229 MacOSError::throwMe(errAuthorizationInternal);
230 }
231
232 if (CFDictionaryContainsKey(config, rulesKey))
233 newRules = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(config, rulesKey)));
234
235 if (CFDictionaryContainsKey(config, rightsKey))
236 newRights = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(config, rightsKey)));
237
238 if (newRules && newRights
239 && (CFDictionaryGetTypeID() == CFGetTypeID(newRules))
240 && (CFDictionaryGetTypeID() == CFGetTypeID(newRights)))
241 {
242 mConfigRights = static_cast<CFMutableDictionaryRef>(newRights);
243 mConfigRules = static_cast<CFMutableDictionaryRef>(newRules);
244 mRules.clear();
245 try {
246 CFDictionaryApplyFunction(newRights, parseRule, this);
247 } catch (...) {
248 Syslog::alert("Failed to parse config and apply dictionary function");
249 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule file
250 }
251 mConfig = config;
252 }
253 else
254 {
255 Syslog::alert("Failed to parse config, invalid rule file");
256 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule file
257 }
258 }
259
260 void AuthorizationDBPlist::parseRule(const void *key, const void *value, void *context)
261 {
262 static_cast<AuthorizationDBPlist*>(context)->addRight(static_cast<CFStringRef>(key), static_cast<CFDictionaryRef>(value));
263 }
264
265 void AuthorizationDBPlist::addRight(CFStringRef key, CFDictionaryRef definition)
266 {
267 string keyString = cfString(key);
268 mRules[keyString] = Rule(keyString, definition, mConfigRules);
269 }
270
271 bool
272 AuthorizationDBPlist::validateRule(string inRightName, CFDictionaryRef inRightDefinition) const
273 {
274 if (!mConfigRules ||
275 0 == CFDictionaryGetCount(mConfigRules)) {
276 Syslog::error("No rule definitions!");
277 MacOSError::throwMe(errAuthorizationInternal);
278 }
279 try {
280 Rule newRule(inRightName, inRightDefinition, mConfigRules);
281 if (newRule->name() == inRightName)
282 return true;
283 } catch (...) {
284 secdebug("authrule", "invalid definition for rule %s.\n",
285 inRightName.c_str());
286 }
287 return false;
288 }
289
290 CFDictionaryRef
291 AuthorizationDBPlist::getRuleDefinition(string &key)
292 {
293 if (!mConfigRights ||
294 0 == CFDictionaryGetCount(mConfigRights)) {
295 Syslog::error("No rule definitions!");
296 MacOSError::throwMe(errAuthorizationInternal);
297 }
298 CFStringRef cfKey = makeCFString(key);
299 StLock<Mutex> _(mLock);
300 if (CFDictionaryContainsKey(mConfigRights, cfKey)) {
301 CFDictionaryRef definition = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(mConfigRights, cfKey)));
302 CFRelease(cfKey);
303 return CFDictionaryCreateCopy(NULL, definition);
304 } else {
305 CFRelease(cfKey);
306 return NULL;
307 }
308 }
309
310 bool
311 AuthorizationDBPlist::existRule(string &ruleName) const
312 {
313 AuthItemRef candidateRule(ruleName.c_str());
314 string ruleForCandidate = getRule(candidateRule)->name();
315 // same name or covered by wildcard right -> modification.
316 if ( (ruleName == ruleForCandidate) ||
317 (*(ruleForCandidate.rbegin()) == '.') )
318 return true;
319
320 return false;
321 }
322
323 Rule
324 AuthorizationDBPlist::getRule(const AuthItemRef &inRight) const
325 {
326 string key(inRight->name());
327 // Lock the rulemap
328 StLock<Mutex> _(mLock);
329
330 secdebug("authdb", "looking up rule %s.", inRight->name());
331 if (mRules.empty())
332 return Rule();
333
334 for (;;) {
335 map<string,Rule>::const_iterator rule = mRules.find(key);
336
337 if (rule != mRules.end())
338 return (*rule).second;
339
340 // no default rule
341 assert (key.size());
342
343 // any reduction of a combination of two chars is futile
344 if (key.size() > 2) {
345 // find last dot with exception of possible dot at end
346 string::size_type index = key.rfind('.', key.size() - 2);
347 // cut right after found dot, or make it match default rule
348 key = key.substr(0, index == string::npos ? 0 : index + 1);
349 } else
350 key.erase();
351 }
352 }
353
354 void
355 AuthorizationDBPlist::setRule(const char *inRightName, CFDictionaryRef inRuleDefinition)
356 {
357 // if mConfig is now a reasonable guard
358 if (!inRuleDefinition || !mConfigRights)
359 {
360 Syslog::alert("Failed to set rule, no definition or rights");
361 MacOSError::throwMe(errAuthorizationDenied); // ???/gh errAuthorizationInternal instead?
362 }
363
364 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName,
365 kCFStringEncodingASCII));
366 if (!keyRef)
367 return;
368
369 {
370 StLock<Mutex> _(mLock);
371 secdebug("authdb", "setting up rule %s.", inRightName);
372 CFDictionarySetValue(mConfigRights, keyRef, inRuleDefinition);
373 save();
374 parseConfig(mConfig);
375 }
376 }
377
378 void
379 AuthorizationDBPlist::removeRule(const char *inRightName)
380 {
381 // if mConfig is now a reasonable guard
382 if (!mConfigRights)
383 {
384 Syslog::alert("Failed to remove rule, no rights");
385 MacOSError::throwMe(errAuthorizationDenied); // ???/gh errAuthorizationInternal instead?
386 }
387
388 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName,
389 kCFStringEncodingASCII));
390 if (!keyRef)
391 return;
392
393 {
394 StLock<Mutex> _(mLock);
395 secdebug("authdb", "removing rule %s.", inRightName);
396 CFDictionaryRemoveValue(mConfigRights, keyRef);
397 save();
398 parseConfig(mConfig);
399 }
400 }
401
402
403 } // end namespace Authorization