]> git.saurik.com Git - apple/security.git/blob - Security/sec/Security/SecCertificatePath.c
f634500cba11e4f70aba8cf0b696a96b9cc3f57d
[apple/security.git] / Security / 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/oids.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 SecCertificatePathCopyDescription(CFTypeRef cf) {
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);
377 check(ix >= 0 && ix < certificatePath->count);
378 return certificatePath->certificates[ix];
379 }
380
381 CFIndex SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path,
382 SecCertificateRef certificate) {
383 CFIndex ix, count = path->count;
384 for (ix = 0; ix < count; ++ix) {
385 if (CFEqual(path->certificates[ix], certificate))
386 return ix;
387 }
388 return kCFNotFound;
389 }
390
391 #if 0
392 /* Return the leaf certificate for certificatePath. */
393 SecCertificateRef SecCertificatePathGetLeaf(
394 SecCertificatePathRef certificatePath) {
395 return SecCertificatePathGetCertificateAtIndex(certificatePath, 0);
396 }
397 #endif
398
399 /* Return the root certificate for certificatePath. Note that root is just
400 the top of the path as far as it is constructed. It may or may not be
401 trusted or self signed. */
402 SecCertificateRef SecCertificatePathGetRoot(
403 SecCertificatePathRef certificatePath) {
404 return SecCertificatePathGetCertificateAtIndex(certificatePath,
405 SecCertificatePathGetCount(certificatePath) - 1);
406 }
407
408 SecKeyRef SecCertificatePathCopyPublicKeyAtIndex(
409 SecCertificatePathRef certificatePath, CFIndex ix) {
410 SecCertificateRef certificate =
411 SecCertificatePathGetCertificateAtIndex(certificatePath, ix);
412 const DERAlgorithmId *algId =
413 SecCertificateGetPublicKeyAlgorithm(certificate);
414 const DERItem *params = NULL;
415 if (algId->params.length != 0) {
416 params = &algId->params;
417 } else {
418 CFIndex count = certificatePath->count;
419 for (++ix; ix < count; ++ix) {
420 certificate = certificatePath->certificates[ix];
421 const DERAlgorithmId *chain_algId =
422 SecCertificateGetPublicKeyAlgorithm(certificate);
423 if (!DEROidCompare(&algId->oid, &chain_algId->oid)) {
424 /* Algorithm oids differ, params stay NULL. */
425 break;
426 }
427 if (chain_algId->params.length != 0) {
428 params = &chain_algId->params;
429 break;
430 }
431 }
432 }
433 const DERItem *keyData = SecCertificateGetPublicKeyData(certificate);
434 SecAsn1Oid oid1 = { .Data = algId->oid.data, .Length = algId->oid.length };
435 SecAsn1Item params1 = {
436 .Data = params ? params->data : NULL,
437 .Length = params ? params->length : 0
438 };
439 SecAsn1Item keyData1 = {
440 .Data = keyData ? keyData->data : NULL,
441 .Length = keyData ? keyData->length : 0
442 };
443 return SecKeyCreatePublicFromDER(kCFAllocatorDefault, &oid1, &params1,
444 &keyData1);
445 }
446
447 SecPathVerifyStatus SecCertificatePathVerify(
448 SecCertificatePathRef certificatePath) {
449 check(certificatePath);
450 if (!certificatePath)
451 return kSecPathVerifyFailed;
452 for (;
453 certificatePath->lastVerifiedSigner < certificatePath->count - 1;
454 ++certificatePath->lastVerifiedSigner) {
455 SecKeyRef issuerKey =
456 SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
457 certificatePath->lastVerifiedSigner + 1);
458 if (!issuerKey)
459 return kSecPathVerifiesUnknown;
460 OSStatus status = SecCertificateIsSignedBy(
461 certificatePath->certificates[certificatePath->lastVerifiedSigner],
462 issuerKey);
463 CFRelease(issuerKey);
464 if (status) {
465 return kSecPathVerifyFailed;
466 }
467 }
468
469 if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) {
470 SecKeyRef issuerKey =
471 SecCertificatePathCopyPublicKeyAtIndex(certificatePath,
472 certificatePath->selfIssued);
473 if (!issuerKey) {
474 certificatePath->selfIssued = -1;
475 } else {
476 OSStatus status = SecCertificateIsSignedBy(
477 certificatePath->certificates[certificatePath->selfIssued],
478 issuerKey);
479 CFRelease(issuerKey);
480 if (!status) {
481 certificatePath->isSelfSigned = true;
482 } else {
483 certificatePath->selfIssued = -1;
484 }
485 }
486 }
487
488 return kSecPathVerifySuccess;
489 }
490
491 /* Return a score for this certificate chain. */
492 CFIndex SecCertificatePathScore(
493 SecCertificatePathRef certificatePath, CFAbsoluteTime verifyTime) {
494 CFIndex score = 0;
495 if (certificatePath->isAnchored) {
496 /* Anchored paths for the win! */
497 score += 10000;
498 }
499
500 /* Score points for each certificate in the chain. */
501 score += 10 * certificatePath->count;
502
503 if (certificatePath->isSelfSigned) {
504 /* If there is a self signed certificate at the end ofthe chain we
505 count it as an extra certificate. If there is one in the middle
506 of the chain we count it for half. */
507 if (certificatePath->selfIssued == certificatePath->count - 1)
508 score += 10;
509 else
510 score += 5;
511 }
512
513 /* Paths that don't verify score terribly. */
514 if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
515 secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex,
516 certificatePath->lastVerifiedSigner, certificatePath->count);
517 score -= 100000;
518 }
519
520 /* Subtract 1 point for each not valid certificate, make sure we
521 subtract less than the amount we add per certificate, since
522 regardless of temporal validity we still prefer longer chains
523 to shorter ones. This distinction is just to ensure that when
524 everything else is equal we prefer the chain with the most
525 certificates that are valid at the given verifyTime. */
526 CFIndex ix;
527 for (ix = 0; ix < certificatePath->count - 1; ++ix) {
528 if (!SecCertificateIsValid(certificatePath->certificates[ix],
529 verifyTime))
530 score -= 1;
531 }
532
533 return score;
534 }