]> git.saurik.com Git - apple/securityd.git/blob - src/AuthorizationDBPlist.cpp
securityd-67.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 map<string,Rule>::const_iterator rule = mRules.find(ruleName);
315 if (rule != mRules.end())
316 return true;
317
318 return false;
319 }
320
321 Rule
322 AuthorizationDBPlist::getRule(const AuthItemRef &inRight) const
323 {
324 string key(inRight->name());
325 // Lock the rulemap
326 StLock<Mutex> _(mLock);
327
328 if (mRules.empty())
329 return Rule();
330
331 for (;;)
332 {
333 map<string,Rule>::const_iterator rule = mRules.find(key);
334
335 if (rule != mRules.end())
336 return (*rule).second;
337
338 // no default rule
339 assert (key.size());
340
341 // any reduction of a combination of two chars is futile
342 if (key.size() > 2) {
343 // find last dot with exception of possible dot at end
344 string::size_type index = key.rfind('.', key.size() - 2);
345 // cut right after found dot, or make it match default rule
346 key = key.substr(0, index == string::npos ? 0 : index + 1);
347 } else
348 key.erase();
349 }
350 }
351
352 void
353 AuthorizationDBPlist::setRule(const char *inRightName, CFDictionaryRef inRuleDefinition)
354 {
355 if (!inRuleDefinition || !mConfigRights)
356 MacOSError::throwMe(errAuthorizationDenied); // errInvalidRule
357
358 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName, kCFStringEncodingASCII));
359 if (!keyRef)
360 return;
361
362 StLock<Mutex> _(mLock);
363
364 CFDictionarySetValue(mConfigRights, keyRef, inRuleDefinition);
365 // release modification lock here already?
366 save();
367 mLastChecked = 0.0;
368 }
369
370 void
371 AuthorizationDBPlist::removeRule(const char *inRightName)
372 {
373 if (!mConfigRights)
374 MacOSError::throwMe(errAuthorizationDenied);
375
376 CFRef<CFStringRef> keyRef(CFStringCreateWithCString(NULL, inRightName, kCFStringEncodingASCII));
377 if (!keyRef)
378 return;
379
380 StLock<Mutex> _(mLock);
381
382 if (CFDictionaryContainsKey(mConfigRights, keyRef))
383 {
384 CFDictionaryRemoveValue(mConfigRights, keyRef);
385 // release modification lock here already?
386 save();
387 mLastChecked = 0.0;
388 }
389 }
390
391
392 } // end namespace Authorization