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