2 * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 * AuthorizationRule.cpp
28 #include "AuthorizationRule.h"
29 #include <Security/AuthorizationTags.h>
30 #include <Security/AuthorizationTagsPriv.h>
31 #include <Security/AuthorizationDB.h>
32 #include <Security/AuthorizationPriv.h>
33 #include <security_utilities/logging.h>
34 #include <security_utilities/ccaudit.h>
35 #include <bsm/audit_uevents.h>
36 #include "authority.h"
39 #include "agentquery.h"
40 #include "AuthorizationMechEval.h"
45 #include <membership.h>
48 #include <membershipPriv.h>
54 namespace Authorization
{
56 CFStringRef
RuleImpl::kUserGroupID
= CFSTR(kAuthorizationRuleParameterGroup
);
57 CFStringRef
RuleImpl::kTimeoutID
= CFSTR(kAuthorizationRuleParameterCredentialTimeout
);
58 CFStringRef
RuleImpl::kSharedID
= CFSTR(kAuthorizationRuleParameterCredentialShared
);
59 CFStringRef
RuleImpl::kAllowRootID
= CFSTR(kAuthorizationRuleParameterAllowRoot
);
60 CFStringRef
RuleImpl::kMechanismsID
= CFSTR(kAuthorizationRuleParameterMechanisms
);
61 CFStringRef
RuleImpl::kSessionOwnerID
= CFSTR(kAuthorizationRuleParameterCredentialSessionOwner
);
62 CFStringRef
RuleImpl::kKofNID
= CFSTR(kAuthorizationRuleParameterKofN
);
63 CFStringRef
RuleImpl::kPromptID
= CFSTR(kAuthorizationRuleParameterDefaultPrompt
);
64 CFStringRef
RuleImpl::kTriesID
= CFSTR("tries"); // XXX/cs move to AuthorizationTagsPriv.h
66 CFStringRef
RuleImpl::kRuleClassID
= CFSTR(kAuthorizationRuleClass
);
67 CFStringRef
RuleImpl::kRuleAllowID
= CFSTR(kAuthorizationRuleClassAllow
);
68 CFStringRef
RuleImpl::kRuleDenyID
= CFSTR(kAuthorizationRuleClassDeny
);
69 CFStringRef
RuleImpl::kRuleUserID
= CFSTR(kAuthorizationRuleClassUser
);
70 CFStringRef
RuleImpl::kRuleDelegateID
= CFSTR(kAuthorizationRightRule
);
71 CFStringRef
RuleImpl::kRuleMechanismsID
= CFSTR(kAuthorizationRuleClassMechanisms
);
72 CFStringRef
RuleImpl::kRuleAuthenticateUserID
= CFSTR(kAuthorizationRuleParameterAuthenticateUser
);
76 RuleImpl::Attribute::getString(CFDictionaryRef config
, CFStringRef key
, bool required
= false, char *defaultValue
= "")
78 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
79 if (value
&& (CFGetTypeID(value
) == CFStringGetTypeID()))
81 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(value
);
83 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
86 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
89 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
96 return string(defaultValue
);
98 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
102 RuleImpl::Attribute::getDouble(CFDictionaryRef config
, CFStringRef key
, bool required
= false, double defaultValue
= 0.0)
104 double doubleValue
= 0;
106 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
107 if (value
&& (CFGetTypeID(value
) == CFNumberGetTypeID()))
109 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(value
), kCFNumberDoubleType
, &doubleValue
);
115 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
121 RuleImpl::Attribute::getBool(CFDictionaryRef config
, CFStringRef key
, bool required
= false, bool defaultValue
= false)
123 bool boolValue
= false;
124 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
126 if (value
&& (CFGetTypeID(value
) == CFBooleanGetTypeID()))
128 boolValue
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(value
));
134 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
140 RuleImpl::Attribute::getVector(CFDictionaryRef config
, CFStringRef key
, bool required
= false)
142 vector
<string
> valueArray
;
144 CFTypeRef value
= CFDictionaryGetValue(config
, key
);
145 if (value
&& (CFGetTypeID(value
) == CFArrayGetTypeID()))
147 CFArrayRef evalArray
= reinterpret_cast<CFArrayRef
>(value
);
149 for (int index
=0; index
< CFArrayGetCount(evalArray
); index
++)
151 CFTypeRef arrayValue
= CFArrayGetValueAtIndex(evalArray
, index
);
152 if (arrayValue
&& (CFGetTypeID(arrayValue
) == CFStringGetTypeID()))
154 CFStringRef stringValue
= reinterpret_cast<CFStringRef
>(arrayValue
);
156 const char *ptr
= CFStringGetCStringPtr(stringValue
, kCFStringEncodingUTF8
);
159 if (CFStringGetCString(stringValue
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
))
162 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
164 valueArray
.push_back(string(ptr
));
170 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
176 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config
, map
<string
,string
> &localizedPrompts
)
178 CFIndex numberOfPrompts
= 0;
179 CFDictionaryRef promptsDict
;
180 if (CFDictionaryContainsKey(config
, kPromptID
))
182 promptsDict
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(config
, kPromptID
));
183 if (promptsDict
&& (CFGetTypeID(promptsDict
) == CFDictionaryGetTypeID()))
184 numberOfPrompts
= CFDictionaryGetCount(promptsDict
);
186 if (numberOfPrompts
== 0)
189 const void *keys
[numberOfPrompts
+1];
190 const void *values
[numberOfPrompts
+1];
191 CFDictionaryGetKeysAndValues(promptsDict
, &keys
[0], &values
[0]);
193 while (numberOfPrompts
-- > 0)
195 CFStringRef keyRef
= reinterpret_cast<CFStringRef
>(keys
[numberOfPrompts
]);
196 CFStringRef valueRef
= reinterpret_cast<CFStringRef
>(values
[numberOfPrompts
]);
197 if (!keyRef
|| (CFGetTypeID(keyRef
) != CFStringGetTypeID()))
199 if (!valueRef
|| (CFGetTypeID(valueRef
) != CFStringGetTypeID()))
201 string key
= cfString(keyRef
);
202 string value
= cfString(valueRef
);
203 localizedPrompts
[kAuthorizationRuleParameterDescription
+key
] = value
;
211 RuleImpl::RuleImpl() :
212 mType(kUser
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true)
214 // XXX/cs read default descriptions from somewhere
215 // @@@ Default rule is shared admin group with 5 minute timeout
218 // return rule built from rule definition; throw if invalid.
219 RuleImpl::RuleImpl(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : mRightName(inRightName
)
221 // @@@ make sure cfRight is non mutable and never used that way
223 if (CFGetTypeID(cfRight
) != CFDictionaryGetTypeID())
224 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
228 string classTag
= Attribute::getString(cfRight
, kRuleClassID
, false, "");
230 if (classTag
.length())
232 if (classTag
== kAuthorizationRuleClassAllow
)
234 secdebug("authrule", "%s : rule allow", inRightName
.c_str());
237 else if (classTag
== kAuthorizationRuleClassDeny
)
239 secdebug("authrule", "%s : rule deny", inRightName
.c_str());
242 else if (classTag
== kAuthorizationRuleClassUser
)
245 mGroupName
= Attribute::getString(cfRight
, kUserGroupID
);
246 // grab other user-in-group attributes
247 mMaxCredentialAge
= Attribute::getDouble(cfRight
, kTimeoutID
, false, DBL_MAX
);
248 mShared
= Attribute::getBool(cfRight
, kSharedID
);
249 mAllowRoot
= Attribute::getBool(cfRight
, kAllowRootID
);
250 mSessionOwner
= Attribute::getBool(cfRight
, kSessionOwnerID
);
251 // authorization tags can have eval now too
252 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
);
253 if (mEvalDef
.size() == 0 && cfRules
/*only rights default see appserver-admin*/)
255 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, CFSTR("authenticate")));
256 if (cfRuleDef
&& CFGetTypeID(cfRuleDef
) == CFDictionaryGetTypeID())
257 mEvalDef
= Attribute::getVector(cfRuleDef
, kMechanismsID
);
259 mTries
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
260 mAuthenticateUser
= Attribute::getBool(cfRight
, kRuleAuthenticateUserID
, false, true);
262 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
264 mGroupName
.c_str(), mMaxCredentialAge
, mShared
? " shared" : "",
265 mAllowRoot
? " allow-root" : "");
268 else if (classTag
== kAuthorizationRuleClassMechanisms
)
270 secdebug("authrule", "%s : rule evaluate mechanisms", inRightName
.c_str());
271 mType
= kEvaluateMechanisms
;
272 // mechanisms to evaluate
273 mEvalDef
= Attribute::getVector(cfRight
, kMechanismsID
, true);
274 mTries
= int(Attribute::getDouble(cfRight
, kTriesID
, false, 0.0)); // "forever"
275 mShared
= Attribute::getBool(cfRight
, kSharedID
, false, true);
277 else if (classTag
== kAuthorizationRightRule
)
279 assert(cfRules
); // rules can't delegate to other rules
280 secdebug("authrule", "%s : rule delegate rule", inRightName
.c_str());
281 mType
= kRuleDelegation
;
284 string ruleDefString
= Attribute::getString(cfRight
, kRuleDelegateID
, false, "");
285 if (ruleDefString
.length())
287 CFStringRef ruleDefRef
= makeCFString(ruleDefString
);
288 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleDefRef
));
290 CFRelease(ruleDefRef
);
291 if (!cfRuleDef
|| CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID())
292 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
293 mRuleDef
.push_back(Rule(ruleDefString
, cfRuleDef
, cfRules
));
297 vector
<string
> ruleDef
= Attribute::getVector(cfRight
, kRuleDelegateID
, true);
298 for (vector
<string
>::const_iterator it
= ruleDef
.begin(); it
!= ruleDef
.end(); it
++)
300 CFStringRef ruleNameRef
= makeCFString(*it
);
301 CFDictionaryRef cfRuleDef
= reinterpret_cast<CFDictionaryRef
>(CFDictionaryGetValue(cfRules
, ruleNameRef
));
303 CFRelease(ruleNameRef
);
304 if (!cfRuleDef
|| (CFGetTypeID(cfRuleDef
) != CFDictionaryGetTypeID()))
305 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
306 mRuleDef
.push_back(Rule(*it
, cfRuleDef
, cfRules
));
310 mKofN
= int(Attribute::getDouble(cfRight
, kKofNID
, false, 0.0));
317 secdebug("authrule", "%s : rule class unknown %s.", inRightName
.c_str(), classTag
.c_str());
318 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
323 // no class tag means, this is the abbreviated specification from the API
324 // it _must_ have a definition for "rule" which will be used as a delegate
325 // it may have a comment (not extracted here)
326 // 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
, cfRules
));
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
.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
));
353 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT
, AuthValueOverlay(authorizeString
)));
355 pid_t creatorPid
= auth
.creatorPid();
356 environmentToClient
.erase(AuthItemRef(AGENT_HINT_CREATOR_PID
));
357 environmentToClient
.insert(AuthItemRef(AGENT_HINT_CREATOR_PID
, AuthValueOverlay(sizeof(pid_t
), &creatorPid
)));
359 Process
&thisProcess
= Server::process();
361 if (SecStaticCodeRef clientCode
= auth
.creatorCode())
362 bundlePath
= codePath(clientCode
);
363 AuthItemSet processHints
= SecurityAgent::Client::clientHints(
364 SecurityAgent::bundle
, bundlePath
, thisProcess
.pid(), thisProcess
.uid());
365 environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_TYPE
));
366 environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_PATH
));
367 environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_PID
));
368 environmentToClient
.erase(AuthItemRef(AGENT_HINT_CLIENT_UID
));
369 environmentToClient
.insert(processHints
.begin(), processHints
.end());
371 map
<string
,string
> defaultPrompts
= inTopLevelRule
->localizedPrompts();
373 if (defaultPrompts
.empty())
374 defaultPrompts
= localizedPrompts();
376 if (!defaultPrompts
.empty())
378 map
<string
,string
>::const_iterator it
;
379 for (it
= defaultPrompts
.begin(); it
!= defaultPrompts
.end(); it
++)
381 const string
&key
= it
->first
;
382 const string
&value
= it
->second
;
383 environmentToClient
.insert(AuthItemRef(key
.c_str(), AuthValueOverlay(value
)));
387 // add rulename as a hint
388 string ruleName
= name();
389 environmentToClient
.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
));
390 environmentToClient
.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE
, AuthValueOverlay(ruleName
)));
393 // If a different evaluation for getting a credential is prescribed,
394 // we'll run that and validate the credentials from there.
395 // we fall back on a default configuration from the authenticate rule
397 RuleImpl::evaluateAuthentication(const AuthItemRef
&inRight
, const Rule
&inRule
,AuthItemSet
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
, AuthorizationToken
&auth
) const
399 OSStatus status
= errAuthorizationDenied
;
401 Credential hintCredential
;
402 if (errAuthorizationSuccess
== evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, hintCredential
)) {
403 if (hintCredential
->name().length())
404 environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER
, AuthValueOverlay(hintCredential
->name())));
405 if (hintCredential
->realname().length())
406 environmentToClient
.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER_LONG
, AuthValueOverlay(hintCredential
->realname())));
409 if ((mType
== kUser
) && (mGroupName
.length()))
410 environmentToClient
.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP
, AuthValueOverlay(mGroupName
)));
413 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
415 Process
&cltProc
= Server::process();
416 // Authorization preserves creator's UID in setuid processes
417 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
418 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
420 AgentMechanismEvaluator
eval(cltUid
, auth
.session(), mEvalDef
);
422 for (tries
= 0; tries
< mTries
; tries
++)
424 AuthItemRef
retryHint(AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
425 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
426 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
427 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
429 status
= eval
.run(AuthValueVector(), environmentToClient
, auth
);
431 if ((status
== errAuthorizationSuccess
) ||
432 (status
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context
434 secdebug("AuthEvalMech", "storing new context for authorization");
435 auth
.setInfoSet(eval
.context());
438 // successfully ran mechanisms to obtain credential
439 if (status
== errAuthorizationSuccess
)
441 // deny is the default
442 status
= errAuthorizationDenied
;
444 CredentialSet newCredentials
= makeCredentials(auth
);
445 // clear context after extracting credentials
448 CommonCriteria::AuditRecord
auditrec(auth
.creatorAuditToken());
449 for (CredentialSet::const_iterator it
= newCredentials
.begin(); it
!= newCredentials
.end(); ++it
)
451 const Credential
& newCredential
= *it
;
453 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
454 if (newCredential
->isValid()) {
455 Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth
.creatorUid(), newCredential
->name().c_str(), newCredential
->uid(), inRight
->name());
456 auditrec
.submit(AUE_ssauthint
, CommonCriteria::errNone
, inRight
->name());
458 // we can't be sure that the user actually exists so inhibit logging of uid
459 Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth
.creatorUid(), newCredential
->name().c_str(), inRight
->name());
460 auditrec
.submit(AUE_ssauthint
, CommonCriteria::errInvalidCredential
, inRight
->name());
463 if (!newCredential
->isValid())
465 reason
= SecurityAgent::invalidPassphrase
; //invalidPassphrase;
469 // verify that this credential authorizes right
470 status
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, newCredential
, true);
472 if (status
== errAuthorizationSuccess
)
474 if (auth
.operatesAsLeastPrivileged()) {
475 Credential
rightCredential(inRight
->name(), mShared
);
476 credentials
.erase(rightCredential
); credentials
.insert(rightCredential
);
478 credentials
.insert(Credential(inRight
->name(), false));
480 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
481 credentials
.erase(newCredential
); credentials
.insert(newCredential
);
482 // just got a new credential - if it's shared also add a non-shared one that to stick in the authorizationref local cache
484 credentials
.insert(Credential(newCredential
->uid(), newCredential
->name(), newCredential
->realname(), false));
487 // use valid credential to set context info
488 // XXX/cs keeping this for now, such that the uid is passed back
489 auth
.setCredentialInfo(newCredential
);
490 secdebug("SSevalMech", "added valid credential for user %s", newCredential
->name().c_str());
491 status
= errAuthorizationSuccess
;
495 reason
= SecurityAgent::userNotInGroup
; //unacceptableUser; // userNotInGroup
498 if (status
== errAuthorizationSuccess
)
502 if ((status
== errAuthorizationCanceled
) ||
503 (status
== errAuthorizationInternal
))
508 else // last mechanism is now authentication - fail
509 if (status
== errAuthorizationDenied
)
510 reason
= SecurityAgent::invalidPassphrase
;
513 // If we fell out of the loop because of too many tries, notify user
516 reason
= SecurityAgent::tooManyTries
;
517 AuthItemRef
retryHint (AGENT_HINT_RETRY_REASON
, AuthValueOverlay(sizeof(reason
), &reason
));
518 environmentToClient
.erase(retryHint
); environmentToClient
.insert(retryHint
); // replace
519 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
520 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
521 eval
.run(AuthValueVector(), environmentToClient
, auth
);
522 // XXX/cs is this still necessary?
525 CommonCriteria::AuditRecord
auditrec(auth
.creatorAuditToken());
526 auditrec
.submit(AUE_ssauthorize
, CommonCriteria::errTooManyTries
, inRight
->name());
532 // create externally verified credentials on the basis of
533 // mechanism-provided information
535 RuleImpl::makeCredentials(const AuthorizationToken
&auth
) const
537 // fetch context and construct a credential to be tested
538 const AuthItemSet
&context
= const_cast<AuthorizationToken
&>(auth
).infoSet();
539 CredentialSet newCredentials
;
542 AuthItemSet::const_iterator found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername
) );
543 if (found
== context
.end())
545 string username
= (**found
).stringValue();
546 secdebug("AuthEvalMech", "found username");
548 const uid_t
*uid
= NULL
;
549 found
= find_if(context
.begin(), context
.end(), FindAuthItemByRightName("uid") );
550 if (found
!= context
.end())
552 uid
= static_cast<const uid_t
*>((**found
).value().data
);
553 secdebug("AuthEvalMech", "found uid");
556 if (username
.length() && uid
)
558 // credential is valid because mechanism says so
559 newCredentials
.insert(Credential(*uid
, username
, "", mShared
));
563 return newCredentials
;
566 // evaluate whether a good credential of the current session owner would authorize a right
568 RuleImpl::evaluateSessionOwner(const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, const CFAbsoluteTime now
, const AuthorizationToken
&auth
, Credential
&credential
) const
570 // username hint is taken from the user who created the authorization, unless it's clearly ineligible
571 // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
572 // meaning that a process like loginwindow that drops privs later is screwed.
575 Session
&session
= auth
.session();
576 Credential sessionCredential
;
577 if (session
.haveOriginatorUid()) {
578 // preflight session credential as if it were a fresh copy
579 const Credential
&cred
= session
.originatorCredential();
580 sessionCredential
= Credential(cred
->uid(), cred
->name(), cred
->realname(), mShared
/*ignored*/);
582 uid
= auth
.creatorUid();
583 Server::active().longTermActivity();
584 struct passwd
*pw
= getpwuid(uid
);
586 // avoid hinting a locked account
587 if ( (pw
->pw_passwd
== NULL
) ||
588 strcmp(pw
->pw_passwd
, "*") ) {
589 // Check if username will authorize the request and set username to
590 // be used as a hint to the user if so
591 secdebug("AuthEvalMech", "preflight credential from current user, result follows:");
592 sessionCredential
= Credential(pw
->pw_uid
, pw
->pw_name
, pw
->pw_gecos
, mShared
/*ignored*/);
597 OSStatus status
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environment
, now
, sessionCredential
, true);
598 if (errAuthorizationSuccess
== status
)
599 credential
= sessionCredential
;
606 RuleImpl::evaluateCredentialForRight(const AuthorizationToken
&auth
, const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, CFAbsoluteTime now
, const Credential
&credential
, bool ignoreShared
) const
608 if (auth
.operatesAsLeastPrivileged()) {
609 if (credential
->isRight() && credential
->isValid() && (inRight
->name() == credential
->name()))
610 return errAuthorizationSuccess
;
612 return errAuthorizationDenied
;
614 return evaluateUserCredentialForRight(auth
, inRight
, inRule
, environment
, now
, credential
, false);
617 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
618 // return errAuthorizationDenied otherwise.
620 RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken
&auth
, const AuthItemRef
&inRight
, const Rule
&inRule
, const AuthItemSet
&environment
, CFAbsoluteTime now
, const Credential
&credential
, bool ignoreShared
) const
622 assert(mType
== kUser
);
624 // Get the username from the credential
625 const char *user
= credential
->name().c_str();
627 // If the credential is not valid or it's age is more than the allowed maximum age
628 // for a credential, deny.
629 if (!credential
->isValid())
631 secdebug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
->name());
632 return errAuthorizationDenied
;
635 if (now
- credential
->creationTime() > mMaxCredentialAge
)
637 secdebug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
->name());
638 return errAuthorizationDenied
;
641 if (!ignoreShared
&& !mShared
&& credential
->isShared())
643 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
->name());
644 return errAuthorizationDenied
;
647 // A root (uid == 0) user can do anything
648 if (credential
->uid() == 0)
650 secdebug("autheval", "user %s has uid 0, granting right %s", user
, inRight
->name());
651 return errAuthorizationSuccess
;
656 Session
&session
= auth
.session();
657 if (session
.haveOriginatorUid())
659 uid_t console_user
= session
.originatorUid();
661 if (credential
->uid() == console_user
)
663 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user
, console_user
, inRight
->name());
664 return errAuthorizationSuccess
;
668 secdebug("autheval", "session-owner check failed.");
671 if (mGroupName
.length())
673 const char *groupname
= mGroupName
.c_str();
674 Server::active().longTermActivity();
677 return errAuthorizationDenied
;
681 uuid_t group_uuid
, user_uuid
;
684 if (mbr_group_name_to_uuid(groupname
, group_uuid
))
687 if (mbr_uid_to_uuid(credential
->uid(), user_uuid
))
690 if (mbr_check_membership(user_uuid
, group_uuid
, &is_member
))
695 secdebug("autheval", "user %s is a member of group %s, granting right %s",
696 user
, groupname
, inRight
->name());
697 return errAuthorizationSuccess
;
703 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
704 user
, groupname
, inRight
->name());
707 return errAuthorizationDenied
;
713 RuleImpl::evaluateUser(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
, AuthorizationToken
&auth
) const
715 // If we got here, this is a kUser type rule, let's start looking for a
716 // credential that is satisfactory
718 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
719 // created by a proccess running as root to automatically get a right.
720 if (mAllowRoot
&& auth
.creatorUid() == 0)
722 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
724 return errAuthorizationSuccess
;
727 // if we're not supposed to authenticate evaluate the session-owner against the group
728 if (!mAuthenticateUser
)
730 Credential hintCredential
;
731 OSStatus status
= evaluateSessionOwner(inRight
, inRule
, environmentToClient
, now
, auth
, hintCredential
);
734 return errAuthorizationSuccess
;
736 return errAuthorizationDenied
;
739 // First -- go though the credentials we either already used or obtained during this authorize operation.
740 for (CredentialSet::const_iterator it
= credentials
.begin(); it
!= credentials
.end(); ++it
)
742 // Passed in user credentials are allowed for least privileged mode
743 if (auth
.operatesAsLeastPrivileged() && !(*it
)->isRight() && (*it
)->isValid())
745 OSStatus status
= evaluateUserCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false);
746 if (errAuthorizationSuccess
== status
) {
747 Credential
rightCredential(inRight
->name(), mShared
);
748 credentials
.erase(rightCredential
); credentials
.insert(rightCredential
);
750 credentials
.insert(Credential(inRight
->name(), false));
755 // if this is least privileged, this will function differently: match credential to requested right
756 OSStatus status
= evaluateCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false);
758 if (status
!= errAuthorizationDenied
) {
759 // add credential to authinfo
760 auth
.setCredentialInfo(*it
);
766 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
769 for (CredentialSet::const_iterator it
= inCredentials
->begin(); it
!= inCredentials
->end(); ++it
)
771 // if this is least privileged, this will function differently: match credential to requested right
772 OSStatus status
= evaluateCredentialForRight(auth
, inRight
, inRule
, environmentToClient
, now
, *it
, false);
774 if (status
== errAuthorizationSuccess
)
776 // Add the credential we used to the output set.
777 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
778 credentials
.erase(*it
); credentials
.insert(*it
);
779 // add credential to authinfo
780 auth
.setCredentialInfo(*it
);
784 else if (status
!= errAuthorizationDenied
)
789 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if our flags let us do so.
790 if (!(flags
& kAuthorizationFlagExtendRights
))
791 return errAuthorizationDenied
;
793 // authorizations that timeout immediately cannot be preauthorized
794 if ((flags
& kAuthorizationFlagPreAuthorize
) &&
795 (mMaxCredentialAge
== 0.0))
797 inRight
->setFlags(inRight
->flags() | kAuthorizationFlagCanNotPreAuthorize
);
798 return errAuthorizationSuccess
;
801 if (!(flags
& kAuthorizationFlagInteractionAllowed
))
802 return errAuthorizationInteractionNotAllowed
;
804 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
806 return evaluateAuthentication(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
810 RuleImpl::evaluateMechanismOnly(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationToken
&auth
, CredentialSet
&outCredentials
) const
815 Process
&cltProc
= Server::process();
816 // Authorization preserves creator's UID in setuid processes
817 uid_t cltUid
= (cltProc
.uid() != 0) ? cltProc
.uid() : auth
.creatorUid();
818 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc
.pid(), cltUid
);
821 AgentMechanismEvaluator
eval(cltUid
, auth
.session(), mEvalDef
);
825 setAgentHints(inRight
, inRule
, environmentToClient
, auth
);
826 AuthItemRef
triesHint(AGENT_HINT_TRIES
, AuthValueOverlay(sizeof(tries
), &tries
));
827 environmentToClient
.erase(triesHint
); environmentToClient
.insert(triesHint
); // replace
829 status
= eval
.run(AuthValueVector(), environmentToClient
, auth
);
831 if ((status
== errAuthorizationSuccess
) ||
832 (status
== errAuthorizationCanceled
)) // @@@ can only pass back sideband through context
834 secdebug("AuthEvalMech", "storing new context for authorization");
835 auth
.setInfoSet(eval
.context());
836 if (status
== errAuthorizationSuccess
)
838 if (auth
.operatesAsLeastPrivileged())
839 outCredentials
.insert(Credential(inRight
->name(), mShared
));
841 outCredentials
= makeCredentials(auth
);
847 while ((status
== errAuthorizationDenied
) // only if we have an expected failure we continue
848 && ((mTries
== 0) // mTries == 0 means we try forever
849 || ((mTries
> 0) // mTries > 0 means we try up to mTries times
850 && (tries
< mTries
))));
853 // HACK kill all hosts to free pages for low memory systems
854 if (name() == "system.login.done")
856 QueryInvokeMechanism
query(securityAgent
, auth
.session());
857 query
.terminateAgent();
858 QueryInvokeMechanism
query2(privilegedAuthHost
, auth
.session());
859 query2
.terminateAgent();
866 RuleImpl::evaluateRules(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
, AuthorizationToken
&auth
) const
868 // line up the rules to try
869 if (!mRuleDef
.size())
870 return errAuthorizationSuccess
;
873 OSStatus status
= errAuthorizationSuccess
;
874 vector
<Rule
>::const_iterator it
;
876 for (it
= mRuleDef
.begin();it
!= mRuleDef
.end(); it
++)
879 if ((mType
== kKofN
) && (count
== mKofN
))
880 return errAuthorizationSuccess
;
882 // get a rule and try it
883 status
= (*it
)->evaluate(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
885 // if status is cancel/internal error abort
886 if ((status
== errAuthorizationCanceled
) || (status
== errAuthorizationInternal
))
889 if (status
!= errAuthorizationSuccess
)
891 // continue if we're only looking for k of n
901 return status
; // return the last failure
906 RuleImpl::evaluate(const AuthItemRef
&inRight
, const Rule
&inRule
, AuthItemSet
&environmentToClient
, AuthorizationFlags flags
, CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
, AuthorizationToken
&auth
) const
911 secdebug("autheval", "rule is always allow");
912 return errAuthorizationSuccess
;
914 secdebug("autheval", "rule is always deny");
915 return errAuthorizationDenied
;
917 secdebug("autheval", "rule is user");
918 return evaluateUser(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
919 case kRuleDelegation
:
920 secdebug("autheval", "rule evaluates rules");
921 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
923 secdebug("autheval", "rule evaluates k-of-n rules");
924 return evaluateRules(inRight
, inRule
, environmentToClient
, flags
, now
, inCredentials
, credentials
, auth
);
925 case kEvaluateMechanisms
:
926 secdebug("autheval", "rule evaluates mechanisms");
927 return evaluateMechanismOnly(inRight
, inRule
, environmentToClient
, auth
, credentials
);
929 MacOSError::throwMe(errAuthorizationInternal
); // XXX/cs invalid rule
933 Rule::Rule() : RefPointer
<RuleImpl
>(new RuleImpl()) {}
934 Rule::Rule(const string
&inRightName
, CFDictionaryRef cfRight
, CFDictionaryRef cfRules
) : RefPointer
<RuleImpl
>(new RuleImpl(inRightName
, cfRight
, cfRules
)) {}
938 } // end namespace Authorization