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