]> git.saurik.com Git - apple/security.git/blob - securityd/src/AuthorizationDBPlist.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / src / AuthorizationDBPlist.cpp
1 /*
2 * Copyright (c) 2003-2005,2007-2010 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 * 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 CFDictionaryRef configPlist;
155
156 secdebug("authdb", "(re)loading policy db from disk.");
157 int fd = open(mFileName.c_str(), O_RDONLY, 0);
158 if (fd == -1) {
159 Syslog::error("Problem opening rules file \"%s\": %s",
160 mFileName.c_str(), strerror(errno));
161 return;
162 }
163
164 struct stat st;
165 if (fstat(fd, &st)) {
166 int error = errno;
167 close(fd);
168 UnixError::throwMe(error);
169 }
170
171 mRulesFileMtimespec = st.st_mtimespec;
172 off_t fileSize = st.st_size;
173 CFMutableDataRef xmlData = CFDataCreateMutable(NULL, fileSize);
174 CFDataSetLength(xmlData, fileSize);
175 void *buffer = CFDataGetMutableBytePtr(xmlData);
176 ssize_t bytesRead = read(fd, buffer, fileSize);
177 if (bytesRead != fileSize) {
178 if (bytesRead == -1) {
179 Syslog::error("Problem reading rules file \"%s\": %s",
180 mFileName.c_str(), strerror(errno));
181 goto cleanup;
182 }
183 Syslog::error("Problem reading rules file \"%s\": "
184 "only read %ul out of %ul bytes",
185 bytesRead, fileSize, mFileName.c_str());
186 goto cleanup;
187 }
188
189 CFStringRef errorString;
190 configPlist = reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(NULL, xmlData, kCFPropertyListMutableContainersAndLeaves, &errorString));
191
192 if (!configPlist) {
193 char buffer[512];
194 const char *error = CFStringGetCStringPtr(errorString,
195 kCFStringEncodingUTF8);
196 if (error == NULL) {
197 if (CFStringGetCString(errorString, buffer, 512,
198 kCFStringEncodingUTF8))
199 error = buffer;
200 }
201
202 Syslog::error("Parsing rules file \"%s\": %s",
203 mFileName.c_str(), error);
204 if (errorString)
205 CFRelease(errorString);
206
207 goto cleanup;
208 }
209
210 if (CFGetTypeID(configPlist) != CFDictionaryGetTypeID()) {
211
212 Syslog::error("Rules file \"%s\": is not a dictionary",
213 mFileName.c_str());
214
215 goto cleanup;
216 }
217
218 parseConfig(configPlist);
219
220 cleanup:
221 if (xmlData)
222 CFRelease(xmlData);
223 if (configPlist)
224 CFRelease(configPlist);
225
226 close(fd);
227
228 // If all went well, we have the copy that's on disk now, so don't go loading it right away
229 mLastChecked = CFAbsoluteTimeGetCurrent();
230 }
231
232 void AuthorizationDBPlist::parseConfig(CFDictionaryRef config)
233 {
234 CFStringRef rightsKey = CFSTR("rights");
235 CFStringRef rulesKey = CFSTR("rules");
236 CFMutableDictionaryRef newRights = NULL;
237 CFMutableDictionaryRef newRules = NULL;
238
239 if (!config)
240 {
241 Syslog::alert("Failed to parse config, no config");
242 MacOSError::throwMe(errAuthorizationInternal);
243 }
244
245 if (CFDictionaryContainsKey(config, rulesKey))
246 newRules = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(config, rulesKey)));
247
248 if (CFDictionaryContainsKey(config, rightsKey))
249 newRights = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(config, rightsKey)));
250
251 if (newRules && newRights
252 && (CFDictionaryGetTypeID() == CFGetTypeID(newRules))
253 && (CFDictionaryGetTypeID() == CFGetTypeID(newRights)))
254 {
255 mConfigRights = static_cast<CFMutableDictionaryRef>(newRights);
256 mConfigRules = static_cast<CFMutableDictionaryRef>(newRules);
257 mRules.clear();
258 try {
259 CFDictionaryApplyFunction(newRights, parseRule, this);
260 } catch (...) {
261 Syslog::alert("Failed to parse config and apply dictionary function");
262 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule file
263 }
264 mConfig = config;
265 }
266 else
267 {
268 Syslog::alert("Failed to parse config, invalid rule file");
269 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule file
270 }
271 }
272
273 void AuthorizationDBPlist::parseRule(const void *key, const void *value, void *context)
274 {
275 static_cast<AuthorizationDBPlist*>(context)->addRight(static_cast<CFStringRef>(key), static_cast<CFDictionaryRef>(value));
276 }
277
278 void AuthorizationDBPlist::addRight(CFStringRef key, CFDictionaryRef definition)
279 {
280 string keyString = cfString(key);
281 mRules[keyString] = Rule(keyString, definition, mConfigRules);
282 }
283
284 bool
285 AuthorizationDBPlist::validateRule(string inRightName, CFDictionaryRef inRightDefinition) const
286 {
287 if (!mConfigRules ||
288 0 == CFDictionaryGetCount(mConfigRules)) {
289 Syslog::error("No rule definitions!");
290 MacOSError::throwMe(errAuthorizationInternal);
291 }
292 try {
293 Rule newRule(inRightName, inRightDefinition, mConfigRules);
294 if (newRule->name() == inRightName)
295 return true;
296 } catch (...) {
297 secdebug("authrule", "invalid definition for rule %s.\n",
298 inRightName.c_str());
299 }
300 return false;
301 }
302
303 CFDictionaryRef
304 AuthorizationDBPlist::getRuleDefinition(string &key)
305 {
306 if (!mConfigRights ||
307 0 == CFDictionaryGetCount(mConfigRights)) {
308 Syslog::error("No rule definitions!");
309 MacOSError::throwMe(errAuthorizationInternal);
310 }
311 CFStringRef cfKey = makeCFString(key);
312 StLock<Mutex> _(mLock);
313 if (CFDictionaryContainsKey(mConfigRights, cfKey)) {
314 CFDictionaryRef definition = reinterpret_cast<CFMutableDictionaryRef>(const_cast<void*>(CFDictionaryGetValue(mConfigRights, cfKey)));
315 CFRelease(cfKey);
316 return CFDictionaryCreateCopy(NULL, definition);
317 } else {
318 CFRelease(cfKey);
319 return NULL;
320 }
321 }
322
323 bool
324 AuthorizationDBPlist::existRule(string &ruleName) const
325 {
326 AuthItemRef candidateRule(ruleName.c_str());
327 string ruleForCandidate = getRule(candidateRule)->name();
328 // same name or covered by wildcard right -> modification.
329 if ( (ruleName == ruleForCandidate) ||
330 (*(ruleForCandidate.rbegin()) == '.') )
331 return true;
332
333 return false;
334 }
335
336 Rule
337 AuthorizationDBPlist::getRule(const AuthItemRef &inRight) const
338 {
339 string key(inRight->name());
340 // Lock the rulemap
341 StLock<Mutex> _(mLock);
342
343 secdebug("authdb", "looking up rule %s.", inRight->name());
344 if (mRules.empty())
345 return Rule();
346
347 for (;;) {
348 map<string,Rule>::const_iterator rule = mRules.find(key);
349
350 if (rule != mRules.end())
351 return (*rule).second;
352
353 // no default rule
354 assert (key.size());
355
356 // any reduction of a combination of two chars is futile
357 if (key.size() > 2) {
358 // find last dot with exception of possible dot at end
359 string::size_type index = key.rfind('.', key.size() - 2);
360 // cut right after found dot, or make it match default rule
361 key = key.substr(0, index == string::npos ? 0 : index + 1);
362 } else
363 key.erase();
364 }
365 }
366
367 void
368 AuthorizationDBPlist::setRule(const char *inRightName, CFDictionaryRef inRuleDefinition)
369 {
370 // if mConfig is now a reasonable guard
371 if (!inRuleDefinition || !mConfigRights)
372 {
373 Syslog::alert("Failed to set rule, no definition or rights");
374 MacOSError::throwMe(errAuthorizationDenied); // ???/gh errAuthorizationInternal instead?
375 }
376
377 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName,
378 kCFStringEncodingASCII));
379 if (!keyRef)
380 return;
381
382 {
383 StLock<Mutex> _(mLock);
384 secdebug("authdb", "setting up rule %s.", inRightName);
385 CFDictionarySetValue(mConfigRights, keyRef, inRuleDefinition);
386 save();
387 parseConfig(mConfig);
388 }
389 }
390
391 void
392 AuthorizationDBPlist::removeRule(const char *inRightName)
393 {
394 // if mConfig is now a reasonable guard
395 if (!mConfigRights)
396 {
397 Syslog::alert("Failed to remove rule, no rights");
398 MacOSError::throwMe(errAuthorizationDenied); // ???/gh errAuthorizationInternal instead?
399 }
400
401 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName,
402 kCFStringEncodingASCII));
403 if (!keyRef)
404 return;
405
406 {
407 StLock<Mutex> _(mLock);
408 secdebug("authdb", "removing rule %s.", inRightName);
409 CFDictionaryRemoveValue(mConfigRights, keyRef);
410 save();
411 parseConfig(mConfig);
412 }
413 }
414
415
416 } // end namespace Authorization