]> git.saurik.com Git - apple/security.git/blame - securityd/src/clientid.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / securityd / src / clientid.cpp
CommitLineData
d8f41ccd 1/*
fa7225c8
A
2 * Copyright (c) 2006-2009,2012,2016 Apple Inc. All Rights Reserved.
3 *
d8f41ccd 4 * @APPLE_LICENSE_HEADER_START@
fa7225c8 5 *
d8f41ccd
A
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.
fa7225c8 12 *
d8f41ccd
A
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.
fa7225c8 20 *
d8f41ccd
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23//
24// clientid - track and manage identity of securityd clients
25//
26#include "clientid.h"
27#include "server.h"
28#include <Security/SecCodePriv.h>
e3d460c9
A
29#include <Security/oidsattr.h>
30#include <Security/SecCertificatePriv.h>
d8f41ccd
A
31
32
33//
34// Constructing a ClientIdentification doesn't do much.
35// We're waiting for setup(), which should be called by the child class's
36// constructor.
37//
38ClientIdentification::ClientIdentification()
e3d460c9 39 : mGotPartitionId(false)
d8f41ccd
A
40{
41}
42
43
44//
45// Initialize the ClientIdentification.
46// This creates a process-level code object for the client.
47//
dbe77505 48void ClientIdentification::setup(Security::CommonCriteria::AuditToken const &audit)
d8f41ccd 49{
dbe77505
A
50 StLock<Mutex> _(mLock);
51 StLock<Mutex> __(mValidityCheckLock);
52
53 audit_token_t const token = audit.auditToken();
54 OSStatus rc = SecCodeCreateWithAuditToken(&token, kSecCSDefaultFlags, &mClientProcess.aref());
55
56 if (rc) {
57 secerror("could not get code for process %d: OSStatus=%d",
58 audit.pid(), int32_t(rc));
59 }
60 mGuests.erase(mGuests.begin(), mGuests.end());
d8f41ccd
A
61}
62
63
64//
65// Return a SecCodeRef for the client process itself, regardless of
66// which guest within it is currently active.
67// Think twice before using this.
68//
69SecCodeRef ClientIdentification::processCode() const
70{
71 return mClientProcess;
72}
73
74
75//
76// Return a SecCodeRef for the currently active guest within the client
77// process.
78//
79// We make a fair effort to cache client guest identities without over-growing
80// the cache. Note that there's currently no protocol for being notified of
81// a guest's death or disappearance (independent from the host process's death),
82// so we couldn't track guests live even if we tried.
83//
84// Note that this consults Server::connection for the currently serviced
85// Connection object, so this is not entirely a function of ClientIdentification state.
86//
87SecCodeRef ClientIdentification::currentGuest() const
88{
89 if (GuestState *guest = current())
90 return guest->code;
91 else
92 return mClientProcess;
93}
94
95ClientIdentification::GuestState *ClientIdentification::current() const
96{
97 // if we have no client identification, we can't find a current guest either
98 if (!processCode())
99 return NULL;
100
101 SecGuestRef guestRef = Server::connection().guestRef();
fa7225c8 102
d8f41ccd
A
103 // try to deliver an already-cached entry
104 {
105 StLock<Mutex> _(mLock);
106 GuestMap::iterator it = mGuests.find(guestRef);
107 if (it != mGuests.end())
108 return &it->second;
109 }
110
111 // okay, make a new one (this may take a while)
112 CFRef<CFDictionaryRef> attributes = (guestRef == kSecNoGuest)
113 ? NULL
114 : makeCFDictionary(1, kSecGuestAttributeCanonical, CFTempNumber(guestRef).get());
115 Server::active().longTermActivity();
116 CFRef<SecCodeRef> code;
117 switch (OSStatus rc = SecCodeCopyGuestWithAttributes(processCode(),
118 attributes, kSecCSDefaultFlags, &code.aref())) {
119 case noErr:
120 break;
121 case errSecCSUnsigned: // not signed; clearly not a host
122 case errSecCSNotAHost: // signed but not marked as a (potential) host
123 code = mClientProcess;
124 break;
125 case errSecCSNoSuchCode: // potential host, but...
126 if (guestRef == kSecNoGuest) { // ... no guests (yet), so return the process
127 code = mClientProcess;
128 break;
129 }
130 // else fall through // ... the guest we expected to be there isn't
131 default:
132 MacOSError::throwMe(rc);
133 }
134 StLock<Mutex> _(mLock);
135 GuestState &slot = mGuests[guestRef];
136 if (!slot.code) // if another thread didn't get here first...
137 slot.code = code;
138 return &slot;
139}
140
141
e3d460c9
A
142//
143// Return the partition id ascribed to this client.
144// This is assigned to the whole client process - it is not per-guest.
145//
146std::string ClientIdentification::partitionId() const
147{
148 if (!mGotPartitionId) {
fa7225c8 149 StLock<Mutex> _(mValidityCheckLock);
e3d460c9
A
150 mClientPartitionId = partitionIdForProcess(processCode());
151 mGotPartitionId = true;
152 }
153 return mClientPartitionId;
154}
155
156
157static std::string hashString(CFDataRef data)
158{
159 CFIndex length = CFDataGetLength(data);
160 const unsigned char *hash = CFDataGetBytePtr(data);
161 char s[2 * length + 1];
162 for (CFIndex n = 0; n < length; n++)
163 sprintf(&s[2*n], "%2.2x", hash[n]);
164 return s;
165}
166
167
168std::string ClientIdentification::partitionIdForProcess(SecStaticCodeRef code)
169{
170 static CFStringRef const appleReq = CFSTR("anchor apple");
171 static CFStringRef const masReq = CFSTR("anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]");
79b9da22
A
172 static CFStringRef const developmentOrDevIDReq = CFSTR("anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13]" // Developer ID CA and Leaf
173 " or "
174 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] and certificate leaf[field.1.2.840.113635.100.6.1.12]" // WWDR CA and Mac Development Leaf
175 " or "
176 "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] and certificate leaf[field.1.2.840.113635.100.6.1.7]"); // WWDR CA and Mac Distribution Leaf
e3d460c9
A
177 static SecRequirementRef apple;
178 static SecRequirementRef mas;
179 static SecRequirementRef developmentOrDevID;
180 static dispatch_once_t onceToken;
181 dispatch_once(&onceToken, ^{
182 if (noErr != SecRequirementCreateWithString(appleReq, kSecCSDefaultFlags, &apple)
183 || noErr != SecRequirementCreateWithString(masReq, kSecCSDefaultFlags, &mas)
184 || noErr != SecRequirementCreateWithString(developmentOrDevIDReq, kSecCSDefaultFlags, &developmentOrDevID))
185 abort();
186 });
fa7225c8 187
e3d460c9
A
188 OSStatus rc;
189 switch (rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, apple)) {
190 case noErr:
191 case errSecCSReqFailed:
192 break;
193 case errSecCSUnsigned:
194 return "unsigned:";
195 default:
196 MacOSError::throwMe(rc);
197 }
198 CFRef<CFDictionaryRef> info;
199 if (OSStatus irc = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()))
200 MacOSError::throwMe(irc);
fa7225c8 201
e3d460c9 202 if (rc == noErr) {
fa7225c8
A
203 // for apple-signed code, make it canonical apple
204 if (CFEqual(CFDictionaryGetValue(info, kSecCodeInfoIdentifier), CFSTR("com.apple.security"))) {
e3d460c9 205 return "apple-tool:"; // take security(1) into a separate partition so it can't automatically peek into Apple's own
fa7225c8 206 } else {
e3d460c9 207 return "apple:";
fa7225c8 208 }
e3d460c9
A
209 } else if (noErr == SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, mas)) {
210 // for MAS-signed code, we take the embedded team identifier (verified by Apple)
211 return "teamid:" + cfString(CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier)));
212 } else if (noErr == SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, developmentOrDevID)) {
213 // for developer-signed code, we take the team identifier from the signing certificate's OU field
214 CFRef<CFDictionaryRef> info;
215 if (noErr != (rc = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())))
216 MacOSError::throwMe(rc);
217 CFArrayRef certChain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates));
218 SecCertificateRef signingCert = SecCertificateRef(CFArrayGetValueAtIndex(certChain, 0));
219 CFRef<CFStringRef> ou;
220 SecCertificateCopySubjectComponent(signingCert, &CSSMOID_OrganizationalUnitName, &ou.aref());
221 return "teamid:" + cfString(ou);
222 } else {
223 // cannot positively classify this code, but it's signed
224 CFDataRef cdhashData = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
225 assert(cdhashData);
226 return "cdhash:" + hashString(cdhashData);
227 }
228}
229
230
d8f41ccd
A
231//
232// Support for the legacy hash identification mechanism.
233// The legacy machinery deals exclusively in terms of processes.
234// It knows nothing about guests and their identities.
235//
236string ClientIdentification::getPath() const
237{
238 assert(mClientProcess);
fa7225c8 239 StLock<Mutex> _(mValidityCheckLock);
d8f41ccd
A
240 return codePath(currentGuest());
241}
242
243const CssmData ClientIdentification::getHash() const
244{
245 if (GuestState *guest = current()) {
246 if (!guest->gotHash) {
247 RefPointer<OSXCode> clientCode = new OSXCodeWrap(guest->code);
248 OSXVerifier::makeLegacyHash(clientCode, guest->legacyHash);
249 guest->gotHash = true;
250 }
251 return CssmData::wrap(guest->legacyHash, SHA1::digestLength);
252 } else
253 return CssmData();
254}
255
fa7225c8
A
256AclSubject* ClientIdentification::copyAclSubject() const
257{
258 StLock<Mutex> _(mValidityCheckLock);
259 RefPointer<OSXCode> clientXCode = new OSXCodeWrap(currentGuest());
260 return new CodeSignatureAclSubject(OSXVerifier(clientXCode));
261}
262
263OSStatus ClientIdentification::copySigningInfo(SecCSFlags flags,
264 CFDictionaryRef *info) const
265{
266 StLock<Mutex> _(mValidityCheckLock);
267 return SecCodeCopySigningInformation(currentGuest(), flags, info);
268}
269
270OSStatus ClientIdentification::checkValidity(SecCSFlags flags,
271 SecRequirementRef requirement) const
272{
273 // Make sure more than one thread cannot be evaluating this code signature concurrently
274 StLock<Mutex> _(mValidityCheckLock);
275 return SecCodeCheckValidityWithErrors(currentGuest(), flags, requirement, NULL);
276}
277
6b200bc3 278bool ClientIdentification::checkAppleSigned() const
d8f41ccd 279{
fa7225c8
A
280 // This is the clownfish supported way to check for a Mac App Store or B&I signed build
281 static CFStringRef const requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])");
282 CFRef<SecRequirementRef> secRequirementRef = NULL;
283 OSStatus status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef.aref());
284 if (status == errSecSuccess) {
285 status = checkValidity(kSecCSDefaultFlags, secRequirementRef);
286 if (status != errSecSuccess) {
287 secnotice("clientid", "code requirement check failed (%d), client is not Apple-signed", (int32_t)status);
288 } else {
289 return true;
d8f41ccd 290 }
fa7225c8
A
291 }
292 return false;
d8f41ccd
A
293}
294
295
e3d460c9
A
296bool ClientIdentification::hasEntitlement(const char *name) const
297{
298 CFRef<CFDictionaryRef> info;
fa7225c8
A
299 {
300 StLock<Mutex> _(mValidityCheckLock);
301 MacOSError::check(SecCodeCopySigningInformation(processCode(), kSecCSDefaultFlags, &info.aref()));
302 }
e3d460c9
A
303 CFCopyRef<CFDictionaryRef> entitlements = (CFDictionaryRef)CFDictionaryGetValue(info, kSecCodeInfoEntitlementsDict);
304 if (entitlements && entitlements.is<CFDictionaryRef>()) {
305 CFTypeRef value = CFDictionaryGetValue(entitlements, CFTempString(name));
306 if (value && value != kCFBooleanFalse)
307 return true; // have entitlement, it's not <false/> - bypass partition construction
308 }
309 return false;
310}
311
312
d8f41ccd
A
313//
314// Bonus function: get the path out of a SecCodeRef
315//
316std::string codePath(SecStaticCodeRef code)
317{
318 CFRef<CFURLRef> path;
319 MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
320 return cfString(path);
321}
322
323
324//
325// Debug dump support
326//
327#if defined(DEBUGDUMP)
328
329static void dumpCode(SecCodeRef code)
330{
331 CFRef<CFURLRef> path;
332 if (OSStatus rc = SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()))
333 Debug::dump("unknown(rc=%d)", int32_t(rc));
334 else
335 Debug::dump("%s", cfString(path).c_str());
336}
337
338void ClientIdentification::dump()
339{
340 Debug::dump(" client=");
341 dumpCode(mClientProcess);
342 for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) {
343 Debug::dump(" guest(0x%x)=", it->first);
344 dumpCode(it->second.code);
345 if (it->second.gotHash)
346 Debug::dump(" [got hash]");
347 }
348}
349
350#endif //DEBUGDUMP