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