2 * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * AuthorizationDBPlist.cpp
28 #include "AuthorizationDBPlist.h"
29 #include <security_utilities/logging.h>
32 namespace Authorization
35 AuthorizationDBPlist::AuthorizationDBPlist(const char *configFile
) : mFileName(configFile
), mLastChecked(DBL_MIN
)
37 memset(&mRulesFileMtimespec
, 0, sizeof(mRulesFileMtimespec
));
40 void AuthorizationDBPlist::sync(CFAbsoluteTime now
)
46 // Don't do anything if we checked the timestamp less than 5 seconds ago
47 if (mLastChecked
> now
- 5.0)
51 if (stat(mFileName
.c_str(), &st
))
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);
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
)))
70 void AuthorizationDBPlist::save() const
75 StLock
<Mutex
> _(mReadWriteLock
);
78 string tempFile
= mFileName
+ ",";
82 fd
= open(tempFile
.c_str(), O_WRONLY
|O_CREAT
|O_EXCL
, 0644);
87 unlink(tempFile
.c_str());
101 Syslog::error("Saving rules file \"%s\": %s", tempFile
.c_str(), strerror(errno
));
105 // convert config to plist
106 CFDataRef configXML
= CFPropertyListCreateXMLData(NULL
, mConfig
);
112 SInt32 configSize
= CFDataGetLength(configXML
);
113 size_t bytesWritten
= write(fd
, CFDataGetBytePtr(configXML
), configSize
);
114 CFRelease(configXML
);
116 if (bytesWritten
!= uint32_t(configSize
))
118 if (bytesWritten
== static_cast<size_t>(-1))
119 Syslog::error("Writing rules file \"%s\": %s", tempFile
.c_str(), strerror(errno
));
121 Syslog::error("Could only write %lu out of %ld bytes from rules file \"%s\"",
122 bytesWritten
, configSize
, tempFile
.c_str());
125 unlink(tempFile
.c_str());
130 if (rename(tempFile
.c_str(), mFileName
.c_str()))
131 unlink(tempFile
.c_str());
136 void AuthorizationDBPlist::load(CFTimeInterval now
)
138 StLock
<Mutex
> _(mReadWriteLock
);
140 int fd
= open(mFileName
.c_str(), O_RDONLY
, 0);
143 Syslog::error("Opening rules file \"%s\": %s", mFileName
.c_str(), strerror(errno
));
152 UnixError::throwMe(error
);
156 mRulesFileMtimespec
= st
.st_mtimespec
;
158 off_t fileSize
= st
.st_size
;
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
)
166 if (bytesRead
== static_cast<size_t>(-1))
168 Syslog::error("Reading rules file \"%s\": %s", mFileName
.c_str(), strerror(errno
));
173 Syslog::error("Could only read %ul out of %ul bytes from rules file \"%s\"",
174 bytesRead
, fileSize
, mFileName
.c_str());
179 CFStringRef errorString
;
180 CFDictionaryRef configPlist
= reinterpret_cast<CFDictionaryRef
>(CFPropertyListCreateFromXMLData(NULL
, xmlData
, kCFPropertyListMutableContainersAndLeaves
, &errorString
));
185 const char *error
= CFStringGetCStringPtr(errorString
, kCFStringEncodingUTF8
);
188 if (CFStringGetCString(errorString
, buffer
, 512, kCFStringEncodingUTF8
))
192 Syslog::error("Parsing rules file \"%s\": %s", mFileName
.c_str(), error
);
194 CFRelease(errorString
);
200 if (CFGetTypeID(configPlist
) != CFDictionaryGetTypeID())
203 Syslog::error("Rules file \"%s\": is not a dictionary", mFileName
.c_str());
206 CFRelease(configPlist
);
211 StLock
<Mutex
> _(mLock
);
212 parseConfig(configPlist
);
216 CFRelease(configPlist
);
224 AuthorizationDBPlist::parseConfig(CFDictionaryRef config
)
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
;
233 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule file
235 if (CFDictionaryContainsKey(config
, rulesKey
))
237 newRules
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(config
, rulesKey
)));
240 if (CFDictionaryContainsKey(config
, rightsKey
))
242 newRights
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(config
, rightsKey
)));
247 && (CFDictionaryGetTypeID() == CFGetTypeID(newRules
))
248 && (CFDictionaryGetTypeID() == CFGetTypeID(newRights
)))
251 mConfigRights
= static_cast<CFMutableDictionaryRef
>(newRights
);
252 mConfigRules
= static_cast<CFMutableDictionaryRef
>(newRules
);
256 CFDictionaryApplyFunction(newRights
, parseRule
, this);
260 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule file
264 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule file
267 void AuthorizationDBPlist::parseRule(const void *key
, const void *value
, void *context
)
269 static_cast<AuthorizationDBPlist
*>(context
)->addRight(static_cast<CFStringRef
>(key
), static_cast<CFDictionaryRef
>(value
));
272 void AuthorizationDBPlist::addRight(CFStringRef key
, CFDictionaryRef definition
)
274 string keyString
= cfString(key
);
275 mRules
[keyString
] = Rule(keyString
, definition
, mConfigRules
);
279 AuthorizationDBPlist::validateRule(string inRightName
, CFDictionaryRef inRightDefinition
) const
282 Rule
newRule(inRightName
, inRightDefinition
, mConfigRules
);
283 if (newRule
->name() == inRightName
)
288 secdebug("authrule", "invalid definition for rule %s.\n", inRightName
.c_str());
294 AuthorizationDBPlist::getRuleDefinition(string
&key
)
296 CFStringRef cfKey
= makeCFString(key
);
297 StLock
<Mutex
> _(mLock
);
298 if (CFDictionaryContainsKey(mConfigRights
, cfKey
))
300 CFDictionaryRef definition
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(mConfigRights
, cfKey
)));
302 return CFDictionaryCreateCopy(NULL
, definition
);
312 AuthorizationDBPlist::existRule(string
&ruleName
) const
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()) == '.') )
325 AuthorizationDBPlist::getRule(const AuthItemRef
&inRight
) const
327 string
key(inRight
->name());
329 StLock
<Mutex
> _(mLock
);
336 map
<string
,Rule
>::const_iterator rule
= mRules
.find(key
);
338 if (rule
!= mRules
.end())
339 return (*rule
).second
;
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);
356 AuthorizationDBPlist::setRule(const char *inRightName
, CFDictionaryRef inRuleDefinition
)
358 if (!inRuleDefinition
|| !mConfigRights
)
359 MacOSError::throwMe(errAuthorizationDenied
); // errInvalidRule
361 CFRef
<CFStringRef
> keyRef(CFStringCreateWithCString(NULL
, inRightName
, kCFStringEncodingASCII
));
365 StLock
<Mutex
> _(mLock
);
367 CFDictionarySetValue(mConfigRights
, keyRef
, inRuleDefinition
);
368 // release modification lock here already?
374 AuthorizationDBPlist::removeRule(const char *inRightName
)
377 MacOSError::throwMe(errAuthorizationDenied
);
379 CFRef
<CFStringRef
> keyRef(CFStringCreateWithCString(NULL
, inRightName
, kCFStringEncodingASCII
));
383 StLock
<Mutex
> _(mLock
);
385 if (CFDictionaryContainsKey(mConfigRights
, keyRef
))
387 CFDictionaryRemoveValue(mConfigRights
, keyRef
);
388 // release modification lock here already?
395 } // end namespace Authorization