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 ********************************************************/
60struct 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. */
87static pthread_once_t kSecCertificatePathRegisterClass = PTHREAD_ONCE_INIT;
88static CFTypeID kSecCertificatePathTypeID = _kCFRuntimeNotATypeID;
89
90static 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
98static 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
112static 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
123static 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
145static 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. */
163CFTypeID SecCertificatePathGetTypeID(void) {
164    pthread_once(&kSecCertificatePathRegisterClass,
165		SecCertificatePathRegisterClass);
166    return kSecCertificatePathTypeID;
167}
168
169/* Create a new certificate path from an old one. */
170SecCertificatePathRef 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. */
215SecCertificatePathRef 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
242exit:
243    return result;
244}
245
246SecCertificatePathRef 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
287SecCertificatePathRef 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. */
334xpc_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
346exit:
347    return xpc_chain;
348}
349
350/* Record the fact that we found our own root cert as our parent
351   certificate. */
352void 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
363void 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. */
371CFIndex SecCertificatePathSelfSignedIndex(
372	SecCertificatePathRef certificatePath) {
373	if (certificatePath->isSelfSigned)
374		return certificatePath->selfIssued;
375	return -1;
376}
377
378Boolean SecCertificatePathIsAnchored(
379	SecCertificatePathRef certificatePath) {
380	return certificatePath->isAnchored;
381}
382
383void SecCertificatePathSetNextSourceIndex(
384	SecCertificatePathRef certificatePath, CFIndex sourceIndex) {
385	certificatePath->nextParentSource = sourceIndex;
386}
387
388CFIndex SecCertificatePathGetNextSourceIndex(
389	SecCertificatePathRef certificatePath) {
390	return certificatePath->nextParentSource;
391}
392
393CFIndex SecCertificatePathGetCount(
394	SecCertificatePathRef certificatePath) {
395	check(certificatePath);
396	return certificatePath ? certificatePath->count : 0;
397}
398
399SecCertificateRef SecCertificatePathGetCertificateAtIndex(
400	SecCertificatePathRef certificatePath, CFIndex ix) {
401	check(certificatePath);
402	check(ix >= 0 && ix < certificatePath->count);
403	return certificatePath->certificates[ix];
404}
405
406CFIndex 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. */
418SecCertificateRef 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.  */
427SecCertificateRef SecCertificatePathGetRoot(
428	SecCertificatePathRef certificatePath) {
429	return SecCertificatePathGetCertificateAtIndex(certificatePath,
430		SecCertificatePathGetCount(certificatePath) - 1);
431}
432
433SecKeyRef 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
472SecPathVerifyStatus 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. */
517CFIndex 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}
560