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