2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 * AuthorizationEngine.cpp
23 * Created by Michael Brouwer on Thu Oct 12 2000.
24 * Copyright (c) 2000 Apple Computer Inc. All rights reserved.
28 #include "AuthorizationEngine.h"
31 #include "authority.h"
33 #include <Security/AuthorizationTags.h>
34 #include <Security/logging.h>
35 #include <Security/debugging.h>
37 #include <CoreFoundation/CFData.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <CoreFoundation/CFPropertyList.h>
48 // for longname lookup
49 #include <netinfo/ni.h>
50 // private header (lu_utils.h from lookup project)
52 int lookupd_query(ni_proplist
*l
, ni_proplist
***out
);
53 ni_proplist
*lookupd_make_query(char *cat
, char *fmt
, ...);
54 int _lu_running(void);
57 using namespace Authorization
;
60 // Errors to be thrown
62 Error::Error(int err
) : error(err
)
66 const char *Error::what() const
67 { return "Authorization error"; }
69 CSSM_RETURN
Error::cssmError() const
70 { return error
; } // @@@ eventually...
72 OSStatus
Error::osStatus() const
75 void Error::throwMe(int err
) { throw Error(err
); }
79 // CredentialImpl class
82 // only for testing whether this credential is usable
83 CredentialImpl::CredentialImpl(const string
&username
, const uid_t uid
, const gid_t gid
, bool shared
) :
84 mUsername(username
), mShared(shared
), mUid(uid
), mGid(gid
), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(true)
88 // credential with validity based on username/password combination.
89 CredentialImpl::CredentialImpl(const string
&username
, const string
&password
, bool shared
) :
90 mShared(shared
), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(false)
92 // try short name first
93 const char *user
= username
.c_str();
94 struct passwd
*pw
= getpwnam(user
);
98 if ( !pw
&& _lu_running() ) {
99 // try lookup query to find passed username as a long name (realname in NI-speak)
100 ni_proplist
**out
= NULL
;
101 // query "user" records. "k" specifies position of keys in varargs
102 ni_proplist
*in
= lookupd_make_query("user", "kv", "realname", user
);
105 int results
= lookupd_query(in
, &out
);
106 ni_proplist_free(in
);
109 // Find the first, if any, name value in returned records, getpwnam, and dispose of them
110 for (int i
=0; i
<results
; ++i
) {
111 ni_proplist
*nipl
= out
[i
];
112 for (unsigned int j
=0; !pw
&& j
< nipl
->ni_proplist_len
; j
++) {
113 if ( !strcmp(nipl
->ni_proplist_val
[j
].nip_name
, "name") &&
114 (nipl
->ni_proplist_val
[j
].nip_val
.ni_namelist_len
> 0) )
115 pw
= getpwnam( *(nipl
->ni_proplist_val
[j
].nip_val
.ni_namelist_val
) );
117 ni_proplist_free(nipl
);
124 debug("autheval", "user %s not found, creating invalid credential", user
);
128 if (pw
->pw_passwd
!= NULL
&& pw
->pw_passwd
[0])
130 const char *passwd
= password
.c_str();
131 if (strcmp(crypt(passwd
, pw
->pw_passwd
), pw
->pw_passwd
))
133 debug("autheval", "password for user %s is invalid, creating invalid credential", user
);
138 debug("autheval", "password for user %s is ok, creating%s credential",
139 user
, mShared
? " shared" : "");
141 mUsername
= string ( pw
->pw_name
);
153 CredentialImpl::~CredentialImpl()
158 CredentialImpl::operator < (const CredentialImpl
&other
) const
160 if (!mShared
&& other
.mShared
)
162 if (!other
.mShared
&& mShared
)
165 return mUsername
< other
.mUsername
;
168 // Returns true if this CredentialImpl should be shared.
170 CredentialImpl::isShared() const
177 CredentialImpl::merge(const CredentialImpl
&other
)
179 assert(mUsername
== other
.mUsername
);
181 if (other
.mValid
&& (!mValid
|| mCreationTime
< other
.mCreationTime
))
183 mCreationTime
= other
.mCreationTime
;
190 // The time at which this credential was obtained.
192 CredentialImpl::creationTime() const
194 return mCreationTime
;
197 // Return true iff this credential is valid.
199 CredentialImpl::isValid() const
205 CredentialImpl::invalidate()
213 Credential::Credential() :
214 RefPointer
<CredentialImpl
>(NULL
)
218 Credential::Credential(CredentialImpl
*impl
) :
219 RefPointer
<CredentialImpl
>(impl
)
223 Credential::Credential(const string
&username
, const uid_t uid
, const gid_t gid
, bool shared
) :
224 RefPointer
<CredentialImpl
>(new CredentialImpl(username
, uid
, gid
, shared
))
228 Credential::Credential(const string
&username
, const string
&password
, bool shared
) :
229 RefPointer
<CredentialImpl
>(new CredentialImpl(username
, password
, shared
))
233 Credential::~Credential()
238 Credential::operator < (const Credential
&other
) const
246 return (**this) < (*other
);
254 Right::overlay(AuthorizationItem
&item
)
256 return static_cast<Right
&>(item
);
260 Right::overlay(AuthorizationItem
*item
)
262 return static_cast<Right
*>(item
);
273 Right::Right(AuthorizationString inName
, size_t inValueLength
, const void *inValue
)
276 valueLength
= inValueLength
;
277 value
= const_cast<void *>(inValue
);
285 Right::operator < (const Right
&other
) const
287 return strcmp(name
, other
.name
) < 0;
294 const AuthorizationRights
RightSet::gEmptyRights
= { 0, NULL
};
296 RightSet::RightSet(const AuthorizationRights
*rights
) :
297 mRights(const_cast<AuthorizationRights
*>(rights
? rights
: &gEmptyRights
))
301 RightSet::RightSet(const RightSet
&other
)
303 mRights
= other
.mRights
;
306 RightSet::~RightSet()
310 RightSet::const_reference
311 RightSet::back() const
313 // @@@ Should this if empty::throwMe()?
314 return static_cast<const_reference
>(mRights
->items
[size() - 1]);
319 // MutableRightSet class
321 MutableRightSet::MutableRightSet(size_t count
, const Right
&element
) :
324 mRights
= new AuthorizationRights();
325 mRights
->items
= reinterpret_cast<pointer
>(malloc(sizeof(Right
) * mCapacity
));
329 throw std::bad_alloc();
332 mRights
->count
= count
;
333 for (size_type ix
= 0; ix
< count
; ++ix
)
334 mRights
->items
[ix
] = element
;
337 MutableRightSet::MutableRightSet(const RightSet
&other
)
339 size_type count
= other
.size();
341 mRights
= new AuthorizationRights();
343 mRights
->items
= reinterpret_cast<pointer
>(malloc(sizeof(Right
) * mCapacity
));
347 throw std::bad_alloc();
350 mRights
->count
= count
;
351 for (size_type ix
= 0; ix
< count
; ++ix
)
352 mRights
->items
[ix
] = other
.mRights
->items
[ix
];
355 MutableRightSet::~MutableRightSet()
357 free(mRights
->items
);
362 MutableRightSet::operator = (const RightSet
&other
)
364 size_type count
= other
.size();
365 if (capacity() < count
)
368 mRights
->count
= count
;
369 for (size_type ix
= 0; ix
< count
; ++ix
)
370 mRights
->items
[ix
] = other
.mRights
->items
[ix
];
376 MutableRightSet::swap(MutableRightSet
&other
)
378 AuthorizationRights
*rights
= mRights
;
379 size_t capacity
= mCapacity
;
380 mRights
= other
.mRights
;
381 mCapacity
= other
.mCapacity
;
382 other
.mRights
= rights
;
383 other
.mCapacity
= capacity
;
386 MutableRightSet::reference
387 MutableRightSet::back()
389 // @@@ Should this if empty::throwMe()?
390 return static_cast<reference
>(mRights
->items
[size() - 1]);
394 MutableRightSet::push_back(const_reference right
)
396 if (size() >= capacity())
397 grow(capacity() + 1);
399 mRights
->items
[mRights
->count
] = right
;
404 MutableRightSet::pop_back()
406 // @@@ Should this if empty::throwMe()?
412 MutableRightSet::grow(size_type min_capacity
)
414 size_type newCapacity
= mCapacity
* mCapacity
;
415 if (newCapacity
< min_capacity
)
416 newCapacity
= min_capacity
;
418 void *newItems
= realloc(mRights
->items
, sizeof(*mRights
->items
) * newCapacity
);
420 throw std::bad_alloc();
422 mRights
->items
= reinterpret_cast<pointer
>(newItems
);
423 mCapacity
= newCapacity
;
430 CFStringRef
Rule::kUserInGroupID
= CFSTR("group");
431 CFStringRef
Rule::kTimeoutID
= CFSTR("timeout");
432 CFStringRef
Rule::kSharedID
= CFSTR("shared");
433 CFStringRef
Rule::kAllowRootID
= CFSTR("allow-root");
434 CFStringRef
Rule::kDenyID
= CFSTR("deny");
435 CFStringRef
Rule::kAllowID
= CFSTR("allow");
439 mType(kUserInGroup
), mGroupName("admin"), mMaxCredentialAge(300.0), mShared(true), mAllowRoot(false)
441 // @@@ Default rule is shared admin group with 5 minute timeout
444 Rule::Rule(CFTypeRef cfRule
)
446 // @@@ This code is ugly. Serves me right for using CF.
447 if (CFGetTypeID(cfRule
) == CFStringGetTypeID())
449 CFStringRef tag
= reinterpret_cast<CFStringRef
>(cfRule
);
450 if (CFEqual(kAllowID
, tag
))
452 debug("authrule", "rule always allow");
455 else if (CFEqual(kDenyID
, tag
))
457 debug("authrule", "rule always deny");
463 else if (CFGetTypeID(cfRule
) == CFDictionaryGetTypeID())
465 mType
= kUserInGroup
;
466 CFDictionaryRef dict
= reinterpret_cast<CFDictionaryRef
>(cfRule
);
467 CFTypeRef groupTag
= CFDictionaryGetValue(dict
, kUserInGroupID
);
468 if (!groupTag
|| CFGetTypeID(groupTag
) != CFStringGetTypeID())
471 CFStringRef group
= reinterpret_cast<CFStringRef
>(groupTag
);
473 const char *ptr
= CFStringGetCStringPtr(group
, kCFStringEncodingUTF8
);
476 if (CFStringGetCString(group
, buffer
, 512, kCFStringEncodingUTF8
))
482 mGroupName
= string(ptr
);
484 mMaxCredentialAge
= DBL_MAX
;
485 CFTypeRef timeoutTag
= CFDictionaryGetValue(dict
, kTimeoutID
);
488 if (CFGetTypeID(timeoutTag
) != CFNumberGetTypeID())
490 CFNumberGetValue(reinterpret_cast<CFNumberRef
>(timeoutTag
), kCFNumberDoubleType
, &mMaxCredentialAge
);
493 CFTypeRef sharedTag
= CFDictionaryGetValue(dict
, kSharedID
);
497 if (CFGetTypeID(sharedTag
) != CFBooleanGetTypeID())
499 mShared
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(sharedTag
));
502 CFTypeRef allowRootTag
= CFDictionaryGetValue(dict
, kAllowRootID
);
506 if (CFGetTypeID(allowRootTag
) != CFBooleanGetTypeID())
508 mAllowRoot
= CFBooleanGetValue(reinterpret_cast<CFBooleanRef
>(allowRootTag
));
510 debug("authrule", "rule user in group \"%s\" timeout %g%s%s",
511 mGroupName
.c_str(), mMaxCredentialAge
, mShared
? " shared" : "",
512 mAllowRoot
? " allow-root" : "");
516 Rule::Rule(const Rule
&other
) :
518 mGroupName(other
.mGroupName
),
519 mMaxCredentialAge(other
.mMaxCredentialAge
),
520 mShared(other
.mShared
),
521 mAllowRoot(other
.mAllowRoot
)
526 Rule::operator = (const Rule
&other
)
529 mGroupName
= other
.mGroupName
;
530 mMaxCredentialAge
= other
.mMaxCredentialAge
;
531 mShared
= other
.mShared
;
532 mAllowRoot
= other
.mAllowRoot
;
541 Rule::evaluate(const Right
&inRight
,
542 const AuthorizationEnvironment
*environment
, AuthorizationFlags flags
,
543 CFAbsoluteTime now
, const CredentialSet
*inCredentials
, CredentialSet
&credentials
,
544 const AuthorizationToken
&auth
)
549 debug("autheval", "rule is always allow");
550 return errAuthorizationSuccess
;
552 debug("autheval", "rule is always deny");
553 return errAuthorizationDenied
;
555 debug("autheval", "rule is user in group");
561 // If we got here, this is a kUserInGroup type rule, let's start looking for a
562 // credential that is satisfactory
564 // Zeroth -- Here is an extra special saucy ugly hack to allow authorizations
565 // created by a proccess running as root to automatically get a right.
566 if (mAllowRoot
&& auth
.creatorUid() == 0)
568 debug("autheval", "creator of authorization has uid == 0 granting right %s",
569 inRight
.rightName());
570 return errAuthorizationSuccess
;
573 // First -- go though the credentials we either already used or obtained during this authorize operation.
574 for (CredentialSet::const_iterator it
= credentials
.begin(); it
!= credentials
.end(); ++it
)
576 OSStatus status
= evaluate(inRight
, environment
, now
, *it
, true);
577 if (status
!= errAuthorizationDenied
)
581 // Second -- go though the credentials passed in to this authorize operation by the state management layer.
584 for (CredentialSet::const_iterator it
= inCredentials
->begin(); it
!= inCredentials
->end(); ++it
)
586 OSStatus status
= evaluate(inRight
, environment
, now
, *it
, false);
587 if (status
== errAuthorizationSuccess
)
589 // Add the credential we used to the output set.
590 // @@@ Deal with potential credential merges.
591 credentials
.insert(*it
);
594 else if (status
!= errAuthorizationDenied
)
599 // Finally -- We didn't find the credential in our passed in credential lists. Obtain a new credential if
600 // our flags let us do so.
601 if (!(flags
& kAuthorizationFlagExtendRights
))
602 return errAuthorizationDenied
;
604 if (!(flags
& kAuthorizationFlagInteractionAllowed
))
605 return errAuthorizationInteractionNotAllowed
;
607 QueryAuthorizeByGroup query
;
610 // @@@ This should really be the loginname of the proccess that originally created the AuthorizationRef.
611 // For now we get the pw_name of the user with the uid of the calling process.
612 uid_t uid
= query
.uid();
615 struct passwd
*pw
= getpwuid(uid
);
618 // avoid hinting a locked account (ie. root)
619 if ( (pw
->pw_passwd
== NULL
) ||
620 strcmp(pw
->pw_passwd
, "*") ) {
621 // Check if username will authorize the request and set username to
622 // be used as a hint to the user if so
623 if (evaluate(inRight
, environment
, now
, Credential(pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
, mShared
), true) == errAuthorizationSuccess
) {
624 // user long name as hint
625 usernamehint
= string( pw
->pw_gecos
);
626 // minus other gecos crud
627 size_t comma
= usernamehint
.find(',');
629 usernamehint
= usernamehint
.substr(0, comma
);
630 // or fallback to short username
631 if (usernamehint
.size() == 0)
632 usernamehint
= string( pw
->pw_name
);
639 Credential newCredential
;
640 // @@@ Keep the default reason the same, so the agent only gets userNotInGroup or invalidPassphrase
641 SecurityAgent::Reason reason
= SecurityAgent::userNotInGroup
;
642 // @@@ Hardcoded 3 tries to avoid infinite loops.
643 for (int tryCount
= 0; tryCount
< 3; ++tryCount
)
645 // Obtain a new credential. Anything but success is considered an error.
646 OSStatus status
= obtainCredential(query
, inRight
, environment
, usernamehint
.c_str(), newCredential
, reason
);
650 // Now we have successfully obtained a credential we need to make sure it authorizes the requested right
651 if (!newCredential
->isValid())
652 reason
= SecurityAgent::invalidPassphrase
;
654 status
= evaluate(inRight
, environment
, now
, newCredential
, true);
655 if (status
== errAuthorizationSuccess
)
657 // Add the new credential we obtained to the output set.
658 // @@@ Deal with potential credential merges.
659 credentials
.insert(newCredential
);
661 return errAuthorizationSuccess
;
663 else if (status
!= errAuthorizationDenied
)
665 reason
= SecurityAgent::userNotInGroup
;
669 query
.cancel(SecurityAgent::tooManyTries
);
670 return errAuthorizationDenied
;
673 // Return errAuthorizationSuccess if this rule allows access based on the specified credential,
674 // return errAuthorizationDenied otherwise.
676 Rule::evaluate(const Right
&inRight
, const AuthorizationEnvironment
*environment
, CFAbsoluteTime now
,
677 const Credential
&credential
, bool ignoreShared
)
679 assert(mType
== kUserInGroup
);
681 // Get the username from the credential
682 const char *user
= credential
->username().c_str();
684 // If the credential is not valid or it's age is more than the allowed maximum age
685 // for a credential, deny.
686 if (!credential
->isValid())
688 debug("autheval", "credential for user %s is invalid, denying right %s", user
, inRight
.rightName());
689 return errAuthorizationDenied
;
692 if (now
- credential
->creationTime() > mMaxCredentialAge
)
694 debug("autheval", "credential for user %s has expired, denying right %s", user
, inRight
.rightName());
695 return errAuthorizationDenied
;
698 if (!ignoreShared
&& !mShared
&& credential
->isShared())
700 debug("autheval", "shared credential for user %s cannot be used, denying right %s", user
, inRight
.rightName());
701 return errAuthorizationDenied
;
704 // A root (uid == 0) user can do anything
705 if (credential
->uid() == 0)
707 debug("autheval", "user %s has uid 0, granting right %s", user
, inRight
.rightName());
708 return errAuthorizationSuccess
;
711 const char *groupname
= mGroupName
.c_str();
712 struct group
*gr
= getgrnam(groupname
);
714 return errAuthorizationDenied
;
716 // Is this the default group of this user?
717 // <grp.h> declares gr_gid int, as opposed to advertised (getgrent(3)) gid_t
718 if (credential
->gid() == gr
->gr_gid
)
720 debug("autheval", "user %s has group %s(%d) as default group, granting right %s",
721 user
, groupname
, gr
->gr_gid
, inRight
.rightName());
723 return errAuthorizationSuccess
;
726 for (char **group
= gr
->gr_mem
; *group
; ++group
)
728 if (!strcmp(*group
, user
))
730 debug("autheval", "user %s is a member of group %s, granting right %s",
731 user
, groupname
, inRight
.rightName());
733 return errAuthorizationSuccess
;
737 debug("autheval", "user %s is not a member of group %s, denying right %s",
738 user
, groupname
, inRight
.rightName());
740 return errAuthorizationDenied
;
744 Rule::obtainCredential(QueryAuthorizeByGroup
&query
, const Right
&inRight
,
745 const AuthorizationEnvironment
*environment
, const char *usernameHint
, Credential
&outCredential
, SecurityAgent::Reason reason
)
747 char nameBuffer
[SecurityAgent::maxUsernameLength
];
748 char passphraseBuffer
[SecurityAgent::maxPassphraseLength
];
749 OSStatus status
= errAuthorizationDenied
;
752 if (query(mGroupName
.c_str(), usernameHint
, nameBuffer
, passphraseBuffer
, reason
))
754 } catch (const CssmCommonError
&err
) {
755 status
= err
.osStatus();
757 status
= errAuthorizationInternal
;
759 if (status
== CSSM_ERRCODE_USER_CANCELED
)
761 debug("auth", "canceled obtaining credential for user in group %s", mGroupName
.c_str());
762 return errAuthorizationCanceled
;
764 if (status
== CSSM_ERRCODE_NO_USER_INTERACTION
)
766 debug("auth", "user interaction not possible obtaining credential for user in group %s", mGroupName
.c_str());
767 return errAuthorizationInteractionNotAllowed
;
772 debug("auth", "failed obtaining credential for user in group %s", mGroupName
.c_str());
776 debug("auth", "obtained credential for user %s", nameBuffer
);
778 string
username(nameBuffer
);
779 string
password(passphraseBuffer
);
780 outCredential
= Credential(username
, password
, mShared
);
781 return errAuthorizationSuccess
;
788 Engine::Engine(const char *configFile
) :
789 mLastChecked(DBL_MIN
)
791 mRulesFileName
= new char[strlen(configFile
) + 1];
792 strcpy(mRulesFileName
, configFile
);
793 memset(&mRulesFileMtimespec
, 0, sizeof(mRulesFileMtimespec
));
798 delete[] mRulesFileName
;
802 Engine::updateRules(CFAbsoluteTime now
)
808 // Don't do anything if we checked the timestamp less than 5 seconds ago
809 if (mLastChecked
> now
- 5.0)
813 if (stat(mRulesFileName
, &st
))
815 Syslog::error("Stating rules file \"%s\": %s", mRulesFileName
, strerror(errno
));
816 /* @@@ No rules file found, use defaults: admin group for everything. */
817 //UnixError::throwMe(errno);
821 // @@@ Make sure this is the right way to compare 2 struct timespec thingies
822 // Technically we should check st_dev and st_ino as well since if either of those change
823 // we are looking at a different file too.
824 if (memcmp(&st
.st_mtimespec
, &mRulesFileMtimespec
, sizeof(mRulesFileMtimespec
)))
835 // Make an entry in the mRules map that matches every right to the default Rule.
837 mRules
.insert(RuleMap::value_type(string(), Rule()));
839 int fd
= open(mRulesFileName
, O_RDONLY
, 0);
842 Syslog::error("Opening rules file \"%s\": %s", mRulesFileName
, strerror(errno
));
850 UnixError::throwMe(errno
);
852 mRulesFileMtimespec
= st
.st_mtimespec
;
854 off_t fileSize
= st
.st_size
;
856 CFRef
<CFMutableDataRef
> xmlData(CFDataCreateMutable(NULL
, fileSize
));
857 CFDataSetLength(xmlData
, fileSize
);
858 void *buffer
= CFDataGetMutableBytePtr(xmlData
);
859 size_t bytesRead
= read(fd
, buffer
, fileSize
);
860 if (bytesRead
!= fileSize
)
862 if (bytesRead
== static_cast<size_t>(-1))
864 Syslog::error("Reading rules file \"%s\": %s", mRulesFileName
, strerror(errno
));
868 Syslog::error("Could only read %ul out of %ul bytes from rules file \"%s\"",
869 bytesRead
, fileSize
, mRulesFileName
);
873 CFStringRef errorString
;
874 CFRef
<CFDictionaryRef
> newRoot(reinterpret_cast<CFDictionaryRef
>
875 (CFPropertyListCreateFromXMLData(NULL
, xmlData
, kCFPropertyListImmutable
, &errorString
)));
879 const char *error
= CFStringGetCStringPtr(errorString
, kCFStringEncodingUTF8
);
882 if (CFStringGetCString(errorString
, buffer
, 512, kCFStringEncodingUTF8
))
886 Syslog::error("Parsing rules file \"%s\": %s", mRulesFileName
, error
);
890 if (CFGetTypeID(newRoot
) != CFDictionaryGetTypeID())
892 Syslog::error("Rules file \"%s\": is not a dictionary", mRulesFileName
);
907 Engine::parseRules(CFDictionaryRef rules
)
909 CFDictionaryApplyFunction(rules
, parseRuleCallback
, this);
913 Engine::parseRuleCallback(const void *key
, const void *value
, void *context
)
915 Engine
*engine
= reinterpret_cast<Engine
*>(context
);
916 if (CFGetTypeID(key
) != CFStringGetTypeID())
919 CFStringRef right
= reinterpret_cast<CFStringRef
>(key
);
920 engine
->parseRule(right
, reinterpret_cast<CFTypeRef
>(value
));
924 Engine::parseRule(CFStringRef cfRight
, CFTypeRef cfRule
)
927 const char *ptr
= CFStringGetCStringPtr(cfRight
, kCFStringEncodingUTF8
);
930 if (CFStringGetCString(cfRight
, buffer
, 512, kCFStringEncodingUTF8
))
937 mRules
[right
] = Rule(cfRule
);
938 debug("authrule", "added rule for right \"%s\"", right
.c_str());
942 Syslog::error("Rules file \"%s\" right \"%s\": rule is invalid", mRulesFileName
, ptr
);
948 @function AuthorizationEngine::getRule
950 Look up the Rule for a given right.
952 @param inRight (input) the right for which we want a rule.
954 @results The Rule for right
957 Engine::getRule(const Right
&inRight
) const
959 string
key(inRight
.rightName());
962 RuleMap::const_iterator it
= mRules
.find(key
);
963 if (it
!= mRules
.end())
965 debug("authrule", "right \"%s\" using right expression \"%s\"", inRight
.rightName(), key
.c_str());
972 // any reduction of a combination of two chars is futile
973 if (key
.size() > 2) {
974 // find last dot with exception of possible dot at end
975 string::size_type index
= key
.rfind('.', key
.size() - 2);
976 // cut right after found dot, or make it match default rule
977 key
= key
.substr(0, index
== string::npos
? 0 : index
+ 1);
984 @function AuthorizationEngine::authorize
988 @param inRights (input) List of rights being requested for authorization.
989 @param environment (optional/input) Environment containing information to be used during evaluation.
990 @param flags (input) Optional flags @@@ see AuthorizationCreate for a description.
991 @param inCredentials (input) Credentials already held by the caller.
992 @param outCredentials (output/optional) Credentials obtained, used or refreshed during this call to authorize the requested rights.
993 @param outRights (output/optional) Subset of inRights which were actually authorized.
995 @results Returns errAuthorizationSuccess if all rights requested are authorized, or if the kAuthorizationFlagPartialRights flag was specified. Might return other status values like errAuthorizationDenied, errAuthorizationCanceled or errAuthorizationInteractionNotAllowed
998 Engine::authorize(const RightSet
&inRights
, const AuthorizationEnvironment
*environment
,
999 AuthorizationFlags flags
, const CredentialSet
*inCredentials
, CredentialSet
*outCredentials
,
1000 MutableRightSet
*outRights
, const AuthorizationToken
&auth
)
1002 CredentialSet credentials
;
1003 MutableRightSet rights
;
1004 OSStatus status
= errAuthorizationSuccess
;
1006 // Get current time of day.
1007 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1009 // Update rules from database if needed
1012 // Check if a credential was passed into the environment and we were asked to extend the rights
1013 if (environment
&& (flags
& kAuthorizationFlagExtendRights
))
1015 const AuthorizationItem
*username
= NULL
, *password
= NULL
;
1016 bool shared
= false;
1017 for (UInt32 ix
= 0; ix
< environment
->count
; ++ix
)
1019 const AuthorizationItem
&item
= environment
->items
[ix
];
1020 if (!strcmp(item
.name
, kAuthorizationEnvironmentUsername
))
1022 if (!strcmp(item
.name
, kAuthorizationEnvironmentPassword
))
1024 if (!strcmp(item
.name
, kAuthorizationEnvironmentShared
))
1028 if (username
&& password
)
1030 // Let's create a credential from the passed in username and password.
1031 Credential
newCredential(string(reinterpret_cast<const char *>(username
->value
), username
->valueLength
),
1032 string(reinterpret_cast<const char *>(password
->value
), password
->valueLength
), shared
);
1033 // If it's valid insert it into the credentials list. Normally this is
1034 // only done if it actually authorizes a requested right, but for this
1035 // special case (environment) we do it even when no rights are being requested.
1036 if (newCredential
->isValid())
1037 credentials
.insert(newCredential
);
1041 RightSet::const_iterator end
= inRights
.end();
1042 for (RightSet::const_iterator it
= inRights
.begin(); it
!= end
; ++it
)
1044 // Get the rule for each right we are trying to obtain.
1045 OSStatus result
= getRule(*it
).evaluate(*it
, environment
, flags
, now
,
1046 inCredentials
, credentials
, auth
);
1047 if (result
== errAuthorizationSuccess
)
1048 rights
.push_back(*it
);
1049 else if (result
== errAuthorizationDenied
|| result
== errAuthorizationInteractionNotAllowed
)
1051 if (!(flags
& kAuthorizationFlagPartialRights
))
1057 else if (result
== errAuthorizationCanceled
)
1064 Syslog::error("Engine::authorize: Rule::evaluate returned %ld returning errAuthorizationInternal", result
);
1065 status
= errAuthorizationInternal
;
1071 outCredentials
->swap(credentials
);
1073 outRights
->swap(rights
);