]> git.saurik.com Git - apple/securityd.git/blob - src/AuthorizationRule.cpp
securityd-36489.tar.gz
[apple/securityd.git] / src / AuthorizationRule.cpp
1 /*
2 * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * AuthorizationRule.cpp
24 * Security
25 *
26 */
27
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"
37 #include "server.h"
38 #include "process.h"
39 #include "agentquery.h"
40 #include "AuthorizationMechEval.h"
41
42 #include <pwd.h>
43 #include <grp.h>
44 #include <unistd.h>
45 #include <membership.h>
46
47 extern "C" {
48 #include <membershipPriv.h>
49 }
50
51 //
52 // Rule class
53 //
54 namespace Authorization {
55
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
65
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);
73
74
75 string
76 RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, char *defaultValue = "")
77 {
78 CFTypeRef value = CFDictionaryGetValue(config, key);
79 if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
80 {
81 CFStringRef stringValue = reinterpret_cast<CFStringRef>(value);
82 char buffer[512];
83 const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
84 if (ptr == NULL)
85 {
86 if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
87 ptr = buffer;
88 else
89 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
90 }
91
92 return string(ptr);
93 }
94 else
95 if (!required)
96 return string(defaultValue);
97 else
98 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
99 }
100
101 double
102 RuleImpl::Attribute::getDouble(CFDictionaryRef config, CFStringRef key, bool required = false, double defaultValue = 0.0)
103 {
104 double doubleValue = 0;
105
106 CFTypeRef value = CFDictionaryGetValue(config, key);
107 if (value && (CFGetTypeID(value) == CFNumberGetTypeID()))
108 {
109 CFNumberGetValue(reinterpret_cast<CFNumberRef>(value), kCFNumberDoubleType, &doubleValue);
110 }
111 else
112 if (!required)
113 return defaultValue;
114 else
115 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
116
117 return doubleValue;
118 }
119
120 bool
121 RuleImpl::Attribute::getBool(CFDictionaryRef config, CFStringRef key, bool required = false, bool defaultValue = false)
122 {
123 bool boolValue = false;
124 CFTypeRef value = CFDictionaryGetValue(config, key);
125
126 if (value && (CFGetTypeID(value) == CFBooleanGetTypeID()))
127 {
128 boolValue = CFBooleanGetValue(reinterpret_cast<CFBooleanRef>(value));
129 }
130 else
131 if (!required)
132 return defaultValue;
133 else
134 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
135
136 return boolValue;
137 }
138
139 vector<string>
140 RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool required = false)
141 {
142 vector<string> valueArray;
143
144 CFTypeRef value = CFDictionaryGetValue(config, key);
145 if (value && (CFGetTypeID(value) == CFArrayGetTypeID()))
146 {
147 CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
148
149 for (int index=0; index < CFArrayGetCount(evalArray); index++)
150 {
151 CFTypeRef arrayValue = CFArrayGetValueAtIndex(evalArray, index);
152 if (arrayValue && (CFGetTypeID(arrayValue) == CFStringGetTypeID()))
153 {
154 CFStringRef stringValue = reinterpret_cast<CFStringRef>(arrayValue);
155 char buffer[512];
156 const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
157 if (ptr == NULL)
158 {
159 if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
160 ptr = buffer;
161 else
162 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
163 }
164 valueArray.push_back(string(ptr));
165 }
166 }
167 }
168 else
169 if (required)
170 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
171
172 return valueArray;
173 }
174
175
176 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string,string> &localizedPrompts)
177 {
178 CFIndex numberOfPrompts = 0;
179 CFDictionaryRef promptsDict;
180 if (CFDictionaryContainsKey(config, kPromptID))
181 {
182 promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, kPromptID));
183 if (promptsDict && (CFGetTypeID(promptsDict) == CFDictionaryGetTypeID()))
184 numberOfPrompts = CFDictionaryGetCount(promptsDict);
185 }
186 if (numberOfPrompts == 0)
187 return false;
188
189 const void *keys[numberOfPrompts+1];
190 const void *values[numberOfPrompts+1];
191 CFDictionaryGetKeysAndValues(promptsDict, &keys[0], &values[0]);
192
193 while (numberOfPrompts-- > 0)
194 {
195 CFStringRef keyRef = reinterpret_cast<CFStringRef>(keys[numberOfPrompts]);
196 CFStringRef valueRef = reinterpret_cast<CFStringRef>(values[numberOfPrompts]);
197 if (!keyRef || (CFGetTypeID(keyRef) != CFStringGetTypeID()))
198 continue;
199 if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID()))
200 continue;
201 string key = cfString(keyRef);
202 string value = cfString(valueRef);
203 localizedPrompts[kAuthorizationRuleParameterDescription+key] = value;
204 }
205
206 return true;
207 }
208
209
210 // default rule
211 RuleImpl::RuleImpl() :
212 mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0), mAuthenticateUser(true)
213 {
214 // XXX/cs read default descriptions from somewhere
215 // @@@ Default rule is shared admin group with 5 minute timeout
216 }
217
218 // return rule built from rule definition; throw if invalid.
219 RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName)
220 {
221 // @@@ make sure cfRight is non mutable and never used that way
222
223 if (CFGetTypeID(cfRight) != CFDictionaryGetTypeID())
224 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
225
226 mTries = 0;
227
228 string classTag = Attribute::getString(cfRight, kRuleClassID, false, "");
229
230 if (classTag.length())
231 {
232 if (classTag == kAuthorizationRuleClassAllow)
233 {
234 secdebug("authrule", "%s : rule allow", inRightName.c_str());
235 mType = kAllow;
236 }
237 else if (classTag == kAuthorizationRuleClassDeny)
238 {
239 secdebug("authrule", "%s : rule deny", inRightName.c_str());
240 mType = kDeny;
241 }
242 else if (classTag == kAuthorizationRuleClassUser)
243 {
244 mType = kUser;
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*/)
254 {
255 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, CFSTR("authenticate")));
256 if (cfRuleDef && CFGetTypeID(cfRuleDef) == CFDictionaryGetTypeID())
257 mEvalDef = Attribute::getVector(cfRuleDef, kMechanismsID);
258 }
259 mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
260 mAuthenticateUser = Attribute::getBool(cfRight, kRuleAuthenticateUserID, false, true);
261
262 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
263 inRightName.c_str(),
264 mGroupName.c_str(), mMaxCredentialAge, mShared ? " shared" : "",
265 mAllowRoot ? " allow-root" : "");
266
267 }
268 else if (classTag == kAuthorizationRuleClassMechanisms)
269 {
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);
276 }
277 else if (classTag == kAuthorizationRightRule)
278 {
279 assert(cfRules); // rules can't delegate to other rules
280 secdebug("authrule", "%s : rule delegate rule", inRightName.c_str());
281 mType = kRuleDelegation;
282
283 // string or
284 string ruleDefString = Attribute::getString(cfRight, kRuleDelegateID, false, "");
285 if (ruleDefString.length())
286 {
287 CFStringRef ruleDefRef = makeCFString(ruleDefString);
288 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleDefRef));
289 if (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));
294 }
295 else // array
296 {
297 vector<string> ruleDef = Attribute::getVector(cfRight, kRuleDelegateID, true);
298 for (vector<string>::const_iterator it = ruleDef.begin(); it != ruleDef.end(); it++)
299 {
300 CFStringRef ruleNameRef = makeCFString(*it);
301 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
302 if (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));
307 }
308 }
309
310 mKofN = int(Attribute::getDouble(cfRight, kKofNID, false, 0.0));
311 if (mKofN)
312 mType = kKofN;
313
314 }
315 else
316 {
317 secdebug("authrule", "%s : rule class unknown %s.", inRightName.c_str(), classTag.c_str());
318 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
319 }
320 }
321 else
322 {
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));
332 if (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));
337 }
338
339 Attribute::getLocalizedPrompts(cfRight, mLocalizedPrompts);
340 }
341
342 /*
343 RuleImpl::~Rule()
344 {
345 }
346 */
347
348 void
349 RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule, AuthItemSet &environmentToClient, AuthorizationToken &auth) const
350 {
351 string authorizeString(inRight->name());
352 environmentToClient.erase(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT));
353 environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT, AuthValueOverlay(authorizeString)));
354
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)));
358
359 Process &thisProcess = Server::process();
360 string bundlePath;
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());
370
371 map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
372
373 if (defaultPrompts.empty())
374 defaultPrompts = localizedPrompts();
375
376 if (!defaultPrompts.empty())
377 {
378 map<string,string>::const_iterator it;
379 for (it = defaultPrompts.begin(); it != defaultPrompts.end(); it++)
380 {
381 const string &key = it->first;
382 const string &value = it->second;
383 environmentToClient.insert(AuthItemRef(key.c_str(), AuthValueOverlay(value)));
384 }
385 }
386
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)));
391 }
392
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
396 OSStatus
397 RuleImpl::evaluateAuthentication(const AuthItemRef &inRight, const Rule &inRule,AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth) const
398 {
399 OSStatus status = errAuthorizationDenied;
400
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())));
407 }
408
409 if ((mType == kUser) && (mGroupName.length()))
410 environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
411
412 uint32 tries;
413 SecurityAgent::Reason reason = SecurityAgent::noReason;
414
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);
419
420 AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
421
422 for (tries = 0; tries < mTries; tries++)
423 {
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
428
429 status = eval.run(AuthValueVector(), environmentToClient, auth);
430
431 if ((status == errAuthorizationSuccess) ||
432 (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
433 {
434 secdebug("AuthEvalMech", "storing new context for authorization");
435 auth.setInfoSet(eval.context());
436 }
437
438 // successfully ran mechanisms to obtain credential
439 if (status == errAuthorizationSuccess)
440 {
441 // deny is the default
442 status = errAuthorizationDenied;
443
444 CredentialSet newCredentials = makeCredentials(auth);
445 // clear context after extracting credentials
446 auth.scrubInfoSet();
447
448 CommonCriteria::AuditRecord auditrec(auth.creatorAuditToken());
449 for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
450 {
451 const Credential& newCredential = *it;
452
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());
457 } else {
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());
461 }
462
463 if (!newCredential->isValid())
464 {
465 reason = SecurityAgent::invalidPassphrase; //invalidPassphrase;
466 continue;
467 }
468
469 // verify that this credential authorizes right
470 status = evaluateUserCredentialForRight(auth, inRight, inRule, environmentToClient, now, newCredential, true);
471
472 if (status == errAuthorizationSuccess)
473 {
474 if (auth.operatesAsLeastPrivileged()) {
475 Credential rightCredential(inRight->name(), mShared);
476 credentials.erase(rightCredential); credentials.insert(rightCredential);
477 if (mShared)
478 credentials.insert(Credential(inRight->name(), false));
479 } else {
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
483 if (mShared)
484 credentials.insert(Credential(newCredential->uid(), newCredential->name(), newCredential->realname(), false));
485 }
486
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;
492 break;
493 }
494 else
495 reason = SecurityAgent::userNotInGroup; //unacceptableUser; // userNotInGroup
496 }
497
498 if (status == errAuthorizationSuccess)
499 break;
500 }
501 else
502 if ((status == errAuthorizationCanceled) ||
503 (status == errAuthorizationInternal))
504 {
505 auth.scrubInfoSet();
506 break;
507 }
508 else // last mechanism is now authentication - fail
509 if (status == errAuthorizationDenied)
510 reason = SecurityAgent::invalidPassphrase;
511 }
512
513 // If we fell out of the loop because of too many tries, notify user
514 if (tries == mTries)
515 {
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?
523 auth.scrubInfoSet();
524
525 CommonCriteria::AuditRecord auditrec(auth.creatorAuditToken());
526 auditrec.submit(AUE_ssauthorize, CommonCriteria::errTooManyTries, inRight->name());
527 }
528
529 return status;
530 }
531
532 // create externally verified credentials on the basis of
533 // mechanism-provided information
534 CredentialSet
535 RuleImpl::makeCredentials(const AuthorizationToken &auth) const
536 {
537 // fetch context and construct a credential to be tested
538 const AuthItemSet &context = const_cast<AuthorizationToken &>(auth).infoSet();
539 CredentialSet newCredentials;
540
541 do {
542 AuthItemSet::const_iterator found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername) );
543 if (found == context.end())
544 break;
545 string username = (**found).stringValue();
546 secdebug("AuthEvalMech", "found username");
547
548 const uid_t *uid = NULL;
549 found = find_if(context.begin(), context.end(), FindAuthItemByRightName("uid") );
550 if (found != context.end())
551 {
552 uid = static_cast<const uid_t *>((**found).value().data);
553 secdebug("AuthEvalMech", "found uid");
554 }
555
556 if (username.length() && uid)
557 {
558 // credential is valid because mechanism says so
559 newCredentials.insert(Credential(*uid, username, "", mShared));
560 }
561 } while(0);
562
563 return newCredentials;
564 }
565
566 // evaluate whether a good credential of the current session owner would authorize a right
567 OSStatus
568 RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, const CFAbsoluteTime now, const AuthorizationToken &auth, Credential &credential) const
569 {
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.
573
574 uid_t uid;
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*/);
581 } else {
582 uid = auth.creatorUid();
583 Server::active().longTermActivity();
584 struct passwd *pw = getpwuid(uid);
585 if (pw != NULL) {
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*/);
593 } //fi
594 endpwent();
595 }
596 }
597 OSStatus status = evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, sessionCredential, true);
598 if (errAuthorizationSuccess == status)
599 credential = sessionCredential;
600
601 return status;
602 }
603
604
605 OSStatus
606 RuleImpl::evaluateCredentialForRight(const AuthorizationToken &auth, const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
607 {
608 if (auth.operatesAsLeastPrivileged()) {
609 if (credential->isRight() && credential->isValid() && (inRight->name() == credential->name()))
610 return errAuthorizationSuccess;
611 else
612 return errAuthorizationDenied;
613 } else
614 return evaluateUserCredentialForRight(auth, inRight, inRule, environment, now, credential, false);
615 }
616
617 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
618 // return errAuthorizationDenied otherwise.
619 OSStatus
620 RuleImpl::evaluateUserCredentialForRight(const AuthorizationToken &auth, const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
621 {
622 assert(mType == kUser);
623
624 // Get the username from the credential
625 const char *user = credential->name().c_str();
626
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())
630 {
631 secdebug("autheval", "credential for user %s is invalid, denying right %s", user, inRight->name());
632 return errAuthorizationDenied;
633 }
634
635 if (now - credential->creationTime() > mMaxCredentialAge)
636 {
637 secdebug("autheval", "credential for user %s has expired, denying right %s", user, inRight->name());
638 return errAuthorizationDenied;
639 }
640
641 if (!ignoreShared && !mShared && credential->isShared())
642 {
643 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight->name());
644 return errAuthorizationDenied;
645 }
646
647 // A root (uid == 0) user can do anything
648 if (credential->uid() == 0)
649 {
650 secdebug("autheval", "user %s has uid 0, granting right %s", user, inRight->name());
651 return errAuthorizationSuccess;
652 }
653
654 if (mSessionOwner)
655 {
656 Session &session = auth.session();
657 if (session.haveOriginatorUid())
658 {
659 uid_t console_user = session.originatorUid();
660
661 if (credential->uid() == console_user)
662 {
663 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user, console_user, inRight->name());
664 return errAuthorizationSuccess;
665 }
666 }
667 else
668 secdebug("autheval", "session-owner check failed.");
669 }
670
671 if (mGroupName.length())
672 {
673 const char *groupname = mGroupName.c_str();
674 Server::active().longTermActivity();
675
676 if (!groupname)
677 return errAuthorizationDenied;
678
679 do
680 {
681 uuid_t group_uuid, user_uuid;
682 int is_member;
683
684 if (mbr_group_name_to_uuid(groupname, group_uuid))
685 break;
686
687 if (mbr_uid_to_uuid(credential->uid(), user_uuid))
688 break;
689
690 if (mbr_check_membership(user_uuid, group_uuid, &is_member))
691 break;
692
693 if (is_member)
694 {
695 secdebug("autheval", "user %s is a member of group %s, granting right %s",
696 user, groupname, inRight->name());
697 return errAuthorizationSuccess;
698 }
699
700 }
701 while (0);
702
703 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
704 user, groupname, inRight->name());
705 }
706
707 return errAuthorizationDenied;
708 }
709
710
711
712 OSStatus
713 RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth) const
714 {
715 // If we got here, this is a kUser type rule, let's start looking for a
716 // credential that is satisfactory
717
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)
721 {
722 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
723 inRight->name());
724 return errAuthorizationSuccess;
725 }
726
727 // if we're not supposed to authenticate evaluate the session-owner against the group
728 if (!mAuthenticateUser)
729 {
730 Credential hintCredential;
731 OSStatus status = evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, hintCredential);
732
733 if (!status)
734 return errAuthorizationSuccess;
735
736 return errAuthorizationDenied;
737 }
738
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)
741 {
742 // Passed in user credentials are allowed for least privileged mode
743 if (auth.operatesAsLeastPrivileged() && !(*it)->isRight() && (*it)->isValid())
744 {
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);
749 if (mShared)
750 credentials.insert(Credential(inRight->name(), false));
751 return status;
752 }
753 }
754
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);
757
758 if (status != errAuthorizationDenied) {
759 // add credential to authinfo
760 auth.setCredentialInfo(*it);
761 return status;
762 }
763
764 }
765
766 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
767 if (inCredentials)
768 {
769 for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
770 {
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);
773
774 if (status == errAuthorizationSuccess)
775 {
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);
781
782 return status;
783 }
784 else if (status != errAuthorizationDenied)
785 return status;
786 }
787 }
788
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;
792
793 // authorizations that timeout immediately cannot be preauthorized
794 if ((flags & kAuthorizationFlagPreAuthorize) &&
795 (mMaxCredentialAge == 0.0))
796 {
797 inRight->setFlags(inRight->flags() | kAuthorizationFlagCanNotPreAuthorize);
798 return errAuthorizationSuccess;
799 }
800
801 if (!(flags & kAuthorizationFlagInteractionAllowed))
802 return errAuthorizationInteractionNotAllowed;
803
804 setAgentHints(inRight, inRule, environmentToClient, auth);
805
806 return evaluateAuthentication(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
807 }
808
809 OSStatus
810 RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationToken &auth, CredentialSet &outCredentials) const
811 {
812 uint32 tries = 0;
813 OSStatus status;
814
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);
819
820 {
821 AgentMechanismEvaluator eval(cltUid, auth.session(), mEvalDef);
822
823 do
824 {
825 setAgentHints(inRight, inRule, environmentToClient, auth);
826 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
827 environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
828
829 status = eval.run(AuthValueVector(), environmentToClient, auth);
830
831 if ((status == errAuthorizationSuccess) ||
832 (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
833 {
834 secdebug("AuthEvalMech", "storing new context for authorization");
835 auth.setInfoSet(eval.context());
836 if (status == errAuthorizationSuccess)
837 {
838 if (auth.operatesAsLeastPrivileged())
839 outCredentials.insert(Credential(inRight->name(), mShared));
840 else
841 outCredentials = makeCredentials(auth);
842 }
843 }
844
845 tries++;
846 }
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))));
851 }
852
853 // HACK kill all hosts to free pages for low memory systems
854 if (name() == "system.login.done")
855 {
856 QueryInvokeMechanism query(securityAgent, auth.session());
857 query.terminateAgent();
858 QueryInvokeMechanism query2(privilegedAuthHost, auth.session());
859 query2.terminateAgent();
860 }
861
862 return status;
863 }
864
865 OSStatus
866 RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth) const
867 {
868 // line up the rules to try
869 if (!mRuleDef.size())
870 return errAuthorizationSuccess;
871
872 uint32_t count = 0;
873 OSStatus status = errAuthorizationSuccess;
874 vector<Rule>::const_iterator it;
875
876 for (it = mRuleDef.begin();it != mRuleDef.end(); it++)
877 {
878 // are we at k yet?
879 if ((mType == kKofN) && (count == mKofN))
880 return errAuthorizationSuccess;
881
882 // get a rule and try it
883 status = (*it)->evaluate(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
884
885 // if status is cancel/internal error abort
886 if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
887 return status;
888
889 if (status != errAuthorizationSuccess)
890 {
891 // continue if we're only looking for k of n
892 if (mType == kKofN)
893 continue;
894
895 break;
896 }
897 else
898 count++;
899 }
900
901 return status; // return the last failure
902 }
903
904
905 OSStatus
906 RuleImpl::evaluate(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationFlags flags, CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials, AuthorizationToken &auth) const
907 {
908 switch (mType)
909 {
910 case kAllow:
911 secdebug("autheval", "rule is always allow");
912 return errAuthorizationSuccess;
913 case kDeny:
914 secdebug("autheval", "rule is always deny");
915 return errAuthorizationDenied;
916 case kUser:
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);
922 case kKofN:
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);
928 default:
929 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
930 }
931 }
932
933 Rule::Rule() : RefPointer<RuleImpl>(new RuleImpl()) {}
934 Rule::Rule(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : RefPointer<RuleImpl>(new RuleImpl(inRightName, cfRight, cfRules)) {}
935
936
937
938 } // end namespace Authorization