2 * Copyright (c) 2006-2009,2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 // clientid - track and manage identity of securityd clients
28 #include <Security/SecCodePriv.h>
32 // Constructing a ClientIdentification doesn't do much.
33 // We're waiting for setup(), which should be called by the child class's
36 ClientIdentification::ClientIdentification()
42 // Initialize the ClientIdentification.
43 // This creates a process-level code object for the client.
45 void ClientIdentification::setup(pid_t pid
)
47 StLock
<Mutex
> _(mLock
);
48 if (OSStatus rc
= SecCodeCreateWithPID(pid
, kSecCSDefaultFlags
,
49 &mClientProcess
.aref()))
50 secdebug("clientid", "could not get code for process %d: OSStatus=%d",
52 mGuests
.erase(mGuests
.begin(), mGuests
.end());
57 // Return a SecCodeRef for the client process itself, regardless of
58 // which guest within it is currently active.
59 // Think twice before using this.
61 SecCodeRef
ClientIdentification::processCode() const
63 return mClientProcess
;
68 // Return a SecCodeRef for the currently active guest within the client
71 // We make a fair effort to cache client guest identities without over-growing
72 // the cache. Note that there's currently no protocol for being notified of
73 // a guest's death or disappearance (independent from the host process's death),
74 // so we couldn't track guests live even if we tried.
76 // Note that this consults Server::connection for the currently serviced
77 // Connection object, so this is not entirely a function of ClientIdentification state.
79 SecCodeRef
ClientIdentification::currentGuest() const
81 if (GuestState
*guest
= current())
84 return mClientProcess
;
87 ClientIdentification::GuestState
*ClientIdentification::current() const
89 // if we have no client identification, we can't find a current guest either
93 SecGuestRef guestRef
= Server::connection().guestRef();
95 // try to deliver an already-cached entry
97 StLock
<Mutex
> _(mLock
);
98 GuestMap::iterator it
= mGuests
.find(guestRef
);
99 if (it
!= mGuests
.end())
103 // okay, make a new one (this may take a while)
104 CFRef
<CFDictionaryRef
> attributes
= (guestRef
== kSecNoGuest
)
106 : makeCFDictionary(1, kSecGuestAttributeCanonical
, CFTempNumber(guestRef
).get());
107 Server::active().longTermActivity();
108 CFRef
<SecCodeRef
> code
;
109 switch (OSStatus rc
= SecCodeCopyGuestWithAttributes(processCode(),
110 attributes
, kSecCSDefaultFlags
, &code
.aref())) {
113 case errSecCSUnsigned
: // not signed; clearly not a host
114 case errSecCSNotAHost
: // signed but not marked as a (potential) host
115 code
= mClientProcess
;
117 case errSecCSNoSuchCode
: // potential host, but...
118 if (guestRef
== kSecNoGuest
) { // ... no guests (yet), so return the process
119 code
= mClientProcess
;
122 // else fall through // ... the guest we expected to be there isn't
124 MacOSError::throwMe(rc
);
126 StLock
<Mutex
> _(mLock
);
127 GuestState
&slot
= mGuests
[guestRef
];
128 if (!slot
.code
) // if another thread didn't get here first...
135 // Support for the legacy hash identification mechanism.
136 // The legacy machinery deals exclusively in terms of processes.
137 // It knows nothing about guests and their identities.
139 string
ClientIdentification::getPath() const
141 assert(mClientProcess
);
142 return codePath(currentGuest());
145 const CssmData
ClientIdentification::getHash() const
147 if (GuestState
*guest
= current()) {
148 if (!guest
->gotHash
) {
149 RefPointer
<OSXCode
> clientCode
= new OSXCodeWrap(guest
->code
);
150 OSXVerifier::makeLegacyHash(clientCode
, guest
->legacyHash
);
151 guest
->gotHash
= true;
153 return CssmData::wrap(guest
->legacyHash
, SHA1::digestLength
);
158 const bool ClientIdentification::checkAppleSigned() const
160 if (GuestState
*guest
= current()) {
161 if (!guest
->checkedSignature
) {
162 // This is the clownfish supported way to check for a Mac App Store or B&I signed build
163 CFStringRef requirementString
= CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])");
164 SecRequirementRef secRequirementRef
= NULL
;
165 OSStatus status
= SecRequirementCreateWithString(requirementString
, kSecCSDefaultFlags
, &secRequirementRef
);
166 if (status
== errSecSuccess
) {
167 OSStatus status
= SecCodeCheckValidity(guest
->code
, kSecCSDefaultFlags
, secRequirementRef
);
168 if (status
!= errSecSuccess
) {
169 secdebug("SecurityAgentXPCQuery", "code requirement check failed (%d)", (int32_t)status
);
171 guest
->appleSigned
= true;
173 guest
->checkedSignature
= true;
175 CFRelease(secRequirementRef
);
177 return guest
->appleSigned
;
184 // Bonus function: get the path out of a SecCodeRef
186 std::string
codePath(SecStaticCodeRef code
)
188 CFRef
<CFURLRef
> path
;
189 MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref()));
190 return cfString(path
);
195 // Debug dump support
197 #if defined(DEBUGDUMP)
199 static void dumpCode(SecCodeRef code
)
201 CFRef
<CFURLRef
> path
;
202 if (OSStatus rc
= SecCodeCopyPath(code
, kSecCSDefaultFlags
, &path
.aref()))
203 Debug::dump("unknown(rc=%d)", int32_t(rc
));
205 Debug::dump("%s", cfString(path
).c_str());
208 void ClientIdentification::dump()
210 Debug::dump(" client=");
211 dumpCode(mClientProcess
);
212 for (GuestMap::const_iterator it
= mGuests
.begin(); it
!= mGuests
.end(); ++it
) {
213 Debug::dump(" guest(0x%x)=", it
->first
);
214 dumpCode(it
->second
.code
);
215 if (it
->second
.gotHash
)
216 Debug::dump(" [got hash]");