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