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