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"
23 #include <bsm/libbsm.h>
24 #include <bsm/audit_uevents.h>
30 namespace Authorization
{
32 CFStringRef
RuleImpl::kUserGroupID
= CFSTR(kAuthorizationRuleParameterGroup
);
33 CFStringRef
RuleImpl::kTimeoutID
= CFSTR(kAuthorizationRuleParameterCredentialTimeout
);
34 CFStringRef
RuleImpl::kSharedID
= CFSTR(kAuthorizationRuleParameterCredentialShared
);
35 CFStringRef
RuleImpl::kAllowRootID
= CFSTR(kAuthorizationRuleParameterAllowRoot
);
36 CFStringRef
RuleImpl::kMechanismsID
= CFSTR(kAuthorizationRuleParameterMechanisms
);
37 CFStringRef
RuleImpl::kSessionOwnerID
= CFSTR(kAuthorizationRuleParameterCredentialSessionOwner
);
38 CFStringRef
RuleImpl::kKofNID
= CFSTR(kAuthorizationRuleParameterKofN
);
39 CFStringRef
RuleImpl::kPromptID
= CFSTR(kAuthorizationRuleParameterDefaultPrompt
);
41 CFStringRef
RuleImpl::kRuleClassID
= CFSTR(kAuthorizationRuleClass
);
42 CFStringRef
RuleImpl::kRuleAllowID
= CFSTR(kAuthorizationRuleClassAllow
);
43 CFStringRef
RuleImpl::kRuleDenyID
= CFSTR(kAuthorizationRuleClassDeny
);
44 CFStringRef
RuleImpl::kRuleUserID
= CFSTR(kAuthorizationRuleClassUser
);
45 CFStringRef
RuleImpl::kRuleDelegateID
= CFSTR(kAuthorizationRightRule
);
46 CFStringRef
RuleImpl::kRuleMechanismsID
= CFSTR(kAuthorizationRuleClassMechanisms
);
50 RuleImpl::Attribute::getString(CFDictionaryRef config
, CFStringRef key
, bool required
= false, char *defaultValue
= NULL
)
52 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
53 if (value
&& (CFGetTypeID(value
) == CFStringGetTypeID()))
55 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(value
);
57 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
60 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
63 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
70 return string(defaultValue
);
72 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
76 RuleImpl::Attribute::getDouble(CFDictionaryRef config
, CFStringRef key
, bool required
= false, double defaultValue
= 0.0)
78 double doubleValue
= 0;
80 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
81 if (value
&& (CFGetTypeID(value
) == CFNumberGetTypeID()))
83 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(value
), kCFNumberDoubleType
, &doubleValue
);
89 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
95 RuleImpl::Attribute::getBool(CFDictionaryRef config
, CFStringRef key
, bool required
= false, bool defaultValue
= false)
97 bool boolValue
= false;
98 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
100 if (value
&& (CFGetTypeID(value
) == CFBooleanGetTypeID()))
102 boolValue
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(value
));
108 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
113 // add reference to string that we're modifying
115 RuleImpl::Attribute::setString(CFMutableDictionaryRef config
, CFStringRef key
, string
&value
)
117 CFStringRef cfstringValue
= CFStringCreateWithCString(NULL
/*allocator*/, value
.c_str(), kCFStringEncodingUTF8
);
121 CFDictionarySetValue(config
, key
, cfstringValue
);
122 CFRelease(cfstringValue
);
125 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
129 RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config
, CFStringRef key
, double value
)
131 CFNumberRef doubleValue
= CFNumberCreate(NULL
/*allocator*/, kCFNumberDoubleType
, doubleValue
);
135 CFDictionarySetValue(config
, key
, doubleValue
);
136 CFRelease(doubleValue
);
139 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid attribute
143 RuleImpl::Attribute::setBool(CFMutableDictionaryRef config
, CFStringRef key
, bool value
)
146 CFDictionarySetValue(config
, key
, kCFBooleanTrue
);
148 CFDictionarySetValue(config
, key
, kCFBooleanFalse
);
152 RuleImpl::Attribute::getVector(CFDictionaryRef config
, CFStringRef key
, bool required
= false)
154 vector
<string
> valueArray
;
156 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
157 if (value
&& (CFGetTypeID(value
) == CFArrayGetTypeID()))
159 CFArrayRef evalArray
= reinterpret_cast<CFArrayRef
>(value
);
161 for (int index
=0; index
< CFArrayGetCount(evalArray
); index
++)
163 CFTypeRef arrayValue
= CFArrayGetValueAtIndex(evalArray
, index
);
164 if (arrayValue
&& (CFGetTypeID(arrayValue
) == CFStringGetTypeID()))
166 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(arrayValue
);
168 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
171 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
174 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
176 valueArray
.push_back(string(ptr
));
182 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
188 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config
, map
<string
,string
> &localizedPrompts
)
190 CFIndex numberOfPrompts
= 0;
191 CFDictionaryRef promptsDict
;
192 if (CFDictionaryContainsKey(config
, kPromptID
))
194 promptsDict
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(config
, kPromptID
));
195 if (promptsDict
&& (CFGetTypeID(promptsDict
) == CFDictionaryGetTypeID()))
196 numberOfPrompts
= CFDictionaryGetCount(promptsDict
);
198 if (numberOfPrompts
== 0)
201 const void *keys
[numberOfPrompts
+1];
202 const void *values
[numberOfPrompts
+1];
203 CFDictionaryGetKeysAndValues(promptsDict
, &keys
[0], &values
[0]);
205 while (numberOfPrompts
-- > 0)
207 CFStringRef keyRef
= reinterpret_cast<CFStringRef
>(keys
[numberOfPrompts
]);
208 CFStringRef valueRef
= reinterpret_cast<CFStringRef
>(values
[numberOfPrompts
]);
209 if (!keyRef
|| (CFGetTypeID(keyRef
) != CFStringGetTypeID()))
211 if (!valueRef
|| (CFGetTypeID(valueRef
) != CFStringGetTypeID()))
213 string key
= cfString(keyRef
);
214 string value
= cfString(valueRef
);
215 localizedPrompts
["description"+key
] = value
;
223 RuleImpl::RuleImpl() :
224 mType(kUser
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
226 // XXX/cs read default descriptions from somewhere
227 // @@@ Default rule is shared admin group with 5 minute timeout
230 // return rule built from rule definition; throw if invalid.
231 RuleImpl::RuleImpl(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : mRightName(inRightName
)
233 // @@@ make sure cfRight is non mutable and never used that way
235 if (CFGetTypeID(cfRight
) != CFDictionaryGetTypeID())
236 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
240 string classTag
= Attribute::getString(cfRight
, kRuleClassID
, false, "");
242 if (classTag
.length())
244 if (classTag
== kAuthorizationRuleClassAllow
)
246 secdebug("authrule", "%s : rule allow", inRightName
.c_str());
249 else if (classTag
== kAuthorizationRuleClassDeny
)
251 secdebug("authrule", "%s : rule deny", inRightName
.c_str());
254 else if (classTag
== kAuthorizationRuleClassUser
)
257 mGroupName
= Attribute::getString(cfRight
, kUserGroupID
);
258 // grab other user-in-group attributes
259 mMaxCredentialAge
= Attribute::getDouble(cfRight
, kTimeoutID
, false, DBL_MAX
);
260 mShared
= Attribute::getBool(cfRight
, kSharedID
);
261 mAllowRoot
= Attribute::getBool(cfRight
, kAllowRootID
);
262 mSessionOwner
= Attribute::getBool(cfRight
, kSessionOwnerID
);
263 // authorization tags can have eval now too
264 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
);
267 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
269 mGroupName
.c_str(), mMaxCredentialAge
, mShared
? " shared" : "",
270 mAllowRoot
? " allow-root" : "");
273 else if (classTag
== kAuthorizationRuleClassMechanisms
)
275 secdebug("authrule", "%s : rule evaluate mechanisms", inRightName
.c_str());
276 mType
= kEvaluateMechanisms
;
277 // mechanisms to evaluate
278 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
, true);
280 else if (classTag
== kAuthorizationRightRule
)
282 assert(cfRules
); // this had better not be a rule
283 secdebug("authrule", "%s : rule delegate rule", inRightName
.c_str());
284 mType
= kRuleDelegation
;
287 string ruleDefString
= Attribute::getString(cfRight
, kRuleDelegateID
, false, "");
288 if (ruleDefString
.length())
290 CFStringRef ruleDefRef
= makeCFString(ruleDefString
);
291 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleDefRef
));
293 CFRelease(ruleDefRef
);
294 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
295 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
296 mRuleDef
.push_back(Rule(ruleDefString
, cfRuleDef
, NULL
));
300 vector
<string
> ruleDef
= Attribute::getVector(cfRight
, kRuleDelegateID
, true);
301 for (vector
<string
>::const_iterator it
= ruleDef
.begin(); it
!= ruleDef
.end(); it
++)
303 CFStringRef ruleNameRef
= makeCFString(*it
);
304 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
306 CFRelease(ruleNameRef
);
307 if (!cfRuleDef
|| (CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()))
308 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
309 mRuleDef
.push_back(Rule(*it
, cfRuleDef
, NULL
));
313 mKofN
= int(Attribute::getDouble(cfRight
, kKofNID
, false, 0.0));
320 secdebug("authrule", "%s : rule class unknown %s.", inRightName
.c_str(), classTag
.c_str());
321 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
326 // no class tag means, this is the abbreviated specification from the API
327 // it _must_ have a definition for "rule" which will be used as a delegate
328 // it may have a comment (not extracted here)
329 // it may have a default prompt, or a whole dictionary of languages (not extracted here)
331 mType
= kRuleDelegation
;
332 string ruleName
= Attribute::getString(cfRight
, kRuleDelegateID
, true);
333 secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName
.c_str(), ruleName
.c_str());
334 CFStringRef ruleNameRef
= makeCFString(ruleName
);
335 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
337 CFRelease(ruleNameRef
);
338 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
339 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
340 mRuleDef
.push_back(Rule(ruleName
, cfRuleDef
, NULL
));
343 Attribute::getLocalizedPrompts(cfRight
, mLocalizedPrompts
);
353 RuleImpl::setAgentHints(const AuthItemRef
&inRight
, const Rule
&inTopLevelRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
) const
355 string
authorizeString(inRight
->name());
356 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
, AuthValueOverlay(authorizeString
)));
358 // XXX/cs pid/uid/client should only be added when we're ready to call the agent
359 pid_t cPid
= Server::connection().process
.pid();
360 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PID
, AuthValueOverlay(sizeof(pid_t
), &cPid
)));
362 uid_t cUid
= auth
.creatorUid();
363 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_UID
, AuthValueOverlay(sizeof(uid_t
), &cUid
)));
365 pid_t creatorPid
= auth
.creatorPid();
366 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CREATOR_PID
, AuthValueOverlay(sizeof(pid_t
), &creatorPid
)));
369 CodeSigning::OSXCode
*osxcode
= auth
.creatorCode();
371 MacOSError::throwMe(errAuthorizationDenied
);
373 string encodedBundle
= osxcode
->encode();
374 char bundleType
= (encodedBundle
.c_str())[0]; // yay, no accessor
375 string bundlePath
= osxcode
->canonicalPath();
377 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE
, AuthValueOverlay(sizeof(bundleType
), &bundleType
)));
378 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH
, AuthValueOverlay(bundlePath
)));
381 map
<string
,string
> defaultPrompts
= inTopLevelRule
->localizedPrompts();
383 if (defaultPrompts
.empty())
384 defaultPrompts
= localizedPrompts();
386 if (!defaultPrompts
.empty())
388 map
<string
,string
>::const_iterator it
;
389 for (it
= defaultPrompts
.begin(); it
!= defaultPrompts
.end(); it
++)
391 const string
&key
= it
->first
;
392 const string
&value
= it
->second
;
393 environmentToClient
.insert(AuthItemRef(key
.c_str(), AuthValueOverlay(value
)));
397 // add rulename as a hint
398 string ruleName
= name();
399 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
, AuthValueOverlay(ruleName
)));
403 RuleImpl::agentNameForAuth(const AuthorizationToken
&auth
) const
406 AuthorizationBlob authBlob
= auth
.handle();
407 CssmData hashedData
= CssmData::wrap(&hash
, sizeof(hash
));
408 CssmData data
= CssmData::wrap(&authBlob
, sizeof(authBlob
));
409 CssmClient::Digest
ctx(Server::csp(), CSSM_ALGID_SHA1
);
411 ctx
.digest(data
, hashedData
);
415 secdebug("auth", "digesting authref failed (%lu)", e
.cssmError());
416 return string("SecurityAgentMechanism");
419 uint8_t *point
= static_cast<uint8_t*>(hashedData
.data());
420 for (uint8_t i
=0; i
< hashedData
.length(); point
++, i
++)
422 uint8 value
= (*point
% 62) + '0';
423 if (value
> '9') value
+= 7;
424 if (value
> 'Z') value
+= 6;
427 return string(static_cast<char *>(hashedData
.data()), hashedData
.length());
431 RuleImpl::evaluateMechanism(const AuthItemRef
&inRight
, const AuthItemSet
&environment
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
433 string agentName
= agentNameForAuth(auth
);
435 // @@@ configuration does not support arguments
436 AuthValueVector arguments
;
437 // XXX/cs Move this up - we shouldn't know how to retrieve the ingoing context
438 AuthItemSet context
= auth
.infoSet();
439 AuthItemSet hints
= environment
;
441 CommonCriteria::AuditRecord
auditrec(auth
.creatorAuditToken());
443 AuthorizationResult result
= kAuthorizationResultAllow
;
444 vector
<string
>::const_iterator currentMechanism
= mEvalDef
.begin();
446 while ( (result
== kAuthorizationResultAllow
) &&
447 (currentMechanism
!= mEvalDef
.end()) ) // iterate mechanisms
449 string::size_type extPlugin
= currentMechanism
->find(':');
450 if (extPlugin
!= string::npos
)
452 // no whitespace removal
453 string
pluginIn(currentMechanism
->substr(0, extPlugin
));
454 string
mechanismIn(currentMechanism
->substr(extPlugin
+ 1));
455 secdebug("SSevalMech", "external mech %s:%s", pluginIn
.c_str(), mechanismIn
.c_str());
457 bool mechExecOk
= false; // successfully ran a mechanism
459 Process
&cltProc
= Server::active().connection().process
;
460 // Authorization preserves creator's UID in setuid processes
461 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
462 secdebug("SSevalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
463 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
466 mechExecOk
= client(pluginIn
, mechanismIn
, arguments
, hints
, context
, &result
);
469 secdebug("SSevalMech", "exception from mech eval or client death");
470 // various server problems, but only if it really failed
471 if (mechExecOk
!= true)
472 result
= kAuthorizationResultUndefined
;
475 secdebug("SSevalMech", "evaluate(plugin: %s, mechanism: %s) %s, result: %lu.", pluginIn
.c_str(), mechanismIn
.c_str(), (mechExecOk
== true) ? "succeeded" : "failed", result
);
479 // internal mechanisms - no glue
480 if (*currentMechanism
== "authinternal")
482 secdebug("SSevalMech", "evaluate authinternal");
483 result
= kAuthorizationResultDeny
;
485 AuthItemSet::iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
486 if (found
== context
.end())
488 string
username(static_cast<const char *>((*found
)->value().data
), (*found
)->value().length
);
489 secdebug("SSevalMech", "found username");
490 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword
) );
491 if (found
== context
.end())
493 string
password(static_cast<const char *>((*found
)->value().data
), (*found
)->value().length
);
494 secdebug("SSevalMech", "found password");
495 Credential
newCredential(username
, password
, true); // create a new shared credential
497 if (newCredential
->isValid())
499 Syslog::info("authinternal authenticated user %s (uid %lu) for right %s.", newCredential
->username().c_str(), newCredential
->uid(), inRight
->name());
500 auditrec
.submit(AUE_ssauthint
, CommonCriteria::errNone
, inRight
->name());
504 // we can't be sure that the user actually exists so inhibit logging of uid
505 Syslog::error("authinternal failed to authenticate user %s for right %s.", newCredential
->username().c_str(), inRight
->name());
507 auditrec
.submit(AUE_ssauthint
, CommonCriteria::errInvalidCredential
, inRight
->name());
510 if (newCredential
->isValid())
512 outCredentials
.clear(); // only keep last one
513 secdebug("SSevalMech", "inserting new credential");
514 outCredentials
.insert(newCredential
);
515 result
= kAuthorizationResultAllow
;
517 result
= kAuthorizationResultDeny
;
521 if (*currentMechanism
== "push_hints_to_context")
523 secdebug("SSevalMech", "evaluate push_hints_to_context");
524 mTries
= 1; // XXX/cs this should be set in authorization config
525 result
= kAuthorizationResultAllow
; // snarfcredential doesn't block evaluation, ever, it may restart
526 // create out context from input hints, no merge
527 // @@@ global copy template not being invoked...
531 if (*currentMechanism
== "switch_to_user")
533 Process
&cltProc
= Server::active().connection().process
;
534 // Authorization preserves creator's UID in setuid processes
535 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
536 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
537 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
540 client
.terminateAgent();
544 result
= kAuthorizationResultAllow
;
548 // we own outHints and outContext
551 case kAuthorizationResultAllow
:
552 secdebug("SSevalMech", "result allow");
555 case kAuthorizationResultDeny
:
556 secdebug("SSevalMech", "result deny");
558 case kAuthorizationResultUndefined
:
559 secdebug("SSevalMech", "result undefined");
560 break; // abort evaluation
561 case kAuthorizationResultUserCanceled
:
562 secdebug("SSevalMech", "result canceled");
563 break; // stop evaluation, return some sideband
565 break; // abort evaluation
569 // End of evaluation, if last step produced meaningful data, incorporate
570 if ((result
== kAuthorizationResultAllow
) ||
571 (result
== kAuthorizationResultUserCanceled
)) // @@@ can only pass back sideband through context
573 secdebug("SSevalMech", "storing new context for authorization");
574 auth
.setInfoSet(context
);
579 case kAuthorizationResultDeny
:
580 return errAuthorizationDenied
;
581 case kAuthorizationResultUserCanceled
:
582 return errAuthorizationCanceled
;
583 case kAuthorizationResultAllow
:
584 return errAuthorizationSuccess
;
586 return errAuthorizationInternal
;
593 RuleImpl::evaluateAuthorization(const AuthItemRef
&inRight
, const Rule
&inRule
,
594 AuthItemSet
&environmentToClient
,
595 AuthorizationFlags flags
, CFAbsoluteTime now
,
596 const CredentialSet
*inCredentials
,
597 CredentialSet
&credentials
, AuthorizationToken
&auth
) const
599 OSStatus status
= errAuthorizationDenied
;
602 evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, usernamehint
);
603 if (usernamehint
.length())
604 environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER
, AuthValueOverlay(usernamehint
)));
606 if ((mType
== kUser
) && (mGroupName
.length()))
607 environmentToClient
.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP
, AuthValueOverlay(mGroupName
)));
610 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
612 for (tries
= 0; tries
< mTries
; tries
++)
614 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
615 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
616 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
617 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
619 status
= evaluateMechanism(inRight
, environmentToClient
, auth
, credentials
);
621 // successfully ran mechanisms to obtain credential
622 if (status
== errAuthorizationSuccess
)
624 // deny is the default
625 status
= errAuthorizationDenied
;
627 // fetch context and construct a credential to be tested
628 AuthItemSet inContext
= auth
.infoSet();
629 CredentialSet newCredentials
= makeCredentials(inContext
);
630 // clear context after extracting credentials
633 for (CredentialSet::const_iterator it
= newCredentials
.begin(); it
!= newCredentials
.end(); ++it
)
635 const Credential
& newCredential
= *it
;
636 CommonCriteria::AuditRecord
auditrec(auth
.creatorAuditToken());
638 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
639 if (newCredential
->isValid())
641 Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), newCredential
->uid(), inRight
->name());
642 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errNone
, inRight
->name());
646 // we can't be sure that the user actually exists so inhibit logging of uid
647 Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth
.creatorUid(), newCredential
->username().c_str(), inRight
->name());
648 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errInvalidCredential
, inRight
->name());
651 if (!newCredential
->isValid())
653 reason
= SecurityAgent::invalidPassphrase
; //invalidPassphrase;
657 // verify that this credential authorizes right
658 status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
660 if (status
== errAuthorizationSuccess
)
662 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
663 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
664 // use valid credential to set context info
665 auth
.setCredentialInfo(newCredential
);
666 secdebug("SSevalMech", "added valid credential for user %s", newCredential
->username().c_str());
667 status
= errAuthorizationSuccess
;
672 reason
= SecurityAgent::userNotInGroup
; //unacceptableUser; // userNotInGroup
673 // don't audit: we denied on the basis of something
674 // other than a bad user or password
678 if (status
== errAuthorizationSuccess
)
682 if ((status
== errAuthorizationCanceled
) ||
683 (status
== errAuthorizationInternal
))
690 // If we fell out of the loop because of too many tries, notify user
693 reason
= SecurityAgent::tooManyTries
;
694 AuthItemRef
retryHint (AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
695 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
696 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
697 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
698 evaluateMechanism(inRight
, environmentToClient
, auth
, credentials
);
702 Process
&cltProc
= Server::active().connection().process
;
703 // Authorization preserves creator's UID in setuid processes
704 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
705 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
706 string agentName
= agentNameForAuth(auth
);
707 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
710 client
.terminateAgent();
718 // create externally verified credentials on the basis of
719 // mechanism-provided information
721 RuleImpl::makeCredentials(const AuthItemSet
&context
) const
723 CredentialSet newCredentials
;
726 AuthItemSet::const_iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
727 if (found
== context
.end())
729 string username
= (**found
).stringValue();
730 secdebug("SSevalMech", "found username");
732 const uid_t
*uid
= NULL
;
733 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("uid") );
734 if (found
!= context
.end())
736 uid
= static_cast<const uid_t
*>((**found
).value().data
);
737 secdebug("SSevalMech", "found uid");
740 const gid_t
*gid
= NULL
;
741 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("gid") );
742 if (found
!= context
.end())
744 gid
= static_cast<const gid_t
*>((**found
).value().data
);
745 secdebug("SSevalMech", "found gid");
748 if (username
.length() && uid
&& gid
)
750 // credential is valid because mechanism says so
751 newCredentials
.insert(Credential(username
, *uid
, *gid
, mShared
));
755 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword
) );
756 if (found
!= context
.end())
758 secdebug("SSevalMech", "found password");
759 string password
= (**found
).stringValue();
760 secdebug("SSevalMech", "falling back on username/password credential if valid");
761 newCredentials
.insert(Credential(username
, password
, mShared
));
766 return newCredentials
;
769 // evaluate whether a good credential of the current session owner would authorize a right
771 RuleImpl::evaluateSessionOwner(const AuthItemRef
&inRight
, const Rule
&inRule
,
772 const AuthItemSet
&environment
,
773 const CFAbsoluteTime now
,
774 const AuthorizationToken
&auth
,
775 string
& usernamehint
) const
777 // username hint is taken from the user who created the authorization, unless it's clearly ineligible
778 OSStatus status
= noErr
;
779 // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
780 // meaning that a process like loginwindow that drops privs later is screwed.
781 uid_t uid
= auth
.creatorUid();
783 Server::active().longTermActivity();
784 struct passwd
*pw
= getpwuid(uid
);
787 // avoid hinting a locked account (ie. root)
788 if ( (pw
->pw_passwd
== NULL
) ||
789 strcmp(pw
->pw_passwd
, "*") ) {
790 // Check if username will authorize the request and set username to
791 // be used as a hint to the user if so
792 status
= evaluateCredentialForRight(inRight
, inRule
, environment
, now
, Credential(pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
, mShared
), true);
794 if (status
== errAuthorizationSuccess
)
795 usernamehint
= pw
->pw_name
;
804 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
805 // return errAuthorizationDenied otherwise.
807 RuleImpl::evaluateCredentialForRight(const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, CFAbsoluteTime now
, const Credential
&credential
, bool ignoreShared
) const
809 assert(mType
== kUser
);
811 // Get the username from the credential
812 const char *user
= credential
->username().c_str();
814 // If the credential is not valid or it's age is more than the allowed maximum age
815 // for a credential, deny.
816 if (!credential
->isValid())
818 secdebug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
->name());
819 return errAuthorizationDenied
;
822 if (now
- credential
->creationTime() > mMaxCredentialAge
)
824 secdebug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
->name());
825 return errAuthorizationDenied
;
828 if (!ignoreShared
&& !mShared
&& credential
->isShared())
830 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
->name());
831 return errAuthorizationDenied
;
834 // A root (uid == 0) user can do anything
835 if (credential
->uid() == 0)
837 secdebug("autheval", "user %s has uid 0, granting right %s", user
, inRight
->name());
838 return errAuthorizationSuccess
;
841 // XXX/cs replace with remembered session-owner once that functionality is added to SecurityServer
845 struct stat console_stat
;
846 if (!lstat("/dev/console", &console_stat
))
848 console_user
= console_stat
.st_uid
;
849 if (credential
->uid() == console_user
)
851 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user
, console_user
, inRight
->name());
852 return errAuthorizationSuccess
;
856 secdebug("autheval", "session-owner check failed.");
859 if (mGroupName
.length())
861 const char *groupname
= mGroupName
.c_str();
862 Server::active().longTermActivity();
863 struct group
*gr
= getgrnam(groupname
);
865 return errAuthorizationDenied
;
867 // Is this the default group of this user?
868 // PR-2875126 <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
869 // When this is fixed this warning should go away.
870 if (credential
->gid() == gr
->gr_gid
)
872 secdebug("autheval", "user %s has group %s(%d) as default group, granting right %s",
873 user
, groupname
, gr
->gr_gid
, inRight
->name());
875 return errAuthorizationSuccess
;
878 for (char **group
= gr
->gr_mem
; *group
; ++group
)
880 if (!strcmp(*group
, user
))
882 secdebug("autheval", "user %s is a member of group %s, granting right %s",
883 user
, groupname
, inRight
->name());
885 return errAuthorizationSuccess
;
889 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
890 user
, groupname
, inRight
->name());
894 return errAuthorizationDenied
;
898 RuleImpl::evaluateUser(const AuthItemRef
&inRight
, const Rule
&inRule
,
899 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
900 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
901 AuthorizationToken
&auth
) const
903 // If we got here, this is a kUser type rule, let's start looking for a
904 // credential that is satisfactory
906 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
907 // created by a proccess running as root to automatically get a right.
908 if (mAllowRoot
&& auth
.creatorUid() == 0)
910 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
912 return errAuthorizationSuccess
;
915 // if this is a "is-admin" rule check that and return
916 // XXX/cs add way to specify is-admin class of rule: if (mNoVerify)
917 if (name() == kAuthorizationRuleIsAdmin
)
920 if (!evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, username
))
921 return errAuthorizationSuccess
;
924 // First -- go though the credentials we either already used or obtained during this authorize operation.
925 for (CredentialSet::const_iterator it
= credentials
.begin(); it
!= credentials
.end(); ++it
)
927 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, true);
928 if (status
!= errAuthorizationDenied
)
930 // add credential to authinfo
931 auth
.setCredentialInfo(*it
);
936 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
939 for (CredentialSet::const_iterator it
= inCredentials
->begin(); it
!= inCredentials
->end(); ++it
)
941 OSStatus status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, *it
, false);
942 if (status
== errAuthorizationSuccess
)
944 // Add the credential we used to the output set.
945 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
946 credentials
.erase(*it
); credentials
.insert(*it
);
947 // add credential to authinfo
948 auth
.setCredentialInfo(*it
);
952 else if (status
!= errAuthorizationDenied
)
957 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
958 // our flags let us do so.
959 if (!(flags
& kAuthorizationFlagExtendRights
))
960 return errAuthorizationDenied
;
962 // authorizations that timeout immediately cannot be preauthorized
963 if ((flags
& kAuthorizationFlagPreAuthorize
) &&
964 (mMaxCredentialAge
== 0.0))
966 inRight
->setFlags(inRight
->flags() | kAuthorizationFlagCanNotPreAuthorize
);
967 return errAuthorizationSuccess
;
970 if (!(flags
& kAuthorizationFlagInteractionAllowed
))
971 return errAuthorizationInteractionNotAllowed
;
973 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
975 // If a different evaluation is prescribed,
976 // we'll run that and validate the credentials from there
977 // we fall back on a default configuration
978 if (mEvalDef
.size() == 0)
979 return evaluateAuthorizationOld(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
981 return evaluateAuthorization(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
984 // XXX/cs insert a mechanism that let's the agent live (keep-alive) only in loginwindow's case
986 RuleImpl::evaluateMechanismOnly(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
993 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
994 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
995 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
997 status
= evaluateMechanism(inRight
, environmentToClient
, auth
, outCredentials
);
1000 while ((status
== errAuthorizationDenied
) // only if we have an expected failure we continue
1001 && ((mTries
== 0) // mTries == 0 means we try forever
1002 || ((mTries
> 0) // mTries > 0 means we try up to mTries times
1003 && (tries
< mTries
))));
1005 if (name() != "system.login.console")
1008 string agentName
= agentNameForAuth(auth
);
1009 Process
&cltProc
= Server::active().connection().process
;
1010 // Authorization preserves creator's UID in setuid processes
1011 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
1012 secdebug("SSevalMech", "terminating agent at request of process %d (UID %d)\n", cltProc
.pid(), cltUid
);
1014 QueryInvokeMechanism
client(cltUid
, auth
, agentName
.c_str());
1018 client
.terminateAgent();
1027 RuleImpl::evaluateRules(const AuthItemRef
&inRight
, const Rule
&inRule
,
1028 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
1029 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
1030 AuthorizationToken
&auth
) const
1032 // line up the rules to try
1033 if (!mRuleDef
.size())
1034 return errAuthorizationSuccess
;
1037 OSStatus status
= errAuthorizationSuccess
;
1038 vector
<Rule
>::const_iterator it
;
1040 for (it
= mRuleDef
.begin();it
!= mRuleDef
.end(); it
++)
1043 if ((mType
== kKofN
) && (count
== mKofN
))
1044 return errAuthorizationSuccess
;
1046 // get a rule and try it
1047 status
= (*it
)->evaluate(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1049 // if status is cancel/internal error abort
1050 if ((status
== errAuthorizationCanceled
) || (status
== errAuthorizationInternal
))
1053 if (status
!= errAuthorizationSuccess
)
1055 // continue if we're only looking for k of n
1065 return status
; // return the last failure
1070 RuleImpl::evaluate(const AuthItemRef
&inRight
, const Rule
&inRule
,
1071 AuthItemSet
&environmentToClient
, AuthorizationFlags flags
,
1072 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
1073 AuthorizationToken
&auth
) const
1078 secdebug("autheval", "rule is always allow");
1079 return errAuthorizationSuccess
;
1081 secdebug("autheval", "rule is always deny");
1082 return errAuthorizationDenied
;
1084 secdebug("autheval", "rule is user");
1085 return evaluateUser(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1086 case kRuleDelegation
:
1087 secdebug("autheval", "rule evaluates rules");
1088 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1090 secdebug("autheval", "rule evaluates k-of-n rules");
1091 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
1092 case kEvaluateMechanisms
:
1093 secdebug("autheval", "rule evaluates mechanisms");
1094 return evaluateMechanismOnly(inRight
, inRule
, environmentToClient
, auth
, credentials
);
1096 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
1103 // This is slated to be removed when the new auth panel is fixed up
1105 RuleImpl::evaluateAuthorizationOld(const AuthItemRef
&inRight
, const Rule
&inRule
,
1106 AuthItemSet
&environmentToClient
,
1107 AuthorizationFlags flags
, CFAbsoluteTime now
,
1108 const CredentialSet
*inCredentials
,
1109 CredentialSet
&credentials
, AuthorizationToken
&auth
) const
1111 Process
&cltProc
= Server::active().connection().process
;
1112 // Authorization preserves creator's UID in setuid processes
1113 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
1114 secdebug("autheval", "Auth query from process %d (UID %d)", cltProc
.pid(), cltUid
);
1115 QueryAuthorizeByGroup
query(cltUid
, auth
);
1117 string usernamehint
;
1119 evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, usernamehint
);
1121 Credential newCredential
;
1122 // @@@ Keep the default reason the same, so the agent only gets userNotInGroup or invalidPassphrase
1123 SecurityAgent::Reason reason
= SecurityAgent::userNotInGroup
;
1125 CommonCriteria::AuditRecord
auditrec(auth
.creatorAuditToken());
1127 // @@@ Hardcoded 3 tries to avoid infinite loops.
1128 for (uint32_t tryCount
= 0; tryCount
< mTries
; ++tryCount
)
1130 // Obtain a new credential. Anything but success is considered an error.
1131 OSStatus status
= obtainCredential(query
, inRight
, environmentToClient
, usernamehint
.c_str(), newCredential
, reason
);
1135 // Now we have successfully obtained a credential we need to make sure it authorizes the requested right
1136 if (!newCredential
->isValid())
1138 reason
= SecurityAgent::invalidPassphrase
;
1139 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errInvalidCredential
, inRight
->name());
1142 status
= evaluateCredentialForRight(inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
1143 if (status
== errAuthorizationSuccess
)
1145 // Add the new credential we obtained to the output set.
1146 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
1147 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
1150 // add credential to authinfo
1151 auth
.setCredentialInfo(newCredential
);
1153 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errNone
, inRight
->name());
1154 return errAuthorizationSuccess
;
1156 else if (status
!= errAuthorizationDenied
)
1158 if (status
== errAuthorizationCanceled
)
1159 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errUserCanceled
, inRight
->name());
1160 // else don't audit--error not due to bad
1161 // username or password
1165 reason
= SecurityAgent::userNotInGroup
;
1167 query
.cancel(SecurityAgent::tooManyTries
);
1169 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errTooManyTries
, inRight
->name());
1170 return errAuthorizationDenied
;
1174 RuleImpl::obtainCredential(QueryAuthorizeByGroup
&query
, const AuthItemRef
&inRight
,
1175 AuthItemSet
&environmentToClient
, const char *usernameHint
, Credential
&outCredential
, SecurityAgent::Reason reason
) const
1177 char nameBuffer
[SecurityAgent::maxUsernameLength
];
1178 char passphraseBuffer
[SecurityAgent::maxPassphraseLength
];
1179 OSStatus status
= errAuthorizationDenied
;
1182 if (query(mGroupName
.c_str(), usernameHint
, nameBuffer
, passphraseBuffer
, reason
))
1184 } catch (const CssmCommonError
&err
) {
1185 status
= err
.osStatus();
1187 status
= errAuthorizationInternal
;
1189 if (status
== CSSM_ERRCODE_USER_CANCELED
)
1191 secdebug("auth", "canceled obtaining credential for user in group %s", mGroupName
.c_str());
1192 return errAuthorizationCanceled
;
1194 if (status
== CSSM_ERRCODE_NO_USER_INTERACTION
)
1196 secdebug("auth", "user interaction not possible obtaining credential for user in group %s", mGroupName
.c_str());
1197 return errAuthorizationInteractionNotAllowed
;
1200 if (status
!= noErr
)
1202 secdebug("auth", "failed obtaining credential for user in group %s", mGroupName
.c_str());
1206 secdebug("auth", "obtained credential for user %s", nameBuffer
);
1207 string
username(nameBuffer
);
1208 string
password(passphraseBuffer
);
1209 outCredential
= Credential(username
, password
, mShared
);
1210 return errAuthorizationSuccess
;
1214 Rule::Rule() : RefPointer
<RuleImpl
>(new RuleImpl()) {}
1215 Rule::Rule(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : RefPointer
<RuleImpl
>(new RuleImpl(inRightName
, cfRight
, cfRules
)) {}
1218 } // end namespace Authorization