]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecCertificatePath.c
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / sec / Security / SecCertificatePath.c
1 /*
2 * Copyright (c) 2007-2010,2012-2014 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 /*
25 * SecCertificatePath.c - CoreFoundation based certificate path object
26 */
27
28 #include "SecCertificatePath.h"
29
30 #include <Security/SecTrust.h>
31 #include <Security/SecTrustStore.h>
32 #include <Security/SecItem.h>
33 #include <Security/SecCertificateInternal.h>
34 #include <Security/SecFramework.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CoreFoundation/CFRuntime.h>
37 #include <CoreFoundation/CFSet.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <CoreFoundation/CFArray.h>
41 #include <CoreFoundation/CFPropertyList.h>
42 #include <AssertMacros.h>
43 #include <stdbool.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <pthread.h>
47 #include <Security/SecBase.h>
48 #include "SecRSAKey.h"
49 #include <libDER/oidsPriv.h>
50 #include <utilities/debugging.h>
51 #include <Security/SecInternal.h>
52 #include <AssertMacros.h>
53 #include <utilities/SecCFError.h>
54 #include <utilities/SecCFWrappers.h>
55
56 // MARK: -
57 // MARK: SecCertificatePath
58 /********************************************************
59 ************* SecCertificatePath object ****************
60 ********************************************************/
61 struct SecCertificatePath {
62 CFRuntimeBase _base;
63 CFIndex count;
64
65 /* Index of next parent source to search for parents. */
66 CFIndex nextParentSource;
67
68 /* Index of last certificate in chain who's signature has been verified.
69 0 means nothing has been checked. 1 means the leaf has been verified
70 against it's issuer, etc. */
71 CFIndex lastVerifiedSigner;
72
73 /* Index of first self issued certificate in the chain. -1 mean there is
74 none. 0 means the leaf is self signed. */
75 CFIndex selfIssued;
76
77 /* True iff cert at index selfIssued does in fact self verify. */
78 bool isSelfSigned;
79
80 /* True if the root of this path is a trusted anchor.
81 FIXME get rid of this since it's a property of the evaluation, not a
82 static feature of a certificate path? */
83 bool isAnchored;
84 SecCertificateRef certificates[];
85 };
86
87 CFGiblisWithHashFor(SecCertificatePath)
88
89 static void SecCertificatePathDestroy(CFTypeRef cf) {
90 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
91 CFIndex ix;
92 for (ix = 0; ix < certificatePath->count; ++ix) {
93 CFRelease(certificatePath->certificates[ix]);
94 }
95 }
96
97 static Boolean SecCertificatePathCompare(CFTypeRef cf1, CFTypeRef cf2) {
98 SecCertificatePathRef cp1 = (SecCertificatePathRef) cf1;
99 SecCertificatePathRef cp2 = (SecCertificatePathRef) cf2;
100 if (cp1->count != cp2->count)
101 return false;
102 CFIndex ix;
103 for (ix = 0; ix < cp1->count; ++ix) {
104 if (!CFEqual(cp1->certificates[ix], cp2->certificates[ix]))
105 return false;
106 }
107
108 return true;
109 }
110
111 static CFHashCode SecCertificatePathHash(CFTypeRef cf) {
112 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
113 CFHashCode hashCode = 0;
114 // hashCode = 31 * SecCertificatePathGetTypeID();
115 CFIndex ix;
116 for (ix = 0; ix < certificatePath->count; ++ix) {
117 hashCode += CFHash(certificatePath->certificates[ix]);
118 }
119 return hashCode;
120 }
121
122 static CFStringRef SecCertificatePathCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
123 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf;
124 CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
125 CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf));
126 CFStringAppendFormat(desc, NULL,
127 CFSTR("<%@ lvs: %" PRIdCFIndex " certs: "), typeStr,
128 certificatePath->lastVerifiedSigner);
129 CFRelease(typeStr);
130 CFIndex ix;
131 for (ix = 0; ix < certificatePath->count; ++ix) {
132 if (ix > 0) {
133 CFStringAppend(desc, CFSTR(", "));
134 }
135 CFStringRef str = CFCopyDescription(certificatePath->certificates[ix]);
136 CFStringAppend(desc, str);
137 CFRelease(str);
138 }
139 CFStringAppend(desc, CFSTR(" >"));
140
141 return desc;
142 }
143
144 /* Create a new certificate path from an old one. */
145 SecCertificatePathRef SecCertificatePathCreate(SecCertificatePathRef path,
146 SecCertificateRef certificate) {
147 CFAllocatorRef allocator = kCFAllocatorDefault;
148 check(certificate);
149 CFIndex count;
150 CFIndex selfIssued, lastVerifiedSigner;
151 bool isSelfSigned;
152 if (path) {
153 count = path->count + 1;
154 lastVerifiedSigner = path->lastVerifiedSigner;
155 selfIssued = path->selfIssued;
156 isSelfSigned = path->isSelfSigned;
157 } else {
158 count = 1;
159 lastVerifiedSigner = 0;
160 selfIssued = -1;
161 isSelfSigned = false;
162 }
163
164 CFIndex size = sizeof(struct SecCertificatePath) +
165 count * sizeof(SecCertificateRef);
166 SecCertificatePathRef result =
167 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
168 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
169 if (!result)
170 return NULL;
171
172 result->count = count;
173 result->nextParentSource = 0;
174 result->lastVerifiedSigner = lastVerifiedSigner;
175 result->selfIssued = selfIssued;
176 result->isSelfSigned = isSelfSigned;
177 result->isAnchored = false;
178 CFIndex ix;
179 for (ix = 0; ix < count - 1; ++ix) {
180 result->certificates[ix] = path->certificates[ix];
181 CFRetain(result->certificates[ix]);
182 }
183 result->certificates[count - 1] = certificate;
184 CFRetainSafe(certificate);
185
186 return result;
187 }
188
189 /* Create a new certificate path from an xpc_array of data. */
190 SecCertificatePathRef SecCertificatePathCreateWithXPCArray(xpc_object_t xpc_path, CFErrorRef *error) {
191 SecCertificatePathRef result = NULL;
192 require_action_quiet(xpc_path, exit, SecError(errSecParam, error, CFSTR("xpc_path is NULL")));
193 require_action_quiet(xpc_get_type(xpc_path) == XPC_TYPE_ARRAY, exit, SecError(errSecDecode, error, CFSTR("xpc_path value is not an array")));
194 size_t count;
195 require_action_quiet(count = xpc_array_get_count(xpc_path), exit, SecError(errSecDecode, error, CFSTR("xpc_path array count == 0")));
196 size_t size = sizeof(struct SecCertificatePath) + count * sizeof(SecCertificateRef);
197 require_action_quiet(result = (SecCertificatePathRef)_CFRuntimeCreateInstance(kCFAllocatorDefault, SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0), exit, SecError(errSecDecode, error, CFSTR("_CFRuntimeCreateInstance returned NULL")));
198
199 result->count = count;
200 result->nextParentSource = 0;
201 result->lastVerifiedSigner = count;
202 result->selfIssued = -1;
203 result->isSelfSigned = false;
204 result->isAnchored = false;
205 size_t ix;
206 for (ix = 0; ix < count; ++ix) {
207 SecCertificateRef certificate = SecCertificateCreateWithXPCArrayAtIndex(xpc_path, ix, error);
208 if (certificate) {
209 result->certificates[ix] = certificate;
210 } else {
211 result->count = ix; // total allocated
212 CFReleaseNull(result);
213 break;
214 }
215 }
216
217 exit:
218 return result;
219 }
220
221 SecCertificatePathRef SecCertificatePathCopyFromParent(
222 SecCertificatePathRef path, CFIndex skipCount) {
223 CFAllocatorRef allocator = kCFAllocatorDefault;
224 CFIndex count;
225 CFIndex selfIssued, lastVerifiedSigner;
226 bool isSelfSigned;
227
228 /* Ensure we are at least returning a path of length 1. */
229 if (skipCount < 0 || path->count < 1 + skipCount)
230 return NULL;
231
232 count = path->count - skipCount;
233 lastVerifiedSigner = path->lastVerifiedSigner > skipCount
234 ? path->lastVerifiedSigner - skipCount : 0;
235 selfIssued = path->selfIssued >= skipCount
236 ? path->selfIssued - skipCount : -1;
237 isSelfSigned = path->selfIssued >= 0 ? path->isSelfSigned : false;
238
239 CFIndex size = sizeof(struct SecCertificatePath) +
240 count * sizeof(SecCertificateRef);
241 SecCertificatePathRef result =
242 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
243 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
244 if (!result)
245 return NULL;
246
247 result->count = count;
248 result->nextParentSource = 0;
249 result->lastVerifiedSigner = lastVerifiedSigner;
250 result->selfIssued = selfIssued;
251 result->isSelfSigned = isSelfSigned;
252 result->isAnchored = path->isAnchored;
253 CFIndex ix;
254 for (ix = 0; ix < count; ++ix) {
255 result->certificates[ix] = path->certificates[ix + skipCount];
256 CFRetain(result->certificates[ix]);
257 }
258
259 return result;
260 }
261
262 SecCertificatePathRef SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path,
263 SecCertificateRef leaf) {
264 CFAllocatorRef allocator = kCFAllocatorDefault;
265 CFIndex count;
266 CFIndex selfIssued, lastVerifiedSigner;
267 bool isSelfSigned;
268
269 /* First make sure the new leaf is signed by path's current leaf. */
270 SecKeyRef issuerKey = SecCertificatePathCopyPublicKeyAtIndex(path, 0);
271 if (!issuerKey)
272 return NULL;
273 OSStatus status = SecCertificateIsSignedBy(leaf, issuerKey);
274 CFRelease(issuerKey);
275 if (status)
276 return NULL;
277
278 count = path->count + 1;
279 lastVerifiedSigner = path->lastVerifiedSigner + 1;
280 selfIssued = path->selfIssued;
281 isSelfSigned = path->isSelfSigned;
282
283 CFIndex size = sizeof(struct SecCertificatePath) +
284 count * sizeof(SecCertificateRef);
285 SecCertificatePathRef result =
286 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator,
287 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0);
288 if (!result)
289 return NULL;
290
291 result->count = count;
292 result->nextParentSource = 0;
293 result->lastVerifiedSigner = lastVerifiedSigner;
294 result->selfIssued = selfIssued;
295 result->isSelfSigned = isSelfSigned;
296 result->isAnchored = path->isAnchored;
297 CFIndex ix;
298 for (ix = 1; ix < count; ++ix) {
299 result->certificates[ix] = path->certificates[ix - 1];
300 CFRetain(result->certificates[ix]);
301 }
302 result->certificates[0] = leaf;
303 CFRetain(leaf);
304
305 return result;
306 }
307
308 /* Create an array of CFDataRefs from a certificate path. */
309 xpc_object_t SecCertificatePathCopyXPCArray(SecCertificatePathRef path, CFErrorRef *error) {
310 xpc_object_t xpc_chain = NULL;
311 size_t ix, count = path->count;
312 require_action_quiet(xpc_chain = xpc_array_create(NULL, 0), exit, SecError(errSecParam, error, CFSTR("xpc_array_create failed")));
313 for (ix = 0; ix < count; ++ix) {
314 SecCertificateRef cert = SecCertificatePathGetCertificateAtIndex(path, ix);
315 if (!SecCertificateAppendToXPCArray(cert, xpc_chain, error)) {
316 xpc_release(xpc_chain);
317 return NULL;
318 }
319 }
320
321 exit:
322 return xpc_chain;
323 }
324
325 /* Record the fact that we found our own root cert as our parent
326 certificate. */
327 void SecCertificatePathSetSelfIssued(
328 SecCertificatePathRef certificatePath) {
329 if (certificatePath->selfIssued >= 0) {
330 secdebug("trust", "%@ is already issued at %" PRIdCFIndex, certificatePath,
331 certificatePath->selfIssued);
332 return;
333 }
334 secdebug("trust", "%@ is self issued", certificatePath);
335 certificatePath->selfIssued = certificatePath->count - 1;
336 }
337
338 void SecCertificatePathSetIsAnchored(
339 SecCertificatePathRef certificatePath) {
340 secdebug("trust", "%@ is anchored", certificatePath);
341 certificatePath->isAnchored = true;
342 }
343
344 /* Return the index of the first non anchor certificate in the chain that is
345 self signed counting from the leaf up. Return -1 if there is none. */
346 CFIndex SecCertificatePathSelfSignedIndex(
347 SecCertificatePathRef certificatePath) {
348 if (certificatePath->isSelfSigned)
349 return certificatePath->selfIssued;
350 return -1;
351 }
352
353 Boolean SecCertificatePathIsAnchored(
354 SecCertificatePathRef certificatePath) {
355 return certificatePath->isAnchored;
356 }
357
358 void SecCertificatePathSetNextSourceIndex(
359 SecCertificatePathRef certificatePath, CFIndex sourceIndex) {
360 certificatePath->nextParentSource = sourceIndex;
361 }
362
363 CFIndex SecCertificatePathGetNextSourceIndex(
364 SecCertificatePathRef certificatePath) {
365 return certificatePath->nextParentSource;
366 }
367
368 CFIndex SecCertificatePathGetCount(
369 SecCertificatePathRef certificatePath) {
370 check(certificatePath);
371 return certificatePath ? certificatePath->count : 0;
372 }
373
374 SecCertificateRef SecCertificatePathGetCertificateAtIndex(
375 SecCertificatePathRef certificatePath, CFIndex ix) {
376 check(certificatePath && ix >= 0 && ix < certificatePath->count);
377 return certificatePath->certificates[ix];
378 }
379
380 CFIndex SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path,
381 SecCertificateRef certificate) {
382 CFIndex ix, count = path->count;
383 for (ix = 0; ix < count; ++ix) {
384 if (CFEqual(path->certificates[ix], certificate))
385 return ix;
386 }
387 return kCFNotFound;
388 }
389
390 #if 0
391 /* Return the leaf certificate for certificatePath. */
392 SecCertificateRef SecCertificatePathGetLeaf(
393 SecCertificatePathRef certificatePath) {
394 return SecCertificatePathGetCertificateAtIndex(certificatePath, 0);
395 }
396 #endif
397
398 /* Return the root certificate for certificatePath. Note that root is just
399 the top of the path as far as it is constructed. It may or may not be
400 trusted or self signed. */
401 SecCertificateRef SecCertificatePathGetRoot(
402 SecCertificatePathRef certificatePath) {
403 return SecCertificatePathGetCertificateAtIndex(certificatePath,
404 SecCertificatePathGetCount(certificatePath) - 1);
405 }
406
407 SecKeyRef SecCertificatePathCopyPublicKeyAtIndex(
408 SecCertificatePathRef certificatePath, CFIndex ix) {
409 SecCertificateRef certificate =
410 SecCertificatePathGetCertificateAtIndex(certificatePath, ix);
411 const DERAlgorithmId *algId =
412 SecCertificateGetPublicKeyAlgorithm(certificate);
413 const DERItem *params = NULL;
414 if (algId->params.length != 0) {
415 params = &algId->params;
416 } else {
417 CFIndex count = certificatePath->count;
418 for (++ix; ix < count; ++ix) {
419 certificate = certificatePath->certificates[ix];
420 const DERAlgorithmId *chain_algId =
421 SecCertificateGetPublicKeyAlgorithm(certificate);
422 if (!DEROidCompare(&algId->oid, &chain_algId->oid)) {
423 /* Algorithm oids differ, params stay NULL. */
424 break;
425 }
426 if (chain_algId->params.length != 0) {
427 params = &chain_algId->params;
428 break;
429 }
430 }
431 }
432 const DERItem *keyData = SecCertificateGetPublicKeyData(certificate);
433 SecAsn1Oid oid1 = { .Data = algId->oid.data, .Length = algId->oid.length };
434 SecAsn1Item params1 = {
435 .Data = params ? params->data : NULL,
436 .Length = params ? params->length : 0
437 };
438 SecAsn1Item keyData1 = {
439 .Data = keyData ? keyData->data : NULL,
440 .Length = keyData ? keyData->length : 0
441 };
442 return SecKeyCreatePublicFromDER(kCFAllocatorDefault, &oid1, &params1,
443 &keyData1);
444 }
445
446 SecPathVerifyStatus SecCertificatePathVerify(
447 SecCertificatePathRef certificatePath) {
448 check(certificatePath);
449 if (!certificatePath)
450 return kSecPathVerifyFailed;
451 for (;
452 certificatePath->lastVerifiedSigner < certificatePath->count - 1;
453 ++certificatePath->lastVerifiedSigner) {
454 SecKeyRef issuerKey =
455 SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
456 certificatePath->lastVerifiedSigner + 1);
457 if (!issuerKey)
458 return kSecPathVerifiesUnknown;
459 OSStatus status = SecCertificateIsSignedBy(
460 certificatePath->certificates[certificatePath->lastVerifiedSigner],
461 issuerKey);
462 CFRelease(issuerKey);
463 if (status) {
464 return kSecPathVerifyFailed;
465 }
466 }
467
468 if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) {
469 SecKeyRef issuerKey =
470 SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
471 certificatePath->selfIssued);
472 if (!issuerKey) {
473 certificatePath->selfIssued = -1;
474 } else {
475 OSStatus status = SecCertificateIsSignedBy(
476 certificatePath->certificates[certificatePath->selfIssued],
477 issuerKey);
478 CFRelease(issuerKey);
479 if (!status) {
480 certificatePath->isSelfSigned = true;
481 } else {
482 certificatePath->selfIssued = -1;
483 }
484 }
485 }
486
487 return kSecPathVerifySuccess;
488 }
489
490 /* Return a score for this certificate chain. */
491 CFIndex SecCertificatePathScore(
492 SecCertificatePathRef certificatePath, CFAbsoluteTime verifyTime) {
493 CFIndex score = 0;
494 if (certificatePath->isAnchored) {
495 /* Anchored paths for the win! */
496 score += 10000;
497 }
498
499 /* Score points for each certificate in the chain. */
500 score += 10 * certificatePath->count;
501
502 if (certificatePath->isSelfSigned) {
503 /* If there is a self signed certificate at the end ofthe chain we
504 count it as an extra certificate. If there is one in the middle
505 of the chain we count it for half. */
506 if (certificatePath->selfIssued == certificatePath->count - 1)
507 score += 10;
508 else
509 score += 5;
510 }
511
512 /* Paths that don't verify score terribly. */
513 if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
514 secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex,
515 certificatePath->lastVerifiedSigner, certificatePath->count);
516 score -= 100000;
517 }
518
519 /* Subtract 1 point for each not valid certificate, make sure we
520 subtract less than the amount we add per certificate, since
521 regardless of temporal validity we still prefer longer chains
522 to shorter ones. This distinction is just to ensure that when
523 everything else is equal we prefer the chain with the most
524 certificates that are valid at the given verifyTime. */
525 CFIndex ix;
526 for (ix = 0; ix < certificatePath->count - 1; ++ix) {
527 if (!SecCertificateIsValid(certificatePath->certificates[ix],
528 verifyTime))
529 score -= 1;
530 }
531
532 return score;
533 }