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>
30 #include <System/sys/fsctl.h>
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
36 namespace Authorization
{
38 AuthorizationDBPlist::AuthorizationDBPlist(const char *configFile
) :
39 mFileName(configFile
), mLastChecked(DBL_MIN
)
41 memset(&mRulesFileMtimespec
, 0, sizeof(mRulesFileMtimespec
));
44 void AuthorizationDBPlist::sync(CFAbsoluteTime now
)
47 StLock
<Mutex
> _(mLock
);
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",
60 StLock
<Mutex
> _(mReadWriteLock
);
61 if (stat(mFileName
.c_str(), &st
)) {
62 Syslog::error("Stating rules file \"%s\": %s", mFileName
.c_str(),
68 if (memcmp(&st
.st_mtimespec
, &mRulesFileMtimespec
, sizeof(mRulesFileMtimespec
))) {
69 StLock
<Mutex
> _(mLock
);
76 void AuthorizationDBPlist::save()
81 StLock
<Mutex
> _(mReadWriteLock
);
83 secdebug("authdb", "policy db changed, saving to disk.");
85 string tempFile
= mFileName
+ ",";
88 fd
= open(tempFile
.c_str(), O_WRONLY
|O_CREAT
|O_EXCL
, 0644);
90 if (errno
== EEXIST
) {
91 unlink(tempFile
.c_str());
103 Syslog::error("Saving rules file \"%s\": %s", tempFile
.c_str(),
108 CFDataRef configXML
= CFPropertyListCreateXMLData(NULL
, mConfig
);
112 CFIndex configSize
= CFDataGetLength(configXML
);
113 ssize_t bytesWritten
= write(fd
, CFDataGetBytePtr(configXML
), configSize
);
114 CFRelease(configXML
);
116 if (bytesWritten
!= configSize
) {
117 if (bytesWritten
== -1)
118 Syslog::error("Problem writing rules file \"%s\": (errno=%s)",
119 tempFile
.c_str(), strerror(errno
));
121 Syslog::error("Problem writing rules file \"%s\": "
122 "only wrote %lu out of %ld bytes",
123 tempFile
.c_str(), bytesWritten
, configSize
);
126 unlink(tempFile
.c_str());
130 if (-1 == fcntl(fd
, F_FULLFSYNC
, NULL
))
134 int fd2
= open (mFileName
.c_str(), O_RDONLY
);
135 if (rename(tempFile
.c_str(), mFileName
.c_str()))
138 unlink(tempFile
.c_str());
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
));
146 mLastChecked
= CFAbsoluteTimeGetCurrent(); // we have the copy that's on disk now, so don't go loading it right away
151 void AuthorizationDBPlist::load()
153 StLock
<Mutex
> _(mReadWriteLock
);
155 secdebug("authdb", "(re)loading policy db from disk.");
156 int fd
= open(mFileName
.c_str(), O_RDONLY
, 0);
158 Syslog::error("Problem opening rules file \"%s\": %s",
159 mFileName
.c_str(), strerror(errno
));
164 if (fstat(fd
, &st
)) {
167 UnixError::throwMe(error
);
170 mRulesFileMtimespec
= st
.st_mtimespec
;
171 off_t fileSize
= st
.st_size
;
172 CFMutableDataRef xmlData
= CFDataCreateMutable(NULL
, fileSize
);
173 CFDataSetLength(xmlData
, fileSize
);
174 void *buffer
= CFDataGetMutableBytePtr(xmlData
);
175 ssize_t bytesRead
= read(fd
, buffer
, fileSize
);
176 if (bytesRead
!= fileSize
) {
177 if (bytesRead
== -1) {
178 Syslog::error("Problem reading rules file \"%s\": %s",
179 mFileName
.c_str(), strerror(errno
));
183 Syslog::error("Problem reading rules file \"%s\": "
184 "only read %ul out of %ul bytes",
185 bytesRead
, fileSize
, mFileName
.c_str());
190 CFStringRef errorString
;
191 CFDictionaryRef configPlist
= reinterpret_cast<CFDictionaryRef
>(CFPropertyListCreateFromXMLData(NULL
, xmlData
, kCFPropertyListMutableContainersAndLeaves
, &errorString
));
195 const char *error
= CFStringGetCStringPtr(errorString
,
196 kCFStringEncodingUTF8
);
198 if (CFStringGetCString(errorString
, buffer
, 512,
199 kCFStringEncodingUTF8
))
203 Syslog::error("Parsing rules file \"%s\": %s",
204 mFileName
.c_str(), error
);
206 CFRelease(errorString
);
212 if (CFGetTypeID(configPlist
) != CFDictionaryGetTypeID()) {
214 Syslog::error("Rules file \"%s\": is not a dictionary",
218 CFRelease(configPlist
);
222 parseConfig(configPlist
);
225 CFRelease(configPlist
);
229 // If all went well, we have the copy that's on disk now, so don't go loading it right away
230 mLastChecked
= CFAbsoluteTimeGetCurrent();
233 void AuthorizationDBPlist::parseConfig(CFDictionaryRef config
)
235 CFStringRef rightsKey
= CFSTR("rights");
236 CFStringRef rulesKey
= CFSTR("rules");
237 CFMutableDictionaryRef newRights
= NULL
;
238 CFMutableDictionaryRef newRules
= NULL
;
242 Syslog::alert("Failed to parse config, no config");
243 MacOSError::throwMe(errAuthorizationInternal
);
246 if (CFDictionaryContainsKey(config
, rulesKey
))
247 newRules
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(config
, rulesKey
)));
249 if (CFDictionaryContainsKey(config
, rightsKey
))
250 newRights
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(config
, rightsKey
)));
252 if (newRules
&& newRights
253 && (CFDictionaryGetTypeID() == CFGetTypeID(newRules
))
254 && (CFDictionaryGetTypeID() == CFGetTypeID(newRights
)))
256 mConfigRights
= static_cast<CFMutableDictionaryRef
>(newRights
);
257 mConfigRules
= static_cast<CFMutableDictionaryRef
>(newRules
);
260 CFDictionaryApplyFunction(newRights
, parseRule
, this);
262 Syslog::alert("Failed to parse config and apply dictionary function");
263 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule file
269 Syslog::alert("Failed to parse config, invalid rule file");
270 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule file
274 void AuthorizationDBPlist::parseRule(const void *key
, const void *value
, void *context
)
276 static_cast<AuthorizationDBPlist
*>(context
)->addRight(static_cast<CFStringRef
>(key
), static_cast<CFDictionaryRef
>(value
));
279 void AuthorizationDBPlist::addRight(CFStringRef key
, CFDictionaryRef definition
)
281 string keyString
= cfString(key
);
282 mRules
[keyString
] = Rule(keyString
, definition
, mConfigRules
);
286 AuthorizationDBPlist::validateRule(string inRightName
, CFDictionaryRef inRightDefinition
) const
289 0 == CFDictionaryGetCount(mConfigRules
)) {
290 Syslog::error("No rule definitions!");
291 MacOSError::throwMe(errAuthorizationInternal
);
294 Rule
newRule(inRightName
, inRightDefinition
, mConfigRules
);
295 if (newRule
->name() == inRightName
)
298 secdebug("authrule", "invalid definition for rule %s.\n",
299 inRightName
.c_str());
305 AuthorizationDBPlist::getRuleDefinition(string
&key
)
307 if (!mConfigRights
||
308 0 == CFDictionaryGetCount(mConfigRights
)) {
309 Syslog::error("No rule definitions!");
310 MacOSError::throwMe(errAuthorizationInternal
);
312 CFStringRef cfKey
= makeCFString(key
);
313 StLock
<Mutex
> _(mLock
);
314 if (CFDictionaryContainsKey(mConfigRights
, cfKey
)) {
315 CFDictionaryRef definition
= reinterpret_cast<CFMutableDictionaryRef
>(const_cast<void*>(CFDictionaryGetValue(mConfigRights
, cfKey
)));
317 return CFDictionaryCreateCopy(NULL
, definition
);
325 AuthorizationDBPlist::existRule(string
&ruleName
) const
327 AuthItemRef
candidateRule(ruleName
.c_str());
328 string ruleForCandidate
= getRule(candidateRule
)->name();
329 // same name or covered by wildcard right -> modification.
330 if ( (ruleName
== ruleForCandidate
) ||
331 (*(ruleForCandidate
.rbegin()) == '.') )
338 AuthorizationDBPlist::getRule(const AuthItemRef
&inRight
) const
340 string
key(inRight
->name());
342 StLock
<Mutex
> _(mLock
);
344 secdebug("authdb", "looking up rule %s.", inRight
->name());
349 map
<string
,Rule
>::const_iterator rule
= mRules
.find(key
);
351 if (rule
!= mRules
.end())
352 return (*rule
).second
;
357 // any reduction of a combination of two chars is futile
358 if (key
.size() > 2) {
359 // find last dot with exception of possible dot at end
360 string::size_type index
= key
.rfind('.', key
.size() - 2);
361 // cut right after found dot, or make it match default rule
362 key
= key
.substr(0, index
== string::npos
? 0 : index
+ 1);
369 AuthorizationDBPlist::setRule(const char *inRightName
, CFDictionaryRef inRuleDefinition
)
371 // if mConfig is now a reasonable guard
372 if (!inRuleDefinition
|| !mConfigRights
)
374 Syslog::alert("Failed to set rule, no definition or rights");
375 MacOSError::throwMe(errAuthorizationDenied
); // ???/gh errAuthorizationInternal instead?
378 CFRef
<CFStringRef
> keyRef(CFStringCreateWithCString(NULL
, inRightName
,
379 kCFStringEncodingASCII
));
384 StLock
<Mutex
> _(mLock
);
385 secdebug("authdb", "setting up rule %s.", inRightName
);
386 CFDictionarySetValue(mConfigRights
, keyRef
, inRuleDefinition
);
388 parseConfig(mConfig
);
393 AuthorizationDBPlist::removeRule(const char *inRightName
)
395 // if mConfig is now a reasonable guard
398 Syslog::alert("Failed to remove rule, no rights");
399 MacOSError::throwMe(errAuthorizationDenied
); // ???/gh errAuthorizationInternal instead?
402 CFRef
<CFStringRef
> keyRef(CFStringCreateWithCString(NULL
, inRightName
,
403 kCFStringEncodingASCII
));
408 StLock
<Mutex
> _(mLock
);
409 secdebug("authdb", "removing rule %s.", inRightName
);
410 CFDictionaryRemoveValue(mConfigRights
, keyRef
);
412 parseConfig(mConfig
);
417 } // end namespace Authorization