2 * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 * AuthorizationRule.cpp
30 #include "AuthorizationRule.h"
31 #include <Security/AuthorizationTags.h>
32 #include <Security/AuthorizationTagsPriv.h>
33 #include <Security/AuthorizationDB.h>
34 #include <Security/AuthorizationPriv.h>
35 #include "authority.h"
38 #include "agentquery.h"
39 #include "AuthorizationMechEval.h"
49 namespace Authorization
{
51 CFStringRef
RuleImpl::kUserGroupID
= CFSTR(kAuthorizationRuleParameterGroup
);
52 CFStringRef
RuleImpl::kTimeoutID
= CFSTR(kAuthorizationRuleParameterCredentialTimeout
);
53 CFStringRef
RuleImpl::kSharedID
= CFSTR(kAuthorizationRuleParameterCredentialShared
);
54 CFStringRef
RuleImpl::kAllowRootID
= CFSTR(kAuthorizationRuleParameterAllowRoot
);
55 CFStringRef
RuleImpl::kMechanismsID
= CFSTR(kAuthorizationRuleParameterMechanisms
);
56 CFStringRef
RuleImpl::kSessionOwnerID
= CFSTR(kAuthorizationRuleParameterCredentialSessionOwner
);
57 CFStringRef
RuleImpl::kKofNID
= CFSTR(kAuthorizationRuleParameterKofN
);
58 CFStringRef
RuleImpl::kPromptID
= CFSTR(kAuthorizationRuleParameterDefaultPrompt
);
59 CFStringRef
RuleImpl::kTriesID
= CFSTR("tries"); // XXX/cs move to AuthorizationTagsPriv.h
61 CFStringRef
RuleImpl::kRuleClassID
= CFSTR(kAuthorizationRuleClass
);
62 CFStringRef
RuleImpl::kRuleAllowID
= CFSTR(kAuthorizationRuleClassAllow
);
63 CFStringRef
RuleImpl::kRuleDenyID
= CFSTR(kAuthorizationRuleClassDeny
);
64 CFStringRef
RuleImpl::kRuleUserID
= CFSTR(kAuthorizationRuleClassUser
);
65 CFStringRef
RuleImpl::kRuleDelegateID
= CFSTR(kAuthorizationRightRule
);
66 CFStringRef
RuleImpl::kRuleMechanismsID
= CFSTR(kAuthorizationRuleClassMechanisms
);
69 RuleImpl::Attribute::getString(CFDictionaryRef config
, CFStringRef key
, bool required
= false, char *defaultValue
= NULL
)
71 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
72 if (value
&& (CFGetTypeID(value
) == CFStringGetTypeID()))
74 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(value
);
76 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
79 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
82 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
89 return string(defaultValue
);
91 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
95 RuleImpl::Attribute::getDouble(CFDictionaryRef config
, CFStringRef key
, bool required
= false, double defaultValue
= 0.0)
97 double doubleValue
= 0;
99 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
100 if (value
&& (CFGetTypeID(value
) == CFNumberGetTypeID()))
102 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(value
), kCFNumberDoubleType
, &doubleValue
);
108 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
114 RuleImpl::Attribute::getBool(CFDictionaryRef config
, CFStringRef key
, bool required
= false, bool defaultValue
= false)
116 bool boolValue
= false;
117 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
119 if (value
&& (CFGetTypeID(value
) == CFBooleanGetTypeID()))
121 boolValue
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(value
));
127 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
132 // add reference to string that we're modifying
134 RuleImpl::Attribute::setString(CFMutableDictionaryRef config
, CFStringRef key
, string
&value
)
136 CFStringRef cfstringValue
= CFStringCreateWithCString(NULL
/*allocator*/, value
.c_str(), kCFStringEncodingUTF8
);
140 CFDictionarySetValue(config
, key
, cfstringValue
);
141 CFRelease(cfstringValue
);
144 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
148 RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config
, CFStringRef key
, double value
)
150 CFNumberRef doubleValue
= CFNumberCreate(NULL
/*allocator*/, kCFNumberDoubleType
, doubleValue
);
154 CFDictionarySetValue(config
, key
, doubleValue
);
155 CFRelease(doubleValue
);
158 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
162 RuleImpl::Attribute::setBool(CFMutableDictionaryRef config
, CFStringRef key
, bool value
)
165 CFDictionarySetValue(config
, key
, kCFBooleanTrue
);
167 CFDictionarySetValue(config
, key
, kCFBooleanFalse
);
171 RuleImpl::Attribute::getVector(CFDictionaryRef config
, CFStringRef key
, bool required
= false)
173 vector
<string
> valueArray
;
175 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
176 if (value
&& (CFGetTypeID(value
) == CFArrayGetTypeID()))
178 CFArrayRef evalArray
= reinterpret_cast<CFArrayRef
>(value
);
180 for (int index
=0; index
< CFArrayGetCount(evalArray
); index
++)
182 CFTypeRef arrayValue
= CFArrayGetValueAtIndex(evalArray
, index
);
183 if (arrayValue
&& (CFGetTypeID(arrayValue
) == CFStringGetTypeID()))
185 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(arrayValue
);
187 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
190 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
193 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
195 valueArray
.push_back(string(ptr
));
201 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
207 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config
, map
<string
,string
> &localizedPrompts
)
209 CFIndex numberOfPrompts
= 0;
210 CFDictionaryRef promptsDict
;
211 if (CFDictionaryContainsKey(config
, kPromptID
))
213 promptsDict
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(config
, kPromptID
));
214 if (promptsDict
&& (CFGetTypeID(promptsDict
) == CFDictionaryGetTypeID()))
215 numberOfPrompts
= CFDictionaryGetCount(promptsDict
);
217 if (numberOfPrompts
== 0)
220 const void *keys
[numberOfPrompts
+1];
221 const void *values
[numberOfPrompts
+1];
222 CFDictionaryGetKeysAndValues(promptsDict
, &keys
[0], &values
[0]);
224 while (numberOfPrompts
-- > 0)
226 CFStringRef keyRef
= reinterpret_cast<CFStringRef
>(keys
[numberOfPrompts
]);
227 CFStringRef valueRef
= reinterpret_cast<CFStringRef
>(values
[numberOfPrompts
]);
228 if (!keyRef
|| (CFGetTypeID(keyRef
) != CFStringGetTypeID()))
230 if (!valueRef
|| (CFGetTypeID(valueRef
) != CFStringGetTypeID()))
232 string key
= cfString(keyRef
);
233 string value
= cfString(valueRef
);
234 localizedPrompts
[kAuthorizationRuleParameterDescription
+key
] = value
;
242 RuleImpl::RuleImpl() :
243 mType(kUser
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
245 // XXX/cs read default descriptions from somewhere
246 // @@@ Default rule is shared admin group with 5 minute timeout
249 // return rule built from rule definition; throw if invalid.
250 RuleImpl::RuleImpl(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : mRightName(inRightName
)
252 // @@@ make sure cfRight is non mutable and never used that way
254 if (CFGetTypeID(cfRight
) != CFDictionaryGetTypeID())
255 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
259 string classTag
= Attribute::getString(cfRight
, kRuleClassID
, false, "");
261 if (classTag
.length())
263 if (classTag
== kAuthorizationRuleClassAllow
)
265 secdebug("authrule", "%s : rule allow", inRightName
.c_str());
268 else if (classTag
== kAuthorizationRuleClassDeny
)
270 secdebug("authrule", "%s : rule deny", inRightName
.c_str());
273 else if (classTag
== kAuthorizationRuleClassUser
)
276 mGroupName
= Attribute::getString(cfRight
, kUserGroupID
);
277 // grab other user-in-group attributes
278 mMaxCredentialAge
= Attribute::getDouble(cfRight
, kTimeoutID
, false, DBL_MAX
);
279 mShared
= Attribute::getBool(cfRight
, kSharedID
);
280 mAllowRoot
= Attribute::getBool(cfRight
, kAllowRootID
);
281 mSessionOwner
= Attribute::getBool(cfRight
, kSessionOwnerID
);
282 // authorization tags can have eval now too
283 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
);
284 mTries
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
286 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
288 mGroupName
.c_str(), mMaxCredentialAge
, mShared
? " shared" : "",
289 mAllowRoot
? " allow-root" : "");
292 else if (classTag
== kAuthorizationRuleClassMechanisms
)
294 secdebug("authrule", "%s : rule evaluate mechanisms", inRightName
.c_str());
295 mType
= kEvaluateMechanisms
;
296 // mechanisms to evaluate
297 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
, true);
298 mTries
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 0.0)); // "forever"
300 else if (classTag
== kAuthorizationRightRule
)
302 assert(cfRules
); // this had better not be a rule
303 secdebug("authrule", "%s : rule delegate rule", inRightName
.c_str());
304 mType
= kRuleDelegation
;
307 string ruleDefString
= Attribute::getString(cfRight
, kRuleDelegateID
, false, "");
308 if (ruleDefString
.length())
310 CFStringRef ruleDefRef
= makeCFString(ruleDefString
);
311 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleDefRef
));
313 CFRelease(ruleDefRef
);
314 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
315 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
316 mRuleDef
.push_back(Rule(ruleDefString
, cfRuleDef
, NULL
));
320 vector
<string
> ruleDef
= Attribute::getVector(cfRight
, kRuleDelegateID
, true);
321 for (vector
<string
>::const_iterator it
= ruleDef
.begin(); it
!= ruleDef
.end(); it
++)
323 CFStringRef ruleNameRef
= makeCFString(*it
);
324 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
326 CFRelease(ruleNameRef
);
327 if (!cfRuleDef
|| (CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()))
328 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
329 mRuleDef
.push_back(Rule(*it
, cfRuleDef
, NULL
));
333 mKofN
= int(Attribute::getDouble(cfRight
, kKofNID
, false, 0.0));
340 secdebug("authrule", "%s : rule class unknown %s.", inRightName
.c_str(), classTag
.c_str());
341 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
346 // no class tag means, this is the abbreviated specification from the API
347 // it _must_ have a definition for "rule" which will be used as a delegate
348 // it may have a comment (not extracted here)
349 // it may have a default prompt, or a whole dictionary of languages (not extracted here)
351 mType
= kRuleDelegation
;
352 string ruleName
= Attribute::getString(cfRight
, kRuleDelegateID
, true);
353 secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName
.c_str(), ruleName
.c_str());
354 CFStringRef ruleNameRef
= makeCFString(ruleName
);
355 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
357 CFRelease(ruleNameRef
);
358 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
359 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
360 mRuleDef
.push_back(Rule(ruleName
, cfRuleDef
, NULL
));
363 Attribute::getLocalizedPrompts(cfRight
, mLocalizedPrompts
);
373 RuleImpl::setAgentHints(const AuthItemRef
&inRight
, const Rule
&inTopLevelRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
) const
375 string
authorizeString(inRight
->name());
376 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
, AuthValueOverlay(authorizeString
)));
378 // XXX/cs pid/uid/client should only be added when we're ready to call the agent
379 pid_t cPid
= Server::process().pid();
380 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(pid_t
), &cPid
)));
382 uid_t cUid
= auth
.creatorUid();
383 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(uid_t
), &cUid
)));
385 pid_t creatorPid
= auth
.creatorPid();
386 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CREATOR_PID
, AuthValueOverlay(sizeof(pid_t
), &creatorPid
)));
389 CodeSigning::OSXCode
*osxcode
= auth
.creatorCode();
391 MacOSError::throwMe(errAuthorizationDenied
);
393 string encodedBundle
= osxcode
->encode();
394 char bundleType
= (encodedBundle
.c_str())[0]; // yay, no accessor
395 string bundlePath
= osxcode
->canonicalPath();
397 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(bundleType
), &bundleType
)));
398 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(bundlePath
)));
401 map
<string
,string
> defaultPrompts
= inTopLevelRule
->localizedPrompts();
403 if (defaultPrompts
.empty())
404 defaultPrompts
= localizedPrompts();
406 if (!defaultPrompts
.empty())
408 map
<string
,string
>::const_iterator it
;
409 for (it
= defaultPrompts
.begin(); it
!= defaultPrompts
.end(); it
++)
411 const string
&key
= it
->first
;
412 const string
&value
= it
->second
;
413 environmentToClient
.insert(AuthItemRef(key
.c_str(), AuthValueOverlay(value
)));
417 // add rulename as a hint
418 string ruleName
= name();
419 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
, AuthValueOverlay(ruleName
)));
423 RuleImpl::agentNameForAuth(const AuthorizationToken
&auth
) const
426 AuthorizationBlob authBlob
= auth
.handle();
427 CssmData hashedData
= CssmData::wrap(&hash
, sizeof(hash
));
428 CssmData data
= CssmData::wrap(&authBlob
, sizeof(authBlob
));
429 CssmClient::Digest
ctx(Server::csp(), CSSM_ALGID_SHA1
);
431 ctx
.digest(data
, hashedData
);
435 secdebug("auth", "digesting authref failed (%lu)", e
.osStatus());
436 return string("SecurityAgentMechanism");
439 uint8_t *point
= static_cast<uint8_t*>(hashedData
.data());
440 for (uint8_t i
=0; i
< hashedData
.length(); point
++, i
++)
442 uint8 value
= (*point
% 62) + '0';
443 if (value
> '9') value
+= 7;
444 if (value
> 'Z') value
+= 6;
447 return string(static_cast<char *>(hashedData
.data()), hashedData
.length());
451 RuleImpl::evaluateAuthorization(const AuthItemRef
&inRight
, const Rule
&inRule
,
452 AuthItemSet
&environmentToClient
,
453 AuthorizationFlags flags
, CFAbsoluteTime now
,
454 const CredentialSet
*inCredentials
,
455 CredentialSet
&credentials
, AuthorizationToken
&auth
) const
457 OSStatus status
= errAuthorizationDenied
;
460 evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, usernamehint
);
461 if (usernamehint
.length())
462 environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER
, AuthValueOverlay(usernamehint
)));
464 if ((mType
== kUser
) && (mGroupName
.length()))
465 environmentToClient
.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP
, AuthValueOverlay(mGroupName
)));
468 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
470 Process
&cltProc
= Server::process();
471 // Authorization preserves creator's UID in setuid processes
472 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
473 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
475 AgentMechanismEvaluator
eval(cltUid
, cltProc
.session(), mEvalDef
);
477 for (tries
= 0; tries
< mTries
; tries
++)
479 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
480 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
481 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
482 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
484 status
= eval
.run(AuthValueVector(), environmentToClient
, auth
.infoSet());
486 if ((status
== errAuthorizationSuccess
) ||
487 (status
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context
489 secdebug("AuthEvalMech", "storing new context for authorization");
490 auth
.setInfoSet(eval
.context());
493 // successfully ran mechanisms to obtain credential
494 if (status
== errAuthorizationSuccess
)
496 // deny is the default
497 status
= errAuthorizationDenied
;
499 // fetch context and construct a credential to be tested
500 AuthItemSet inContext
= auth
.infoSet();
501 CredentialSet newCredentials
= makeCredentials(inContext
);
502 // clear context after extracting credentials
505 for (CredentialSet::const_iterator it
= newCredentials
.begin(); it
!= newCredentials
.end(); ++it
)
507 const Credential
& newCredential
= *it
;
509 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
510 if (newCredential
->isValid())
511 Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), newCredential
->uid(), inRight
->name());
513 // we can't be sure that the user actually exists so inhibit logging of uid
514 Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), inRight
->name());
516 if (!newCredential
->isValid())
518 reason
= SecurityAgent::invalidPassphrase
; //invalidPassphrase;
522 // verify that this credential authorizes right
523 status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
525 if (status
== errAuthorizationSuccess
)
527 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
528 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
529 // use valid credential to set context info
530 auth
.setCredentialInfo(newCredential
);
531 secdebug("SSevalMech", "added valid credential for user %s", newCredential
->username().c_str());
532 status
= errAuthorizationSuccess
;
536 reason
= SecurityAgent::userNotInGroup
; //unacceptableUser; // userNotInGroup
539 if (status
== errAuthorizationSuccess
)
543 if ((status
== errAuthorizationCanceled
) ||
544 (status
== errAuthorizationInternal
))
551 // If we fell out of the loop because of too many tries, notify user
554 reason
= SecurityAgent::tooManyTries
;
555 AuthItemRef
retryHint (AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
556 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
557 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
558 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
559 eval
.run(AuthValueVector(), environmentToClient
, auth
.infoSet());
560 // XXX/cs is this still necessary?
567 // create externally verified credentials on the basis of
568 // mechanism-provided information
570 RuleImpl::makeCredentials(const AuthItemSet
&context
) const
572 CredentialSet newCredentials
;
575 AuthItemSet::const_iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
576 if (found
== context
.end())
578 string username
= (**found
).stringValue();
579 secdebug("AuthEvalMech", "found username");
581 const uid_t
*uid
= NULL
;
582 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("uid") );
583 if (found
!= context
.end())
585 uid
= static_cast<const uid_t
*>((**found
).value().data
);
586 secdebug("AuthEvalMech", "found uid");
589 const gid_t
*gid
= NULL
;
590 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("gid") );
591 if (found
!= context
.end())
593 gid
= static_cast<const gid_t
*>((**found
).value().data
);
594 secdebug("AuthEvalMech", "found gid");
597 if (username
.length() && uid
&& gid
)
599 // credential is valid because mechanism says so
600 newCredentials
.insert(Credential(username
, *uid
, *gid
, mShared
));
604 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword
) );
605 if (found
!= context
.end())
607 secdebug("AuthEvalMech", "found password");
608 string password
= (**found
).stringValue();
609 secdebug("AuthEvalMech", "falling back on username/password credential if valid");
610 newCredentials
.insert(Credential(username
, password
, mShared
));
615 return newCredentials
;
618 // evaluate whether a good credential of the current session owner would authorize a right
620 RuleImpl::evaluateSessionOwner(const AuthItemRef
&inRight
, const Rule
&inRule
,
621 const AuthItemSet
&environment
,
622 const CFAbsoluteTime now
,
623 const AuthorizationToken
&auth
,
624 string
& usernamehint
) const
626 // username hint is taken from the user who created the authorization, unless it's clearly ineligible
627 OSStatus status
= noErr
;
628 // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
629 // meaning that a process like loginwindow that drops privs later is screwed.
630 uid_t uid
= auth
.creatorUid();
632 Server::active().longTermActivity();
633 struct passwd
*pw
= getpwuid(uid
);
636 // avoid hinting a locked account (ie. root)
637 if ( (pw
->pw_passwd
== NULL
) ||
638 strcmp(pw
->pw_passwd
, "*") ) {
639 // Check if username will authorize the request and set username to
640 // be used as a hint to the user if so
641 status
= evaluateCredentialForRight(inRight
, inRule
, environment
, now
, Credential(pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
, mShared
), true);
643 if (status
== errAuthorizationSuccess
)
644 usernamehint
= pw
->pw_name
;
653 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
654 // return errAuthorizationDenied otherwise.
656 RuleImpl::evaluateCredentialForRight(const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, CFAbsoluteTime now
, const Credential
&credential
, bool ignoreShared
) const
658 assert(mType
== kUser
);
660 // Get the username from the credential
661 const char *user
= credential
->username().c_str();
663 // If the credential is not valid or it's age is more than the allowed maximum age
664 // for a credential, deny.
665 if (!credential
->isValid())
667 secdebug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
->name());
668 return errAuthorizationDenied
;
671 if (now
- credential
->creationTime() > mMaxCredentialAge
)
673 secdebug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
->name());
674 return errAuthorizationDenied
;
677 if (!ignoreShared
&& !mShared
&& credential
->isShared())
679 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
->name());
680 return errAuthorizationDenied
;
683 // A root (uid == 0) user can do anything
684 if (credential
->uid() == 0)
686 secdebug("autheval", "user %s has uid 0, granting right %s", user
, inRight
->name());
687 return errAuthorizationSuccess
;
690 // XXX/cs replace with remembered session-owner once that functionality is added to SecurityServer
694 struct stat console_stat
;
695 if (!lstat("/dev/console", &console_stat
))
697 console_user
= console_stat
.st_uid
;
698 if (credential
->uid() == console_user
)
700 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user
, console_user
, inRight
->name());
701 return errAuthorizationSuccess
;
705 secdebug("autheval", "session-owner check failed.");
708 if (mGroupName
.length())
710 const char *groupname
= mGroupName
.c_str();
711 Server::active().longTermActivity();
712 struct group
*gr
= getgrnam(groupname
);
714 return errAuthorizationDenied
;
716 // Is this the default group of this user?
717 // PR-2875126 <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
718 // When this is fixed this warning should go away.
719 if (credential
->gid() == gr
->gr_gid
)
721 secdebug("autheval", "user %s has group %s(%d) as default group, granting right %s",
722 user
, groupname
, gr
->gr_gid
, inRight
->name());
724 return errAuthorizationSuccess
;
727 for (char **group
= gr
->gr_mem
; *group
; ++group
)
729 if (!strcmp(*group
, user
))
731 secdebug("autheval", "user %s is a member of group %s, granting right %s",
732 user
, groupname
, inRight
->name());
734 return errAuthorizationSuccess
;
738 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
739 user
, groupname
, inRight
->name());
743 return errAuthorizationDenied
;
749 RuleImpl::evaluateUser(const AuthItemRef
&inRight
, const Rule
&inRule
,
750 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
751 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
752 AuthorizationToken
&auth
) const
754 // If we got here, this is a kUser type rule, let's start looking for a
755 // credential that is satisfactory
757 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
758 // created by a proccess running as root to automatically get a right.
759 if (mAllowRoot
&& auth
.creatorUid() == 0)
761 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
763 return errAuthorizationSuccess
;
766 // if this is a "is-admin" rule check that and return
767 // XXX/cs add way to specify is-admin class of rule: if (mNoVerify)
768 if (name() == kAuthorizationRuleIsAdmin
)
771 if (!evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, username
))
772 return errAuthorizationSuccess
;
775 // First -- go though the credentials we either already used or obtained during this authorize operation.
776 for (CredentialSet::const_iterator it
= credentials
.begin(); it
!= credentials
.end(); ++it
)
778 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, true);
779 if (status
!= errAuthorizationDenied
)
781 // add credential to authinfo
782 auth
.setCredentialInfo(*it
);
787 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
790 for (CredentialSet::const_iterator it
= inCredentials
->begin(); it
!= inCredentials
->end(); ++it
)
792 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, false);
793 if (status
== errAuthorizationSuccess
)
795 // Add the credential we used to the output set.
796 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
797 credentials
.erase(*it
); credentials
.insert(*it
);
798 // add credential to authinfo
799 auth
.setCredentialInfo(*it
);
803 else if (status
!= errAuthorizationDenied
)
808 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
809 // our flags let us do so.
810 if (!(flags
& kAuthorizationFlagExtendRights
))
811 return errAuthorizationDenied
;
813 // authorizations that timeout immediately cannot be preauthorized
814 if ((flags
& kAuthorizationFlagPreAuthorize
) &&
815 (mMaxCredentialAge
== 0.0))
817 inRight
->setFlags(inRight
->flags() | kAuthorizationFlagCanNotPreAuthorize
);
818 return errAuthorizationSuccess
;
821 if (!(flags
& kAuthorizationFlagInteractionAllowed
))
822 return errAuthorizationInteractionNotAllowed
;
824 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
826 // If a different evaluation is prescribed,
827 // we'll run that and validate the credentials from there
828 // we fall back on a default configuration
829 return evaluateAuthorization(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
833 RuleImpl::evaluateMechanismOnly(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
838 Process
&cltProc
= Server::process();
839 // Authorization preserves creator's UID in setuid processes
840 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
841 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
844 AgentMechanismEvaluator
eval(cltUid
, cltProc
.session(), mEvalDef
);
848 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
849 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
850 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
852 status
= eval
.run(AuthValueVector(), environmentToClient
, auth
.infoSet());
854 if ((status
== errAuthorizationSuccess
) ||
855 (status
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context
857 secdebug("AuthEvalMech", "storing new context for authorization");
858 auth
.setInfoSet(eval
.context());
859 if (status
== errAuthorizationSuccess
)
861 outCredentials
= makeCredentials(eval
.context());
867 while ((status
== errAuthorizationDenied
) // only if we have an expected failure we continue
868 && ((mTries
== 0) // mTries == 0 means we try forever
869 || ((mTries
> 0) // mTries > 0 means we try up to mTries times
870 && (tries
< mTries
))));
873 if (name() == "system.login.console")
875 QueryInvokeMechanism
query(cltUid
, cltProc
.session());
876 query
.terminateAgent();
883 RuleImpl::evaluateRules(const AuthItemRef
&inRight
, const Rule
&inRule
,
884 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
885 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
886 AuthorizationToken
&auth
) const
888 // line up the rules to try
889 if (!mRuleDef
.size())
890 return errAuthorizationSuccess
;
893 OSStatus status
= errAuthorizationSuccess
;
894 vector
<Rule
>::const_iterator it
;
896 for (it
= mRuleDef
.begin();it
!= mRuleDef
.end(); it
++)
899 if ((mType
== kKofN
) && (count
== mKofN
))
900 return errAuthorizationSuccess
;
902 // get a rule and try it
903 status
= (*it
)->evaluate(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
905 // if status is cancel/internal error abort
906 if ((status
== errAuthorizationCanceled
) || (status
== errAuthorizationInternal
))
909 if (status
!= errAuthorizationSuccess
)
911 // continue if we're only looking for k of n
921 return status
; // return the last failure
926 RuleImpl::evaluate(const AuthItemRef
&inRight
, const Rule
&inRule
,
927 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
928 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
929 AuthorizationToken
&auth
) const
934 secdebug("autheval", "rule is always allow");
935 return errAuthorizationSuccess
;
937 secdebug("autheval", "rule is always deny");
938 return errAuthorizationDenied
;
940 secdebug("autheval", "rule is user");
941 return evaluateUser(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
942 case kRuleDelegation
:
943 secdebug("autheval", "rule evaluates rules");
944 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
946 secdebug("autheval", "rule evaluates k-of-n rules");
947 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
948 case kEvaluateMechanisms
:
949 secdebug("autheval", "rule evaluates mechanisms");
950 return evaluateMechanismOnly(inRight
, inRule
, environmentToClient
, auth
, credentials
);
952 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
956 Rule::Rule() : RefPointer
<RuleImpl
>(new RuleImpl()) {}
957 Rule::Rule(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : RefPointer
<RuleImpl
>(new RuleImpl(inRightName
, cfRight
, cfRules
)) {}
961 } // end namespace Authorization