]> git.saurik.com Git - apple/securityd.git/blob - src/AuthorizationRule.cpp
847ae602d8a316222d2a91ebb4b289b75bd7dbf8
[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 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 *
25 * AuthorizationRule.cpp
26 * Security
27 *
28 */
29
30 #include "AuthorizationRule.h"
31 #include <Security/AuthorizationTags.h>
32 #include <Security/AuthorizationTagsPriv.h>
33 #include <Security/AuthorizationDB.h>
34 #include <Security/AuthorizationPriv.h>
35 #include "authority.h"
36 #include "server.h"
37 #include "process.h"
38 #include "agentquery.h"
39 #include "AuthorizationMechEval.h"
40
41 #include <pwd.h>
42 #include <grp.h>
43 #include <unistd.h>
44
45
46 //
47 // Rule class
48 //
49 namespace Authorization {
50
51 CFStringRef RuleImpl::kUserGroupID = CFSTR(kAuthorizationRuleParameterGroup);
52 CFStringRef RuleImpl::kTimeoutID = CFSTR(kAuthorizationRuleParameterCredentialTimeout);
53 CFStringRef RuleImpl::kSharedID = CFSTR(kAuthorizationRuleParameterCredentialShared);
54 CFStringRef RuleImpl::kAllowRootID = CFSTR(kAuthorizationRuleParameterAllowRoot);
55 CFStringRef RuleImpl::kMechanismsID = CFSTR(kAuthorizationRuleParameterMechanisms);
56 CFStringRef RuleImpl::kSessionOwnerID = CFSTR(kAuthorizationRuleParameterCredentialSessionOwner);
57 CFStringRef RuleImpl::kKofNID = CFSTR(kAuthorizationRuleParameterKofN);
58 CFStringRef RuleImpl::kPromptID = CFSTR(kAuthorizationRuleParameterDefaultPrompt);
59 CFStringRef RuleImpl::kTriesID = CFSTR("tries"); // XXX/cs move to AuthorizationTagsPriv.h
60
61 CFStringRef RuleImpl::kRuleClassID = CFSTR(kAuthorizationRuleClass);
62 CFStringRef RuleImpl::kRuleAllowID = CFSTR(kAuthorizationRuleClassAllow);
63 CFStringRef RuleImpl::kRuleDenyID = CFSTR(kAuthorizationRuleClassDeny);
64 CFStringRef RuleImpl::kRuleUserID = CFSTR(kAuthorizationRuleClassUser);
65 CFStringRef RuleImpl::kRuleDelegateID = CFSTR(kAuthorizationRightRule);
66 CFStringRef RuleImpl::kRuleMechanismsID = CFSTR(kAuthorizationRuleClassMechanisms);
67
68 string
69 RuleImpl::Attribute::getString(CFDictionaryRef config, CFStringRef key, bool required = false, char *defaultValue = NULL)
70 {
71 CFTypeRef value = CFDictionaryGetValue(config, key);
72 if (value && (CFGetTypeID(value) == CFStringGetTypeID()))
73 {
74 CFStringRef stringValue = reinterpret_cast<CFStringRef>(value);
75 char buffer[512];
76 const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
77 if (ptr == NULL)
78 {
79 if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
80 ptr = buffer;
81 else
82 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
83 }
84
85 return string(ptr);
86 }
87 else
88 if (!required)
89 return string(defaultValue);
90 else
91 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
92 }
93
94 double
95 RuleImpl::Attribute::getDouble(CFDictionaryRef config, CFStringRef key, bool required = false, double defaultValue = 0.0)
96 {
97 double doubleValue = 0;
98
99 CFTypeRef value = CFDictionaryGetValue(config, key);
100 if (value && (CFGetTypeID(value) == CFNumberGetTypeID()))
101 {
102 CFNumberGetValue(reinterpret_cast<CFNumberRef>(value), kCFNumberDoubleType, &doubleValue);
103 }
104 else
105 if (!required)
106 return defaultValue;
107 else
108 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
109
110 return doubleValue;
111 }
112
113 bool
114 RuleImpl::Attribute::getBool(CFDictionaryRef config, CFStringRef key, bool required = false, bool defaultValue = false)
115 {
116 bool boolValue = false;
117 CFTypeRef value = CFDictionaryGetValue(config, key);
118
119 if (value && (CFGetTypeID(value) == CFBooleanGetTypeID()))
120 {
121 boolValue = CFBooleanGetValue(reinterpret_cast<CFBooleanRef>(value));
122 }
123 else
124 if (!required)
125 return defaultValue;
126 else
127 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
128
129 return boolValue;
130 }
131
132 // add reference to string that we're modifying
133 void
134 RuleImpl::Attribute::setString(CFMutableDictionaryRef config, CFStringRef key, string &value)
135 {
136 CFStringRef cfstringValue = CFStringCreateWithCString(NULL /*allocator*/, value.c_str(), kCFStringEncodingUTF8);
137
138 if (cfstringValue)
139 {
140 CFDictionarySetValue(config, key, cfstringValue);
141 CFRelease(cfstringValue);
142 }
143 else
144 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid attribute
145 }
146
147 void
148 RuleImpl::Attribute::setDouble(CFMutableDictionaryRef config, CFStringRef key, double value)
149 {
150 CFNumberRef doubleValue = CFNumberCreate(NULL /*allocator*/, kCFNumberDoubleType, doubleValue);
151
152 if (doubleValue)
153 {
154 CFDictionarySetValue(config, key, doubleValue);
155 CFRelease(doubleValue);
156 }
157 else
158 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid attribute
159 }
160
161 void
162 RuleImpl::Attribute::setBool(CFMutableDictionaryRef config, CFStringRef key, bool value)
163 {
164 if (value)
165 CFDictionarySetValue(config, key, kCFBooleanTrue);
166 else
167 CFDictionarySetValue(config, key, kCFBooleanFalse);
168 }
169
170 vector<string>
171 RuleImpl::Attribute::getVector(CFDictionaryRef config, CFStringRef key, bool required = false)
172 {
173 vector<string> valueArray;
174
175 CFTypeRef value = CFDictionaryGetValue(config, key);
176 if (value && (CFGetTypeID(value) == CFArrayGetTypeID()))
177 {
178 CFArrayRef evalArray = reinterpret_cast<CFArrayRef>(value);
179
180 for (int index=0; index < CFArrayGetCount(evalArray); index++)
181 {
182 CFTypeRef arrayValue = CFArrayGetValueAtIndex(evalArray, index);
183 if (arrayValue && (CFGetTypeID(arrayValue) == CFStringGetTypeID()))
184 {
185 CFStringRef stringValue = reinterpret_cast<CFStringRef>(arrayValue);
186 char buffer[512];
187 const char *ptr = CFStringGetCStringPtr(stringValue, kCFStringEncodingUTF8);
188 if (ptr == NULL)
189 {
190 if (CFStringGetCString(stringValue, buffer, sizeof(buffer), kCFStringEncodingUTF8))
191 ptr = buffer;
192 else
193 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
194 }
195 valueArray.push_back(string(ptr));
196 }
197 }
198 }
199 else
200 if (required)
201 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
202
203 return valueArray;
204 }
205
206
207 bool RuleImpl::Attribute::getLocalizedPrompts(CFDictionaryRef config, map<string,string> &localizedPrompts)
208 {
209 CFIndex numberOfPrompts = 0;
210 CFDictionaryRef promptsDict;
211 if (CFDictionaryContainsKey(config, kPromptID))
212 {
213 promptsDict = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(config, kPromptID));
214 if (promptsDict && (CFGetTypeID(promptsDict) == CFDictionaryGetTypeID()))
215 numberOfPrompts = CFDictionaryGetCount(promptsDict);
216 }
217 if (numberOfPrompts == 0)
218 return false;
219
220 const void *keys[numberOfPrompts+1];
221 const void *values[numberOfPrompts+1];
222 CFDictionaryGetKeysAndValues(promptsDict, &keys[0], &values[0]);
223
224 while (numberOfPrompts-- > 0)
225 {
226 CFStringRef keyRef = reinterpret_cast<CFStringRef>(keys[numberOfPrompts]);
227 CFStringRef valueRef = reinterpret_cast<CFStringRef>(values[numberOfPrompts]);
228 if (!keyRef || (CFGetTypeID(keyRef) != CFStringGetTypeID()))
229 continue;
230 if (!valueRef || (CFGetTypeID(valueRef) != CFStringGetTypeID()))
231 continue;
232 string key = cfString(keyRef);
233 string value = cfString(valueRef);
234 localizedPrompts[kAuthorizationRuleParameterDescription+key] = value;
235 }
236
237 return true;
238 }
239
240
241 // default rule
242 RuleImpl::RuleImpl() :
243 mType(kUser), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false), mSessionOwner(false), mTries(0)
244 {
245 // XXX/cs read default descriptions from somewhere
246 // @@@ Default rule is shared admin group with 5 minute timeout
247 }
248
249 // return rule built from rule definition; throw if invalid.
250 RuleImpl::RuleImpl(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : mRightName(inRightName)
251 {
252 // @@@ make sure cfRight is non mutable and never used that way
253
254 if (CFGetTypeID(cfRight) != CFDictionaryGetTypeID())
255 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
256
257 mTries = 0;
258
259 string classTag = Attribute::getString(cfRight, kRuleClassID, false, "");
260
261 if (classTag.length())
262 {
263 if (classTag == kAuthorizationRuleClassAllow)
264 {
265 secdebug("authrule", "%s : rule allow", inRightName.c_str());
266 mType = kAllow;
267 }
268 else if (classTag == kAuthorizationRuleClassDeny)
269 {
270 secdebug("authrule", "%s : rule deny", inRightName.c_str());
271 mType = kDeny;
272 }
273 else if (classTag == kAuthorizationRuleClassUser)
274 {
275 mType = kUser;
276 mGroupName = Attribute::getString(cfRight, kUserGroupID);
277 // grab other user-in-group attributes
278 mMaxCredentialAge = Attribute::getDouble(cfRight, kTimeoutID, false, DBL_MAX);
279 mShared = Attribute::getBool(cfRight, kSharedID);
280 mAllowRoot = Attribute::getBool(cfRight, kAllowRootID);
281 mSessionOwner = Attribute::getBool(cfRight, kSessionOwnerID);
282 // authorization tags can have eval now too
283 mEvalDef = Attribute::getVector(cfRight, kMechanismsID);
284 mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 3.0)); // XXX/cs double(kAuthorizationMaxTries)
285
286 secdebug("authrule", "%s : rule user in group \"%s\" timeout %g%s%s",
287 inRightName.c_str(),
288 mGroupName.c_str(), mMaxCredentialAge, mShared ? " shared" : "",
289 mAllowRoot ? " allow-root" : "");
290
291 }
292 else if (classTag == kAuthorizationRuleClassMechanisms)
293 {
294 secdebug("authrule", "%s : rule evaluate mechanisms", inRightName.c_str());
295 mType = kEvaluateMechanisms;
296 // mechanisms to evaluate
297 mEvalDef = Attribute::getVector(cfRight, kMechanismsID, true);
298 mTries = int(Attribute::getDouble(cfRight, kTriesID, false, 0.0)); // "forever"
299 }
300 else if (classTag == kAuthorizationRightRule)
301 {
302 assert(cfRules); // this had better not be a rule
303 secdebug("authrule", "%s : rule delegate rule", inRightName.c_str());
304 mType = kRuleDelegation;
305
306 // string or
307 string ruleDefString = Attribute::getString(cfRight, kRuleDelegateID, false, "");
308 if (ruleDefString.length())
309 {
310 CFStringRef ruleDefRef = makeCFString(ruleDefString);
311 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleDefRef));
312 if (ruleDefRef)
313 CFRelease(ruleDefRef);
314 if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
315 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
316 mRuleDef.push_back(Rule(ruleDefString, cfRuleDef, NULL));
317 }
318 else // array
319 {
320 vector<string> ruleDef = Attribute::getVector(cfRight, kRuleDelegateID, true);
321 for (vector<string>::const_iterator it = ruleDef.begin(); it != ruleDef.end(); it++)
322 {
323 CFStringRef ruleNameRef = makeCFString(*it);
324 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
325 if (ruleNameRef)
326 CFRelease(ruleNameRef);
327 if (!cfRuleDef || (CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID()))
328 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
329 mRuleDef.push_back(Rule(*it, cfRuleDef, NULL));
330 }
331 }
332
333 mKofN = int(Attribute::getDouble(cfRight, kKofNID, false, 0.0));
334 if (mKofN)
335 mType = kKofN;
336
337 }
338 else
339 {
340 secdebug("authrule", "%s : rule class unknown %s.", inRightName.c_str(), classTag.c_str());
341 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
342 }
343 }
344 else
345 {
346 // no class tag means, this is the abbreviated specification from the API
347 // it _must_ have a definition for "rule" which will be used as a delegate
348 // it may have a comment (not extracted here)
349 // it may have a default prompt, or a whole dictionary of languages (not extracted here)
350 assert(cfRules);
351 mType = kRuleDelegation;
352 string ruleName = Attribute::getString(cfRight, kRuleDelegateID, true);
353 secdebug("authrule", "%s : rule delegate rule (1): %s", inRightName.c_str(), ruleName.c_str());
354 CFStringRef ruleNameRef = makeCFString(ruleName);
355 CFDictionaryRef cfRuleDef = reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(cfRules, ruleNameRef));
356 if (ruleNameRef)
357 CFRelease(ruleNameRef);
358 if (!cfRuleDef || CFGetTypeID(cfRuleDef) != CFDictionaryGetTypeID())
359 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
360 mRuleDef.push_back(Rule(ruleName, cfRuleDef, NULL));
361 }
362
363 Attribute::getLocalizedPrompts(cfRight, mLocalizedPrompts);
364 }
365
366 /*
367 RuleImpl::~Rule()
368 {
369 }
370 */
371
372 void
373 RuleImpl::setAgentHints(const AuthItemRef &inRight, const Rule &inTopLevelRule, AuthItemSet &environmentToClient, AuthorizationToken &auth) const
374 {
375 string authorizeString(inRight->name());
376 environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RIGHT, AuthValueOverlay(authorizeString)));
377
378 // XXX/cs pid/uid/client should only be added when we're ready to call the agent
379 pid_t cPid = Server::process().pid();
380 environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_PID, AuthValueOverlay(sizeof(pid_t), &cPid)));
381
382 uid_t cUid = auth.creatorUid();
383 environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_UID, AuthValueOverlay(sizeof(uid_t), &cUid)));
384
385 pid_t creatorPid = auth.creatorPid();
386 environmentToClient.insert(AuthItemRef(AGENT_HINT_CREATOR_PID, AuthValueOverlay(sizeof(pid_t), &creatorPid)));
387
388 {
389 CodeSigning::OSXCode *osxcode = auth.creatorCode();
390 if (!osxcode)
391 MacOSError::throwMe(errAuthorizationDenied);
392
393 string encodedBundle = osxcode->encode();
394 char bundleType = (encodedBundle.c_str())[0]; // yay, no accessor
395 string bundlePath = osxcode->canonicalPath();
396
397 environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_TYPE, AuthValueOverlay(sizeof(bundleType), &bundleType)));
398 environmentToClient.insert(AuthItemRef(AGENT_HINT_CLIENT_PATH, AuthValueOverlay(bundlePath)));
399 }
400
401 map<string,string> defaultPrompts = inTopLevelRule->localizedPrompts();
402
403 if (defaultPrompts.empty())
404 defaultPrompts = localizedPrompts();
405
406 if (!defaultPrompts.empty())
407 {
408 map<string,string>::const_iterator it;
409 for (it = defaultPrompts.begin(); it != defaultPrompts.end(); it++)
410 {
411 const string &key = it->first;
412 const string &value = it->second;
413 environmentToClient.insert(AuthItemRef(key.c_str(), AuthValueOverlay(value)));
414 }
415 }
416
417 // add rulename as a hint
418 string ruleName = name();
419 environmentToClient.insert(AuthItemRef(AGENT_HINT_AUTHORIZE_RULE, AuthValueOverlay(ruleName)));
420 }
421
422 string
423 RuleImpl::agentNameForAuth(const AuthorizationToken &auth) const
424 {
425 uint8_t hash[20];
426 AuthorizationBlob authBlob = auth.handle();
427 CssmData hashedData = CssmData::wrap(&hash, sizeof(hash));
428 CssmData data = CssmData::wrap(&authBlob, sizeof(authBlob));
429 CssmClient::Digest ctx(Server::csp(), CSSM_ALGID_SHA1);
430 try {
431 ctx.digest(data, hashedData);
432 }
433 catch (CssmError &e)
434 {
435 secdebug("auth", "digesting authref failed (%lu)", e.osStatus());
436 return string("SecurityAgentMechanism");
437 }
438
439 uint8_t *point = static_cast<uint8_t*>(hashedData.data());
440 for (uint8_t i=0; i < hashedData.length(); point++, i++)
441 {
442 uint8 value = (*point % 62) + '0';
443 if (value > '9') value += 7;
444 if (value > 'Z') value += 6;
445 *point = value;
446 }
447 return string(static_cast<char *>(hashedData.data()), hashedData.length());
448 }
449
450 OSStatus
451 RuleImpl::evaluateAuthorization(const AuthItemRef &inRight, const Rule &inRule,
452 AuthItemSet &environmentToClient,
453 AuthorizationFlags flags, CFAbsoluteTime now,
454 const CredentialSet *inCredentials,
455 CredentialSet &credentials, AuthorizationToken &auth) const
456 {
457 OSStatus status = errAuthorizationDenied;
458
459 string usernamehint;
460 evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, usernamehint);
461 if (usernamehint.length())
462 environmentToClient.insert(AuthItemRef(AGENT_HINT_SUGGESTED_USER, AuthValueOverlay(usernamehint)));
463
464 if ((mType == kUser) && (mGroupName.length()))
465 environmentToClient.insert(AuthItemRef(AGENT_HINT_REQUIRE_USER_IN_GROUP, AuthValueOverlay(mGroupName)));
466
467 uint32 tries;
468 SecurityAgent::Reason reason = SecurityAgent::noReason;
469
470 Process &cltProc = Server::process();
471 // Authorization preserves creator's UID in setuid processes
472 uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
473 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
474
475 AgentMechanismEvaluator eval(cltUid, cltProc.session(), mEvalDef);
476
477 for (tries = 0; tries < mTries; tries++)
478 {
479 AuthItemRef retryHint(AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
480 environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); // replace
481 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
482 environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
483
484 status = eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
485
486 if ((status == errAuthorizationSuccess) ||
487 (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
488 {
489 secdebug("AuthEvalMech", "storing new context for authorization");
490 auth.setInfoSet(eval.context());
491 }
492
493 // successfully ran mechanisms to obtain credential
494 if (status == errAuthorizationSuccess)
495 {
496 // deny is the default
497 status = errAuthorizationDenied;
498
499 // fetch context and construct a credential to be tested
500 AuthItemSet inContext = auth.infoSet();
501 CredentialSet newCredentials = makeCredentials(inContext);
502 // clear context after extracting credentials
503 auth.clearInfoSet();
504
505 for (CredentialSet::const_iterator it = newCredentials.begin(); it != newCredentials.end(); ++it)
506 {
507 const Credential& newCredential = *it;
508
509 // @@@ we log the uid a process was running under when it created the authref, which is misleading in the case of loginwindow
510 if (newCredential->isValid())
511 Syslog::info("uid %lu succeeded authenticating as user %s (uid %lu) for right %s.", auth.creatorUid(), newCredential->username().c_str(), newCredential->uid(), inRight->name());
512 else
513 // we can't be sure that the user actually exists so inhibit logging of uid
514 Syslog::error("uid %lu failed to authenticate as user %s for right %s.", auth.creatorUid(), newCredential->username().c_str(), inRight->name());
515
516 if (!newCredential->isValid())
517 {
518 reason = SecurityAgent::invalidPassphrase; //invalidPassphrase;
519 continue;
520 }
521
522 // verify that this credential authorizes right
523 status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, newCredential, true);
524
525 if (status == errAuthorizationSuccess)
526 {
527 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
528 credentials.erase(newCredential); credentials.insert(newCredential);
529 // use valid credential to set context info
530 auth.setCredentialInfo(newCredential);
531 secdebug("SSevalMech", "added valid credential for user %s", newCredential->username().c_str());
532 status = errAuthorizationSuccess;
533 break;
534 }
535 else
536 reason = SecurityAgent::userNotInGroup; //unacceptableUser; // userNotInGroup
537 }
538
539 if (status == errAuthorizationSuccess)
540 break;
541 }
542 else
543 if ((status == errAuthorizationCanceled) ||
544 (status == errAuthorizationInternal))
545 {
546 auth.clearInfoSet();
547 break;
548 }
549 }
550
551 // If we fell out of the loop because of too many tries, notify user
552 if (tries == mTries)
553 {
554 reason = SecurityAgent::tooManyTries;
555 AuthItemRef retryHint (AGENT_HINT_RETRY_REASON, AuthValueOverlay(sizeof(reason), &reason));
556 environmentToClient.erase(retryHint); environmentToClient.insert(retryHint); // replace
557 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
558 environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
559 eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
560 // XXX/cs is this still necessary?
561 auth.clearInfoSet();
562 }
563
564 return status;
565 }
566
567 // create externally verified credentials on the basis of
568 // mechanism-provided information
569 CredentialSet
570 RuleImpl::makeCredentials(const AuthItemSet &context) const
571 {
572 CredentialSet newCredentials;
573
574 do {
575 AuthItemSet::const_iterator found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentUsername) );
576 if (found == context.end())
577 break;
578 string username = (**found).stringValue();
579 secdebug("AuthEvalMech", "found username");
580
581 const uid_t *uid = NULL;
582 found = find_if(context.begin(), context.end(), FindAuthItemByRightName("uid") );
583 if (found != context.end())
584 {
585 uid = static_cast<const uid_t *>((**found).value().data);
586 secdebug("AuthEvalMech", "found uid");
587 }
588
589 const gid_t *gid = NULL;
590 found = find_if(context.begin(), context.end(), FindAuthItemByRightName("gid") );
591 if (found != context.end())
592 {
593 gid = static_cast<const gid_t *>((**found).value().data);
594 secdebug("AuthEvalMech", "found gid");
595 }
596
597 if (username.length() && uid && gid)
598 {
599 // credential is valid because mechanism says so
600 newCredentials.insert(Credential(username, *uid, *gid, mShared));
601 }
602 else
603 {
604 found = find_if(context.begin(), context.end(), FindAuthItemByRightName(kAuthorizationEnvironmentPassword) );
605 if (found != context.end())
606 {
607 secdebug("AuthEvalMech", "found password");
608 string password = (**found).stringValue();
609 secdebug("AuthEvalMech", "falling back on username/password credential if valid");
610 newCredentials.insert(Credential(username, password, mShared));
611 }
612 }
613 } while(0);
614
615 return newCredentials;
616 }
617
618 // evaluate whether a good credential of the current session owner would authorize a right
619 OSStatus
620 RuleImpl::evaluateSessionOwner(const AuthItemRef &inRight, const Rule &inRule,
621 const AuthItemSet &environment,
622 const CFAbsoluteTime now,
623 const AuthorizationToken &auth,
624 string& usernamehint) const
625 {
626 // username hint is taken from the user who created the authorization, unless it's clearly ineligible
627 OSStatus status = noErr;
628 // @@@ we have no access to current requester uid here and the process uid is only taken when the authorization is created
629 // meaning that a process like loginwindow that drops privs later is screwed.
630 uid_t uid = auth.creatorUid();
631
632 Server::active().longTermActivity();
633 struct passwd *pw = getpwuid(uid);
634 if (pw != NULL)
635 {
636 // avoid hinting a locked account (ie. root)
637 if ( (pw->pw_passwd == NULL) ||
638 strcmp(pw->pw_passwd, "*") ) {
639 // Check if username will authorize the request and set username to
640 // be used as a hint to the user if so
641 status = evaluateCredentialForRight(inRight, inRule, environment, now, Credential(pw->pw_name, pw->pw_uid, pw->pw_gid, mShared), true);
642
643 if (status == errAuthorizationSuccess)
644 usernamehint = pw->pw_name;
645 } //fi
646 endpwent();
647 }
648 return status;
649 }
650
651
652
653 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
654 // return errAuthorizationDenied otherwise.
655 OSStatus
656 RuleImpl::evaluateCredentialForRight(const AuthItemRef &inRight, const Rule &inRule, const AuthItemSet &environment, CFAbsoluteTime now, const Credential &credential, bool ignoreShared) const
657 {
658 assert(mType == kUser);
659
660 // Get the username from the credential
661 const char *user = credential->username().c_str();
662
663 // If the credential is not valid or it's age is more than the allowed maximum age
664 // for a credential, deny.
665 if (!credential->isValid())
666 {
667 secdebug("autheval", "credential for user %s is invalid, denying right %s", user, inRight->name());
668 return errAuthorizationDenied;
669 }
670
671 if (now - credential->creationTime() > mMaxCredentialAge)
672 {
673 secdebug("autheval", "credential for user %s has expired, denying right %s", user, inRight->name());
674 return errAuthorizationDenied;
675 }
676
677 if (!ignoreShared && !mShared && credential->isShared())
678 {
679 secdebug("autheval", "shared credential for user %s cannot be used, denying right %s", user, inRight->name());
680 return errAuthorizationDenied;
681 }
682
683 // A root (uid == 0) user can do anything
684 if (credential->uid() == 0)
685 {
686 secdebug("autheval", "user %s has uid 0, granting right %s", user, inRight->name());
687 return errAuthorizationSuccess;
688 }
689
690 // XXX/cs replace with remembered session-owner once that functionality is added to SecurityServer
691 if (mSessionOwner)
692 {
693 uid_t console_user;
694 struct stat console_stat;
695 if (!lstat("/dev/console", &console_stat))
696 {
697 console_user = console_stat.st_uid;
698 if (credential->uid() == console_user)
699 {
700 secdebug("autheval", "user %s is session-owner(uid: %d), granting right %s", user, console_user, inRight->name());
701 return errAuthorizationSuccess;
702 }
703 }
704 else
705 secdebug("autheval", "session-owner check failed.");
706 }
707
708 if (mGroupName.length())
709 {
710 const char *groupname = mGroupName.c_str();
711 Server::active().longTermActivity();
712 struct group *gr = getgrnam(groupname);
713 if (!gr)
714 return errAuthorizationDenied;
715
716 // Is this the default group of this user?
717 // PR-2875126 <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
718 // When this is fixed this warning should go away.
719 if (credential->gid() == gr->gr_gid)
720 {
721 secdebug("autheval", "user %s has group %s(%d) as default group, granting right %s",
722 user, groupname, gr->gr_gid, inRight->name());
723 endgrent();
724 return errAuthorizationSuccess;
725 }
726
727 for (char **group = gr->gr_mem; *group; ++group)
728 {
729 if (!strcmp(*group, user))
730 {
731 secdebug("autheval", "user %s is a member of group %s, granting right %s",
732 user, groupname, inRight->name());
733 endgrent();
734 return errAuthorizationSuccess;
735 }
736 }
737
738 secdebug("autheval", "user %s is not a member of group %s, denying right %s",
739 user, groupname, inRight->name());
740 endgrent();
741 }
742
743 return errAuthorizationDenied;
744 }
745
746
747
748 OSStatus
749 RuleImpl::evaluateUser(const AuthItemRef &inRight, const Rule &inRule,
750 AuthItemSet &environmentToClient, AuthorizationFlags flags,
751 CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
752 AuthorizationToken &auth) const
753 {
754 // If we got here, this is a kUser type rule, let's start looking for a
755 // credential that is satisfactory
756
757 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
758 // created by a proccess running as root to automatically get a right.
759 if (mAllowRoot && auth.creatorUid() == 0)
760 {
761 secdebug("autheval", "creator of authorization has uid == 0 granting right %s",
762 inRight->name());
763 return errAuthorizationSuccess;
764 }
765
766 // if this is a "is-admin" rule check that and return
767 // XXX/cs add way to specify is-admin class of rule: if (mNoVerify)
768 if (name() == kAuthorizationRuleIsAdmin)
769 {
770 string username;
771 if (!evaluateSessionOwner(inRight, inRule, environmentToClient, now, auth, username))
772 return errAuthorizationSuccess;
773 }
774
775 // First -- go though the credentials we either already used or obtained during this authorize operation.
776 for (CredentialSet::const_iterator it = credentials.begin(); it != credentials.end(); ++it)
777 {
778 OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, true);
779 if (status != errAuthorizationDenied)
780 {
781 // add credential to authinfo
782 auth.setCredentialInfo(*it);
783 return status;
784 }
785 }
786
787 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
788 if (inCredentials)
789 {
790 for (CredentialSet::const_iterator it = inCredentials->begin(); it != inCredentials->end(); ++it)
791 {
792 OSStatus status = evaluateCredentialForRight(inRight, inRule, environmentToClient, now, *it, false);
793 if (status == errAuthorizationSuccess)
794 {
795 // Add the credential we used to the output set.
796 // whack an equivalent credential, so it gets updated to a later achieved credential which must have been more stringent
797 credentials.erase(*it); credentials.insert(*it);
798 // add credential to authinfo
799 auth.setCredentialInfo(*it);
800
801 return status;
802 }
803 else if (status != errAuthorizationDenied)
804 return status;
805 }
806 }
807
808 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
809 // our flags let us do so.
810 if (!(flags & kAuthorizationFlagExtendRights))
811 return errAuthorizationDenied;
812
813 // authorizations that timeout immediately cannot be preauthorized
814 if ((flags & kAuthorizationFlagPreAuthorize) &&
815 (mMaxCredentialAge == 0.0))
816 {
817 inRight->setFlags(inRight->flags() | kAuthorizationFlagCanNotPreAuthorize);
818 return errAuthorizationSuccess;
819 }
820
821 if (!(flags & kAuthorizationFlagInteractionAllowed))
822 return errAuthorizationInteractionNotAllowed;
823
824 setAgentHints(inRight, inRule, environmentToClient, auth);
825
826 // If a different evaluation is prescribed,
827 // we'll run that and validate the credentials from there
828 // we fall back on a default configuration
829 return evaluateAuthorization(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
830 }
831
832 OSStatus
833 RuleImpl::evaluateMechanismOnly(const AuthItemRef &inRight, const Rule &inRule, AuthItemSet &environmentToClient, AuthorizationToken &auth, CredentialSet &outCredentials) const
834 {
835 uint32 tries = 0;
836 OSStatus status;
837
838 Process &cltProc = Server::process();
839 // Authorization preserves creator's UID in setuid processes
840 uid_t cltUid = (cltProc.uid() != 0) ? cltProc.uid() : auth.creatorUid();
841 secdebug("AuthEvalMech", "Mechanism invocation by process %d (UID %d)", cltProc.pid(), cltUid);
842
843 {
844 AgentMechanismEvaluator eval(cltUid, cltProc.session(), mEvalDef);
845
846 do
847 {
848 setAgentHints(inRight, inRule, environmentToClient, auth);
849 AuthItemRef triesHint(AGENT_HINT_TRIES, AuthValueOverlay(sizeof(tries), &tries));
850 environmentToClient.erase(triesHint); environmentToClient.insert(triesHint); // replace
851
852 status = eval.run(AuthValueVector(), environmentToClient, auth.infoSet());
853
854 if ((status == errAuthorizationSuccess) ||
855 (status == errAuthorizationCanceled)) // @@@ can only pass back sideband through context
856 {
857 secdebug("AuthEvalMech", "storing new context for authorization");
858 auth.setInfoSet(eval.context());
859 if (status == errAuthorizationSuccess)
860 {
861 outCredentials = makeCredentials(eval.context());
862 }
863 }
864
865 tries++;
866 }
867 while ((status == errAuthorizationDenied) // only if we have an expected failure we continue
868 && ((mTries == 0) // mTries == 0 means we try forever
869 || ((mTries > 0) // mTries > 0 means we try up to mTries times
870 && (tries < mTries))));
871 }
872
873 if (name() == "system.login.console")
874 {
875 QueryInvokeMechanism query(cltUid, cltProc.session());
876 query.terminateAgent();
877 }
878
879 return status;
880 }
881
882 OSStatus
883 RuleImpl::evaluateRules(const AuthItemRef &inRight, const Rule &inRule,
884 AuthItemSet &environmentToClient, AuthorizationFlags flags,
885 CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
886 AuthorizationToken &auth) const
887 {
888 // line up the rules to try
889 if (!mRuleDef.size())
890 return errAuthorizationSuccess;
891
892 uint32_t count = 0;
893 OSStatus status = errAuthorizationSuccess;
894 vector<Rule>::const_iterator it;
895
896 for (it = mRuleDef.begin();it != mRuleDef.end(); it++)
897 {
898 // are we at k yet?
899 if ((mType == kKofN) && (count == mKofN))
900 return errAuthorizationSuccess;
901
902 // get a rule and try it
903 status = (*it)->evaluate(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
904
905 // if status is cancel/internal error abort
906 if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
907 return status;
908
909 if (status != errAuthorizationSuccess)
910 {
911 // continue if we're only looking for k of n
912 if (mType == kKofN)
913 continue;
914
915 break;
916 }
917 else
918 count++;
919 }
920
921 return status; // return the last failure
922 }
923
924
925 OSStatus
926 RuleImpl::evaluate(const AuthItemRef &inRight, const Rule &inRule,
927 AuthItemSet &environmentToClient, AuthorizationFlags flags,
928 CFAbsoluteTime now, const CredentialSet *inCredentials, CredentialSet &credentials,
929 AuthorizationToken &auth) const
930 {
931 switch (mType)
932 {
933 case kAllow:
934 secdebug("autheval", "rule is always allow");
935 return errAuthorizationSuccess;
936 case kDeny:
937 secdebug("autheval", "rule is always deny");
938 return errAuthorizationDenied;
939 case kUser:
940 secdebug("autheval", "rule is user");
941 return evaluateUser(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
942 case kRuleDelegation:
943 secdebug("autheval", "rule evaluates rules");
944 return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
945 case kKofN:
946 secdebug("autheval", "rule evaluates k-of-n rules");
947 return evaluateRules(inRight, inRule, environmentToClient, flags, now, inCredentials, credentials, auth);
948 case kEvaluateMechanisms:
949 secdebug("autheval", "rule evaluates mechanisms");
950 return evaluateMechanismOnly(inRight, inRule, environmentToClient, auth, credentials);
951 default:
952 MacOSError::throwMe(errAuthorizationInternal); // XXX/cs invalid rule
953 }
954 }
955
956 Rule::Rule() : RefPointer<RuleImpl>(new RuleImpl()) {}
957 Rule::Rule(const string &inRightName, CFDictionaryRef cfRight, CFDictionaryRef cfRules) : RefPointer<RuleImpl>(new RuleImpl(inRightName, cfRight, cfRules)) {}
958
959
960
961 } // end namespace Authorization