2 * AuthorizationRule.cpp
5 * Created by Conrad Sauerwald on Wed Mar 19 2003.
6 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
10 #include "AuthorizationRule.h"
11 #include "AuthorizationTags.h"
12 #include "AuthorizationDB.h"
13 #include "AuthorizationPriv.h"
14 #include "authority.h"
27 namespace Authorization
{
29 CFStringRef
RuleImpl::kUserGroupID
= CFSTR(kAuthorizationRuleParameterGroup
);
30 CFStringRef
RuleImpl::kTimeoutID
= CFSTR(kAuthorizationRuleParameterCredentialTimeout
);
31 CFStringRef
RuleImpl::kSharedID
= CFSTR(kAuthorizationRuleParameterCredentialShared
);
32 CFStringRef
RuleImpl::kAllowRootID
= CFSTR(kAuthorizationRuleParameterAllowRoot
);
33 CFStringRef
RuleImpl::kMechanismsID
= CFSTR(kAuthorizationRuleParameterMechanisms
);
34 CFStringRef
RuleImpl::kSessionOwnerID
= CFSTR(kAuthorizationRuleParameterCredentialSessionOwner
);
35 CFStringRef
RuleImpl::kKofNID
= CFSTR(kAuthorizationRuleParameterKofN
);
36 CFStringRef
RuleImpl::kPromptID
= CFSTR(kAuthorizationRuleParameterDefaultPrompt
);
38 CFStringRef
RuleImpl::kRuleClassID
= CFSTR(kAuthorizationRuleClass
);
39 CFStringRef
RuleImpl::kRuleAllowID
= CFSTR(kAuthorizationRuleClassAllow
);
40 CFStringRef
RuleImpl::kRuleDenyID
= CFSTR(kAuthorizationRuleClassDeny
);
41 CFStringRef
RuleImpl::kRuleUserID
= CFSTR(kAuthorizationRuleClassUser
);
42 CFStringRef
RuleImpl::kRuleDelegateID
= CFSTR(kAuthorizationRightRule
);
43 CFStringRef
RuleImpl::kRuleMechanismsID
= CFSTR(kAuthorizationRuleClassMechanisms
);
46 RuleImpl::Attribute::getString(CFDictionaryRef config
, CFStringRef key
, bool required
= false, char *defaultValue
= NULL
)
48 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
49 if (value
&& (CFGetTypeID(value
) == CFStringGetTypeID()))
51 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(value
);
53 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
56 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
59 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
66 return string(defaultValue
);
68 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
72 RuleImpl::Attribute::getDouble(CFDictionaryRef config
, CFStringRef key
, bool required
= false, double defaultValue
= 0.0)
74 double doubleValue
= 0;
76 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
77 if (value
&& (CFGetTypeID(value
) == CFNumberGetTypeID()))
79 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(value
), kCFNumberDoubleType
, &doubleValue
);
85 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
91 RuleImpl::Attribute::getBool(CFDictionaryRef config
, CFStringRef key
, bool required
= false, bool defaultValue
= false)
93 bool boolValue
= false;
94 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
96 if (value
&& (CFGetTypeID(value
) == CFBooleanGetTypeID()))
98 boolValue
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(value
));
104 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
109 // add reference to string that we're modifying
111 RuleImpl::Attribute::setString(CFMutableDictionaryRef config
, CFStringRef key
, string
&value
)
113 CFStringRef cfstringValue
= CFStringCreateWithCString(NULL
/*allocator*/, value
.c_str(), kCFStringEncodingUTF8
);
117 CFDictionarySetValue(config
, key
, cfstringValue
);
118 CFRelease(cfstringValue
);
121 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
125 RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config
, CFStringRef key
, double value
)
127 CFNumberRef doubleValue
= CFNumberCreate(NULL
/*allocator*/, kCFNumberDoubleType
, doubleValue
);
131 CFDictionarySetValue(config
, key
, doubleValue
);
132 CFRelease(doubleValue
);
135 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
139 RuleImpl::Attribute::setBool(CFMutableDictionaryRef config
, CFStringRef key
, bool value
)
142 CFDictionarySetValue(config
, key
, kCFBooleanTrue
);
144 CFDictionarySetValue(config
, key
, kCFBooleanFalse
);
148 RuleImpl::Attribute::getVector(CFDictionaryRef config
, CFStringRef key
, bool required
= false)
150 vector
<string
> valueArray
;
152 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
153 if (value
&& (CFGetTypeID(value
) == CFArrayGetTypeID()))
155 CFArrayRef evalArray
= reinterpret_cast<CFArrayRef
>(value
);
157 for (int index
=0; index
< CFArrayGetCount(evalArray
); index
++)
159 CFTypeRef arrayValue
= CFArrayGetValueAtIndex(evalArray
, index
);
160 if (arrayValue
&& (CFGetTypeID(arrayValue
) == CFStringGetTypeID()))
162 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(arrayValue
);
164 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
167 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
170 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
172 valueArray
.push_back(string(ptr
));
178 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
184 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config
, map
<string
,string
> &localizedPrompts
)
186 CFIndex numberOfPrompts
= 0;
187 CFDictionaryRef promptsDict
;
188 if (CFDictionaryContainsKey(config
, kPromptID
))
190 promptsDict
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(config
, kPromptID
));
191 if (promptsDict
&& (CFGetTypeID(promptsDict
) == CFDictionaryGetTypeID()))
192 numberOfPrompts
= CFDictionaryGetCount(promptsDict
);
194 if (numberOfPrompts
== 0)
197 const void *keys
[numberOfPrompts
+1];
198 const void *values
[numberOfPrompts
+1];
199 CFDictionaryGetKeysAndValues(promptsDict
, &keys
[0], &values
[0]);
201 while (numberOfPrompts
-- > 0)
203 CFStringRef keyRef
= reinterpret_cast<CFStringRef
>(keys
[numberOfPrompts
]);
204 CFStringRef valueRef
= reinterpret_cast<CFStringRef
>(values
[numberOfPrompts
]);
205 if (!keyRef
|| (CFGetTypeID(keyRef
) != CFStringGetTypeID()))
207 if (!valueRef
|| (CFGetTypeID(valueRef
) != CFStringGetTypeID()))
209 string key
= cfString(keyRef
);
210 string value
= cfString(valueRef
);
211 localizedPrompts
["description"+key
] = value
;
219 RuleImpl::RuleImpl() :
220 mType(kUser
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
222 // XXX/cs read default descriptions from somewhere
223 // @@@ Default rule is shared admin group with 5 minute timeout
226 // return rule built from rule definition; throw if invalid.
227 RuleImpl::RuleImpl(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : mRightName(inRightName
)
229 // @@@ make sure cfRight is non mutable and never used that way
231 if (CFGetTypeID(cfRight
) != CFDictionaryGetTypeID())
232 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
236 string classTag
= Attribute::getString(cfRight
, kRuleClassID
, false, "");
238 if (classTag
.length())
240 if (classTag
== kAuthorizationRuleClassAllow
)
242 secdebug("authrule", "%s : rule allow", inRightName
.c_str());
245 else if (classTag
== kAuthorizationRuleClassDeny
)
247 secdebug("authrule", "%s : rule deny", inRightName
.c_str());
250 else if (classTag
== kAuthorizationRuleClassUser
)
253 mGroupName
= Attribute::getString(cfRight
, kUserGroupID
);
254 // grab other user-in-group attributes
255 mMaxCredentialAge
= Attribute::getDouble(cfRight
, kTimeoutID
, false, DBL_MAX
);
256 mShared
= Attribute::getBool(cfRight
, kSharedID
);
257 mAllowRoot
= Attribute::getBool(cfRight
, kAllowRootID
);
258 mSessionOwner
= Attribute::getBool(cfRight
, kSessionOwnerID
);
259 // authorization tags can have eval now too
260 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
);
263 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
265 mGroupName
.c_str(), mMaxCredentialAge
, mShared
? " shared" : "",
266 mAllowRoot
? " allow-root" : "");
269 else if (classTag
== kAuthorizationRuleClassMechanisms
)
271 secdebug("authrule", "%s : rule evaluate mechanisms", inRightName
.c_str());
272 mType
= kEvaluateMechanisms
;
273 // mechanisms to evaluate
274 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
, true);
276 else if (classTag
== kAuthorizationRightRule
)
278 assert(cfRules
); // this had better not be a rule
279 secdebug("authrule", "%s : rule delegate rule", inRightName
.c_str());
280 mType
= kRuleDelegation
;
283 string ruleDefString
= Attribute::getString(cfRight
, kRuleDelegateID
, false, "");
284 if (ruleDefString
.length())
286 CFStringRef ruleDefRef
= makeCFString(ruleDefString
);
287 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleDefRef
));
289 CFRelease(ruleDefRef
);
290 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
291 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
292 mRuleDef
.push_back(Rule(ruleDefString
, cfRuleDef
, NULL
));
296 vector
<string
> ruleDef
= Attribute::getVector(cfRight
, kRuleDelegateID
, true);
297 for (vector
<string
>::const_iterator it
= ruleDef
.begin(); it
!= ruleDef
.end(); it
++)
299 CFStringRef ruleNameRef
= makeCFString(*it
);
300 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
302 CFRelease(ruleNameRef
);
303 if (!cfRuleDef
|| (CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()))
304 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
305 mRuleDef
.push_back(Rule(*it
, cfRuleDef
, NULL
));
309 mKofN
= int(Attribute::getDouble(cfRight
, kKofNID
, false, 0.0));
316 secdebug("authrule", "%s : rule class unknown %s.", inRightName
.c_str(), classTag
.c_str());
317 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
322 // no class tag means, this is the abbreviated specification from the API
323 // it _must_ have a definition for "rule" which will be used as a delegate
324 // it may have a comment (not extracted here)
325 // it may have a default prompt, or a whole dictionary of languages (not extracted here)
327 mType
= kRuleDelegation
;
328 string ruleName
= Attribute::getString(cfRight
, kRuleDelegateID
, true);
329 secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName
.c_str(), ruleName
.c_str());
330 CFStringRef ruleNameRef
= makeCFString(ruleName
);
331 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
333 CFRelease(ruleNameRef
);
334 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
335 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
336 mRuleDef
.push_back(Rule(ruleName
, cfRuleDef
, NULL
));
339 Attribute::getLocalizedPrompts(cfRight
, mLocalizedPrompts
);
349 RuleImpl::setAgentHints(const AuthItemRef
&inRight
, const Rule
&inTopLevelRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
) const
351 string
authorizeString(inRight
->name());
352 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
, AuthValueOverlay(authorizeString
)));
354 // XXX/cs pid/uid/client should only be added when we're ready to call the agent
355 pid_t cPid
= Server::connection().process
.pid();
356 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(pid_t
), &cPid
)));
358 uid_t cUid
= auth
.creatorUid();
359 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(uid_t
), &cUid
)));
361 pid_t creatorPid
= auth
.creatorPid();
362 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CREATOR_PID
, AuthValueOverlay(sizeof(pid_t
), &creatorPid
)));
365 CodeSigning::OSXCode
*osxcode
= auth
.creatorCode();
367 MacOSError::throwMe(errAuthorizationDenied
);
369 string encodedBundle
= osxcode
->encode();
370 char bundleType
= (encodedBundle
.c_str())[0]; // yay, no accessor
371 string bundlePath
= osxcode
->canonicalPath();
373 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(bundleType
), &bundleType
)));
374 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(bundlePath
)));
377 map
<string
,string
> defaultPrompts
= inTopLevelRule
->localizedPrompts();
379 if (defaultPrompts
.empty())
380 defaultPrompts
= localizedPrompts();
382 if (!defaultPrompts
.empty())
384 map
<string
,string
>::const_iterator it
;
385 for (it
= defaultPrompts
.begin(); it
!= defaultPrompts
.end(); it
++)
387 const string
&key
= it
->first
;
388 const string
&value
= it
->second
;
389 environmentToClient
.insert(AuthItemRef(key
.c_str(), AuthValueOverlay(value
)));
393 // add rulename as a hint
394 string ruleName
= name();
395 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
, AuthValueOverlay(ruleName
)));
399 RuleImpl::agentNameForAuth(const AuthorizationToken
&auth
) const
402 AuthorizationBlob authBlob
= auth
.handle();
403 CssmData hashedData
= CssmData::wrap(&hash
, sizeof(hash
));
404 CssmData data
= CssmData::wrap(&authBlob
, sizeof(authBlob
));
405 CssmClient::Digest
ctx(Server::csp(), CSSM_ALGID_SHA1
);
407 ctx
.digest(data
, hashedData
);
411 secdebug("auth", "digesting authref failed (%lu)", e
.cssmError());
412 return string("SecurityAgentMechanism");
415 uint8_t *point
= static_cast<uint8_t*>(hashedData
.data());
416 for (uint8_t i
=0; i
< hashedData
.length(); point
++, i
++)
418 uint8 value
= (*point
% 62) + '0';
419 if (value
> '9') value
+= 7;
420 if (value
> 'Z') value
+= 6;
423 return string(static_cast<char *>(hashedData
.data()), hashedData
.length());
427 RuleImpl::evaluateMechanism(const AuthItemRef
&inRight
, const AuthItemSet
&environment
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
429 string agentName
= agentNameForAuth(auth
);
431 // @@@ configuration does not support arguments
432 AuthValueVector arguments
;
433 // XXX/cs Move this up - we shouldn't know how to retrieve the ingoing context
434 AuthItemSet context
= auth
.infoSet();
435 AuthItemSet hints
= environment
;
437 AuthorizationResult result
= kAuthorizationResultAllow
;
438 vector
<string
>::const_iterator currentMechanism
= mEvalDef
.begin();
440 while ( (result
== kAuthorizationResultAllow
) &&
441 (currentMechanism
!= mEvalDef
.end()) ) // iterate mechanisms
443 string::size_type extPlugin
= currentMechanism
->find(':');
444 if (extPlugin
!= string::npos
)
446 // no whitespace removal
447 string
pluginIn(currentMechanism
->substr(0, extPlugin
));
448 string
mechanismIn(currentMechanism
->substr(extPlugin
+ 1));
449 secdebug("SSevalMech", "external mech %s:%s", pluginIn
.c_str(), mechanismIn
.c_str());
451 bool mechExecOk
= false; // successfully ran a mechanism
453 Process
&cltProc
= Server::active().connection().process
;
454 // Authorization preserves creator's UID in setuid processes
455 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
456 secdebug("SSevalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
457 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
460 mechExecOk
= client(pluginIn
, mechanismIn
, arguments
, hints
, context
, &result
);
463 secdebug("SSevalMech", "exception from mech eval or client death");
464 // various server problems, but only if it really failed
465 if (mechExecOk
!= true)
466 result
= kAuthorizationResultUndefined
;
469 secdebug("SSevalMech", "evaluate(plugin: %s, mechanism: %s) %s, result: %lu.", pluginIn
.c_str(), mechanismIn
.c_str(), (mechExecOk
== true) ? "succeeded" : "failed", result
);
473 // internal mechanisms - no glue
474 if (*currentMechanism
== "authinternal")
476 secdebug("SSevalMech", "evaluate authinternal");
477 result
= kAuthorizationResultDeny
;
479 AuthItemSet::iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
480 if (found
== context
.end())
482 string
username(static_cast<const char *>((*found
)->value().data
), (*found
)->value().length
);
483 secdebug("SSevalMech", "found username");
484 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword
) );
485 if (found
== context
.end())
487 string
password(static_cast<const char *>((*found
)->value().data
), (*found
)->value().length
);
488 secdebug("SSevalMech", "found password");
489 Credential
newCredential(username
, password
, true); // create a new shared credential
491 if (newCredential
->isValid())
492 Syslog::info("authinternal authenticated user %s (uid %lu) for right %s.", newCredential
->username().c_str(), newCredential
->uid(), inRight
->name());
494 // we can't be sure that the user actually exists so inhibit logging of uid
495 Syslog::error("authinternal failed to authenticate user %s for right %s.", newCredential
->username().c_str(), inRight
->name());
497 if (newCredential
->isValid())
499 outCredentials
.clear(); // only keep last one
500 secdebug("SSevalMech", "inserting new credential");
501 outCredentials
.insert(newCredential
);
502 result
= kAuthorizationResultAllow
;
504 result
= kAuthorizationResultDeny
;
508 if (*currentMechanism
== "push_hints_to_context")
510 secdebug("SSevalMech", "evaluate push_hints_to_context");
511 mTries
= 1; // XXX/cs this should be set in authorization config
512 result
= kAuthorizationResultAllow
; // snarfcredential doesn't block evaluation, ever, it may restart
513 // create out context from input hints, no merge
514 // @@@ global copy template not being invoked...
518 if (*currentMechanism
== "switch_to_user")
520 Process
&cltProc
= Server::active().connection().process
;
521 // Authorization preserves creator's UID in setuid processes
522 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
523 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
524 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
527 client
.terminateAgent();
531 result
= kAuthorizationResultAllow
;
535 // we own outHints and outContext
538 case kAuthorizationResultAllow
:
539 secdebug("SSevalMech", "result allow");
542 case kAuthorizationResultDeny
:
543 secdebug("SSevalMech", "result deny");
545 case kAuthorizationResultUndefined
:
546 secdebug("SSevalMech", "result undefined");
547 break; // abort evaluation
548 case kAuthorizationResultUserCanceled
:
549 secdebug("SSevalMech", "result canceled");
550 break; // stop evaluation, return some sideband
552 break; // abort evaluation
556 // End of evaluation, if last step produced meaningful data, incorporate
557 if ((result
== kAuthorizationResultAllow
) ||
558 (result
== kAuthorizationResultUserCanceled
)) // @@@ can only pass back sideband through context
560 secdebug("SSevalMech", "storing new context for authorization");
561 auth
.setInfoSet(context
);
566 case kAuthorizationResultDeny
:
567 return errAuthorizationDenied
;
568 case kAuthorizationResultUserCanceled
:
569 return errAuthorizationCanceled
;
570 case kAuthorizationResultAllow
:
571 return errAuthorizationSuccess
;
573 return errAuthorizationInternal
;
580 RuleImpl::evaluateAuthorization(const AuthItemRef
&inRight
, const Rule
&inRule
,
581 AuthItemSet
&environmentToClient
,
582 AuthorizationFlags flags
, CFAbsoluteTime now
,
583 const CredentialSet
*inCredentials
,
584 CredentialSet
&credentials
, AuthorizationToken
&auth
) const
586 OSStatus status
= errAuthorizationDenied
;
589 evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, usernamehint
);
590 if (usernamehint
.length())
591 environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER
, AuthValueOverlay(usernamehint
)));
593 if ((mType
== kUser
) && (mGroupName
.length()))
594 environmentToClient
.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP
, AuthValueOverlay(mGroupName
)));
597 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
599 for (tries
= 0; tries
< mTries
; tries
++)
601 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
602 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
603 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
604 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
606 status
= evaluateMechanism(inRight
, environmentToClient
, auth
, credentials
);
608 // successfully ran mechanisms to obtain credential
609 if (status
== errAuthorizationSuccess
)
611 // deny is the default
612 status
= errAuthorizationDenied
;
614 // fetch context and construct a credential to be tested
615 AuthItemSet inContext
= auth
.infoSet();
616 CredentialSet newCredentials
= makeCredentials(inContext
);
618 for (CredentialSet::const_iterator it
= newCredentials
.begin(); it
!= newCredentials
.end(); ++it
)
620 const Credential
& newCredential
= *it
;
622 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
623 if (newCredential
->isValid())
624 Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), newCredential
->uid(), inRight
->name());
626 // we can't be sure that the user actually exists so inhibit logging of uid
627 Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), inRight
->name());
629 if (!newCredential
->isValid())
631 reason
= SecurityAgent::invalidPassphrase
; //invalidPassphrase;
635 // verify that this credential authorizes right
636 status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
638 if (status
== errAuthorizationSuccess
)
640 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
641 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
642 secdebug("SSevalMech", "added valid credential for user %s", newCredential
->username().c_str());
643 status
= errAuthorizationSuccess
;
647 reason
= SecurityAgent::userNotInGroup
; //unacceptableUser; // userNotInGroup
650 if (status
== errAuthorizationSuccess
)
654 if ((status
== errAuthorizationCanceled
) ||
655 (status
== errAuthorizationInternal
))
659 // If we fell out of the loop because of too many tries, notify user
662 reason
= SecurityAgent::tooManyTries
;
663 AuthItemRef
retryHint (AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
664 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
665 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
666 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
667 evaluateMechanism(inRight
, environmentToClient
, auth
, credentials
);
670 Process
&cltProc
= Server::active().connection().process
;
671 // Authorization preserves creator's UID in setuid processes
672 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
673 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
674 string agentName
= agentNameForAuth(auth
);
675 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
678 client
.terminateAgent();
686 // create externally verified credentials on the basis of
687 // mechanism-provided information
689 RuleImpl::makeCredentials(const AuthItemSet
&context
) const
691 CredentialSet newCredentials
;
694 AuthItemSet::const_iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
695 if (found
== context
.end())
697 string username
= (**found
).stringValue();
698 secdebug("SSevalMech", "found username");
700 const uid_t
*uid
= NULL
;
701 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("uid") );
702 if (found
!= context
.end())
704 uid
= static_cast<const uid_t
*>((**found
).value().data
);
705 secdebug("SSevalMech", "found uid");
708 const gid_t
*gid
= NULL
;
709 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("gid") );
710 if (found
!= context
.end())
712 gid
= static_cast<const gid_t
*>((**found
).value().data
);
713 secdebug("SSevalMech", "found gid");
716 if (username
.length() && uid
&& gid
)
718 // credential is valid because mechanism says so
719 newCredentials
.insert(Credential(username
, *uid
, *gid
, mShared
));
723 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword
) );
724 if (found
!= context
.end())
726 secdebug("SSevalMech", "found password");
727 string password
= (**found
).stringValue();
728 secdebug("SSevalMech", "falling back on username/password credential if valid");
729 newCredentials
.insert(Credential(username
, password
, mShared
));
734 return newCredentials
;
737 // evaluate whether a good credential of the current session owner would authorize a right
739 RuleImpl::evaluateSessionOwner(const AuthItemRef
&inRight
, const Rule
&inRule
,
740 const AuthItemSet
&environment
,
741 const CFAbsoluteTime now
,
742 const AuthorizationToken
&auth
,
743 string
& usernamehint
) const
745 // username hint is taken from the user who created the authorization, unless it's clearly ineligible
746 OSStatus status
= noErr
;
747 // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
748 // meaning that a process like loginwindow that drops privs later is screwed.
749 uid_t uid
= auth
.creatorUid();
751 Server::active().longTermActivity();
752 struct passwd
*pw
= getpwuid(uid
);
755 // avoid hinting a locked account (ie. root)
756 if ( (pw
->pw_passwd
== NULL
) ||
757 strcmp(pw
->pw_passwd
, "*") ) {
758 // Check if username will authorize the request and set username to
759 // be used as a hint to the user if so
760 status
= evaluateCredentialForRight(inRight
, inRule
, environment
, now
, Credential(pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
, mShared
), true);
762 if (status
== errAuthorizationSuccess
)
763 usernamehint
= pw
->pw_name
;
772 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
773 // return errAuthorizationDenied otherwise.
775 RuleImpl::evaluateCredentialForRight(const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, CFAbsoluteTime now
, const Credential
&credential
, bool ignoreShared
) const
777 assert(mType
== kUser
);
779 // Get the username from the credential
780 const char *user
= credential
->username().c_str();
782 // If the credential is not valid or it's age is more than the allowed maximum age
783 // for a credential, deny.
784 if (!credential
->isValid())
786 secdebug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
->name());
787 return errAuthorizationDenied
;
790 if (now
- credential
->creationTime() > mMaxCredentialAge
)
792 secdebug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
->name());
793 return errAuthorizationDenied
;
796 if (!ignoreShared
&& !mShared
&& credential
->isShared())
798 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
->name());
799 return errAuthorizationDenied
;
802 // A root (uid == 0) user can do anything
803 if (credential
->uid() == 0)
805 secdebug("autheval", "user %s has uid 0, granting right %s", user
, inRight
->name());
806 return errAuthorizationSuccess
;
809 // XXX/cs replace with remembered session-owner once that functionality is added to SecurityServer
813 struct stat console_stat
;
814 if (!lstat("/dev/console", &console_stat
))
816 console_user
= console_stat
.st_uid
;
817 if (credential
->uid() == console_user
)
819 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user
, console_user
, inRight
->name());
820 return errAuthorizationSuccess
;
824 secdebug("autheval", "session-owner check failed.");
827 if (mGroupName
.length())
829 const char *groupname
= mGroupName
.c_str();
830 Server::active().longTermActivity();
831 struct group
*gr
= getgrnam(groupname
);
833 return errAuthorizationDenied
;
835 // Is this the default group of this user?
836 // PR-2875126 <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
837 // When this is fixed this warning should go away.
838 if (credential
->gid() == gr
->gr_gid
)
840 secdebug("autheval", "user %s has group %s(%d) as default group, granting right %s",
841 user
, groupname
, gr
->gr_gid
, inRight
->name());
843 return errAuthorizationSuccess
;
846 for (char **group
= gr
->gr_mem
; *group
; ++group
)
848 if (!strcmp(*group
, user
))
850 secdebug("autheval", "user %s is a member of group %s, granting right %s",
851 user
, groupname
, inRight
->name());
853 return errAuthorizationSuccess
;
857 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
858 user
, groupname
, inRight
->name());
862 return errAuthorizationDenied
;
866 RuleImpl::evaluateUser(const AuthItemRef
&inRight
, const Rule
&inRule
,
867 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
868 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
869 AuthorizationToken
&auth
) const
871 // If we got here, this is a kUser type rule, let's start looking for a
872 // credential that is satisfactory
874 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
875 // created by a proccess running as root to automatically get a right.
876 if (mAllowRoot
&& auth
.creatorUid() == 0)
878 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
880 return errAuthorizationSuccess
;
883 // if this is a "is-admin" rule check that and return
884 // XXX/cs add way to specify is-admin class of rule: if (mNoVerify)
885 if (name() == kAuthorizationRuleIsAdmin
)
888 if (!evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, username
))
889 return errAuthorizationSuccess
;
892 // First -- go though the credentials we either already used or obtained during this authorize operation.
893 for (CredentialSet::const_iterator it
= credentials
.begin(); it
!= credentials
.end(); ++it
)
895 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, true);
896 if (status
!= errAuthorizationDenied
)
898 // add credential to authinfo
899 auth
.setCredentialInfo(*it
);
904 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
907 for (CredentialSet::const_iterator it
= inCredentials
->begin(); it
!= inCredentials
->end(); ++it
)
909 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, false);
910 if (status
== errAuthorizationSuccess
)
912 // Add the credential we used to the output set.
913 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
914 credentials
.erase(*it
); credentials
.insert(*it
);
915 // add credential to authinfo
916 auth
.setCredentialInfo(*it
);
920 else if (status
!= errAuthorizationDenied
)
925 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
926 // our flags let us do so.
927 if (!(flags
& kAuthorizationFlagExtendRights
))
928 return errAuthorizationDenied
;
930 // authorizations that timeout immediately cannot be preauthorized
931 if ((flags
& kAuthorizationFlagPreAuthorize
) &&
932 (mMaxCredentialAge
== 0.0))
934 inRight
->setFlags(inRight
->flags() | kAuthorizationFlagCanNotPreAuthorize
);
935 return errAuthorizationSuccess
;
938 if (!(flags
& kAuthorizationFlagInteractionAllowed
))
939 return errAuthorizationInteractionNotAllowed
;
941 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
943 // If a different evaluation is prescribed,
944 // we'll run that and validate the credentials from there
945 // we fall back on a default configuration
946 if (mEvalDef
.size() == 0)
947 return evaluateAuthorizationOld(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
949 return evaluateAuthorization(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
952 // XXX/cs insert a mechanism that let's the agent live (keep-alive) only in loginwindow's case
954 RuleImpl::evaluateMechanismOnly(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
961 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
962 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
963 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
965 status
= evaluateMechanism(inRight
, environmentToClient
, auth
, outCredentials
);
968 while ((status
== errAuthorizationDenied
) // only if we have an expected failure we continue
969 && ((mTries
== 0) // mTries == 0 means we try forever
970 || ((mTries
> 0) // mTries > 0 means we try up to mTries times
971 && (tries
< mTries
))));
973 if (name() != "system.login.console")
976 string agentName
= agentNameForAuth(auth
);
977 Process
&cltProc
= Server::active().connection().process
;
978 // Authorization preserves creator's UID in setuid processes
979 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
980 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
982 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
986 client
.terminateAgent();
995 RuleImpl::evaluateRules(const AuthItemRef
&inRight
, const Rule
&inRule
,
996 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
997 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
998 AuthorizationToken
&auth
) const
1000 // line up the rules to try
1001 if (!mRuleDef
.size())
1002 return errAuthorizationSuccess
;
1005 OSStatus status
= errAuthorizationSuccess
;
1006 vector
<Rule
>::const_iterator it
;
1008 for (it
= mRuleDef
.begin();it
!= mRuleDef
.end(); it
++)
1011 if ((mType
== kKofN
) && (count
== mKofN
))
1012 return errAuthorizationSuccess
;
1014 // get a rule and try it
1015 status
= (*it
)->evaluate(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1017 // if status is cancel/internal error abort
1018 if ((status
== errAuthorizationCanceled
) || (status
== errAuthorizationInternal
))
1021 if (status
!= errAuthorizationSuccess
)
1023 // continue if we're only looking for k of n
1033 return status
; // return the last failure
1038 RuleImpl::evaluate(const AuthItemRef
&inRight
, const Rule
&inRule
,
1039 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
1040 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
1041 AuthorizationToken
&auth
) const
1046 secdebug("autheval", "rule is always allow");
1047 return errAuthorizationSuccess
;
1049 secdebug("autheval", "rule is always deny");
1050 return errAuthorizationDenied
;
1052 secdebug("autheval", "rule is user");
1053 return evaluateUser(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1054 case kRuleDelegation
:
1055 secdebug("autheval", "rule evaluates rules");
1056 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1058 secdebug("autheval", "rule evaluates k-of-n rules");
1059 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1060 case kEvaluateMechanisms
:
1061 secdebug("autheval", "rule evaluates mechanisms");
1062 return evaluateMechanismOnly(inRight
, inRule
, environmentToClient
, auth
, credentials
);
1064 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
1071 // This is slated to be removed when the new auth panel is fixed up
1073 RuleImpl::evaluateAuthorizationOld(const AuthItemRef
&inRight
, const Rule
&inRule
,
1074 AuthItemSet
&environmentToClient
,
1075 AuthorizationFlags flags
, CFAbsoluteTime now
,
1076 const CredentialSet
*inCredentials
,
1077 CredentialSet
&credentials
, AuthorizationToken
&auth
) const
1079 Process
&cltProc
= Server::active().connection().process
;
1080 // Authorization preserves creator's UID in setuid processes
1081 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
1082 secdebug("autheval", "Auth query from process %d (UID %d)", cltProc
.pid(), cltUid
);
1083 QueryAuthorizeByGroup
query(cltUid
, auth
);
1085 string usernamehint
;
1087 evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, usernamehint
);
1089 Credential newCredential
;
1090 // @@@ Keep the default reason the same, so the agent only gets userNotInGroup or invalidPassphrase
1091 SecurityAgent::Reason reason
= SecurityAgent::userNotInGroup
;
1092 // @@@ Hardcoded 3 tries to avoid infinite loops.
1093 for (uint32_t tryCount
= 0; tryCount
< mTries
; ++tryCount
)
1095 // Obtain a new credential. Anything but success is considered an error.
1096 OSStatus status
= obtainCredential(query
, inRight
, environmentToClient
, usernamehint
.c_str(), newCredential
, reason
);
1100 // Now we have successfully obtained a credential we need to make sure it authorizes the requested right
1101 if (!newCredential
->isValid())
1102 reason
= SecurityAgent::invalidPassphrase
;
1104 status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
1105 if (status
== errAuthorizationSuccess
)
1107 // Add the new credential we obtained to the output set.
1108 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
1109 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
1112 // add credential to authinfo
1113 auth
.setCredentialInfo(newCredential
);
1115 return errAuthorizationSuccess
;
1117 else if (status
!= errAuthorizationDenied
)
1120 reason
= SecurityAgent::userNotInGroup
;
1122 query
.cancel(SecurityAgent::tooManyTries
);
1123 return errAuthorizationDenied
;
1127 RuleImpl::obtainCredential(QueryAuthorizeByGroup
&query
, const AuthItemRef
&inRight
,
1128 AuthItemSet
&environmentToClient
, const char *usernameHint
, Credential
&outCredential
, SecurityAgent::Reason reason
) const
1130 char nameBuffer
[SecurityAgent::maxUsernameLength
];
1131 char passphraseBuffer
[SecurityAgent::maxPassphraseLength
];
1132 OSStatus status
= errAuthorizationDenied
;
1135 if (query(mGroupName
.c_str(), usernameHint
, nameBuffer
, passphraseBuffer
, reason
))
1137 } catch (const CssmCommonError
&err
) {
1138 status
= err
.osStatus();
1140 status
= errAuthorizationInternal
;
1142 if (status
== CSSM_ERRCODE_USER_CANCELED
)
1144 secdebug("auth", "canceled obtaining credential for user in group %s", mGroupName
.c_str());
1145 return errAuthorizationCanceled
;
1147 if (status
== CSSM_ERRCODE_NO_USER_INTERACTION
)
1149 secdebug("auth", "user interaction not possible obtaining credential for user in group %s", mGroupName
.c_str());
1150 return errAuthorizationInteractionNotAllowed
;
1153 if (status
!= noErr
)
1155 secdebug("auth", "failed obtaining credential for user in group %s", mGroupName
.c_str());
1159 secdebug("auth", "obtained credential for user %s", nameBuffer
);
1160 string
username(nameBuffer
);
1161 string
password(passphraseBuffer
);
1162 outCredential
= Credential(username
, password
, mShared
);
1163 return errAuthorizationSuccess
;
1167 Rule::Rule() : RefPointer
<RuleImpl
>(new RuleImpl()) {}
1168 Rule::Rule(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : RefPointer
<RuleImpl
>(new RuleImpl(inRightName
, cfRight
, cfRules
)) {}
1171 } // end namespace Authorization