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