1/*
2 * Copyright (c) 2000-2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19/*
20	policies.cpp - TP module policy implementation
21*/
22
23#include <Security/cssmtype.h>
24#include <Security/cssmapi.h>
25#include "tpPolicies.h"
26#include <Security/cssmerr.h>
27#include "tpdebugging.h"
28#include "certGroupUtils.h"
29#include <Security/x509defs.h>
30#include <Security/oidsalg.h>
31#include <Security/oidsattr.h>
32#include <Security/oidscert.h>
33#include <Security/certextensions.h>
34#include <Security/cssmapple.h>
35#include <Security/SecCertificate.h>
36#include <Security/SecCertificatePriv.h>
37#include <string.h>
38#include <ctype.h>
39#include <assert.h>
40#include <CoreFoundation/CFString.h>
41#include <CommonCrypto/CommonDigest.h>
42
43#pragma clang diagnostic push
44#pragma clang diagnostic ignored "-Wunused-const-variable"
45
46/*
47 * Our private per-extension info. One of these per (understood) extension per
48 * cert.
49 */
50typedef struct {
51	CSSM_BOOL		present;
52	CSSM_BOOL		critical;
53	CE_Data  		*extnData;		// mallocd by CL
54	CSSM_DATA		*valToFree;		// the data we pass to freeField()
55} iSignExtenInfo;
56
57/*
58 * Struct to keep track of info pertinent to one cert.
59 */
60typedef struct {
61
62	/* extensions we're interested in */
63	iSignExtenInfo		authorityId;
64	iSignExtenInfo		subjectId;
65	iSignExtenInfo		keyUsage;
66	iSignExtenInfo		extendKeyUsage;
67	iSignExtenInfo		basicConstraints;
68	iSignExtenInfo		netscapeCertType;
69	iSignExtenInfo		subjectAltName;
70	iSignExtenInfo		certPolicies;
71	iSignExtenInfo		qualCertStatements;
72	iSignExtenInfo		nameConstraints;
73	iSignExtenInfo		policyMappings;
74	iSignExtenInfo		policyConstraints;
75	iSignExtenInfo		inhibitAnyPolicy;
76	iSignExtenInfo		certificatePolicies;
77
78	/* flag indicating presence of CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING */
79	CSSM_BOOL			foundPassbookSigning;
80	/* flag indicating presence of CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE */
81	CSSM_BOOL			foundAppleSysInt2Marker;
82	/* flag indicating presence of CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION */
83	CSSM_BOOL			foundAppleServerAuthMarker;
84	/* flag indicating presence of CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE */
85	CSSM_BOOL			foundEscrowServiceMarker;
86	/* flag indicating presence of a critical extension we don't understand */
87	CSSM_BOOL			foundUnknownCritical;
88	/* flag indicating that this certificate was signed with a known-broken algorithm */
89	CSSM_BOOL			untrustedSigAlg;
90
91} iSignCertInfo;
92
93/*
94 * The list of Qualified Cert Statement statementIds we understand, even though
95 * we don't actually do anything with them; if these are found in a Qualified
96 * Cert Statement that's critical, we can truthfully say "yes we understand this".
97 */
98static const CSSM_OID_PTR knownQualifiedCertStatements[] =
99{
100	(const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V1,
101	(const CSSM_OID_PTR)&CSSMOID_OID_QCS_SYNTAX_V2,
102	(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_COMPLIANCE,
103	(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE,
104	(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_RETENTION,
105	(const CSSM_OID_PTR)&CSSMOID_ETSI_QCS_QC_SSCD
106};
107#define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
108
109static CSSM_RETURN tp_verifyMacAppStoreReceiptOpts(TPCertGroup &certGroup,
110	const CSSM_DATA *fieldOpts,	const iSignCertInfo *certInfo);
111
112static CSSM_RETURN tp_verifyPassbookSigningOpts(TPCertGroup &certGroup,
113	const CSSM_DATA *fieldOpts, const iSignCertInfo *certInfo);
114
115bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind);
116
117#define kSecPolicySHA1Size 20
118static const UInt8 kAppleCASHA1[kSecPolicySHA1Size] = {
119    0x61, 0x1E, 0x5B, 0x66, 0x2C, 0x59, 0x3A, 0x08, 0xFF, 0x58,
120    0xD1, 0x4A, 0xE2, 0x24, 0x52, 0xD1, 0x98, 0xDF, 0x6C, 0x60
121};
122
123static const UInt8 kMobileRootSHA1[kSecPolicySHA1Size] =  {
124    0xBD, 0xD6, 0x7C, 0x34, 0xD0, 0xB2, 0x68, 0x5D, 0x31, 0x82,
125    0xCD, 0x32, 0xCB, 0xF4, 0x54, 0x69, 0xA1, 0xF1, 0x6B, 0x09
126};
127
128static const UInt8 kAppleCorpCASHA1[kSecPolicySHA1Size] = {
129    0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
130    0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
131};
132
133/*
134 * Certificate policy OIDs
135 */
136
137/* 2.5.29.32.0 */
138#define ANY_POLICY_OID				OID_EXTENSION, 0x32, 0x00
139#define ANY_POLICY_OID_LEN			OID_EXTENSION_LENGTH + 2
140
141/* 2.5.29.54 */
142#define INHIBIT_ANY_POLICY_OID		OID_EXTENSION, 0x54
143#define INHIBIT_ANY_POLICY_OID_LEN	OID_EXTENSION_LENGTH + 1
144
145/* 2.16.840.1.101.2.1 */
146#define US_DOD_INFOSEC				0x60, 0x86, 0x48, 0x01, 0x65, 0x02, 0x01
147#define US_DOD_INFOSEC_LEN			7
148
149/* 2.16.840.1.101.2.1.11.10 */
150#define PIV_AUTH_OID				US_DOD_INFOSEC, 0x0B, 0x0A
151#define PIV_AUTH_OID_LEN			US_DOD_INFOSEC_LEN + 2
152
153/* 2.16.840.1.101.2.1.11.20 */
154#define PIV_AUTH_2048_OID			US_DOD_INFOSEC, 0x0B, 0x14
155#define PIV_AUTH_2048_OID_LEN		US_DOD_INFOSEC_LEN + 2
156
157static const uint8 	OID_ANY_POLICY[] = {ANY_POLICY_OID};
158const CSSM_OID	CSSMOID_ANY_POLICY   = {ANY_POLICY_OID_LEN, (uint8 *)OID_ANY_POLICY};
159static const uint8 	OID_INHIBIT_ANY_POLICY[] = {INHIBIT_ANY_POLICY_OID};
160const CSSM_OID	CSSMOID_INHIBIT_ANY_POLICY   = {INHIBIT_ANY_POLICY_OID_LEN, (uint8 *)OID_INHIBIT_ANY_POLICY};
161static const uint8 	OID_PIV_AUTH[] = {PIV_AUTH_OID};
162const CSSM_OID	CSSMOID_PIV_AUTH   = {PIV_AUTH_OID_LEN, (uint8 *)OID_PIV_AUTH};
163static const uint8 	OID_PIV_AUTH_2048[] = {PIV_AUTH_2048_OID};
164const CSSM_OID	CSSMOID_PIV_AUTH_2048   = {PIV_AUTH_2048_OID_LEN, (uint8 *)OID_PIV_AUTH_2048};
165
166static CSSM_RETURN tp_verifyAppleIDSharingOpts(TPCertGroup &certGroup,
167												const CSSM_DATA *fieldOpts,			// optional Common Name
168												const iSignCertInfo *certInfo);
169/*
170 * Setup a single iSignExtenInfo. Called once per known extension
171 * per cert.
172 */
173static CSSM_RETURN tpSetupExtension(
174	Allocator			&alloc,
175	CSSM_DATA 			*extnData,
176	iSignExtenInfo		*extnInfo)		// which component of certInfo
177{
178	if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) {
179		tpPolicyError("tpSetupExtension: malformed CSSM_FIELD");
180		return CSSMERR_TP_UNKNOWN_FORMAT;
181	}
182	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data;
183	extnInfo->present   = CSSM_TRUE;
184	extnInfo->critical  = cssmExt->critical;
185	extnInfo->extnData  = (CE_Data *)cssmExt->value.parsedValue;
186	extnInfo->valToFree = extnData;
187	return CSSM_OK;
188}
189
190/*
191 * Fetch a known extension, set up associated iSignExtenInfo if present.
192 */
193static CSSM_RETURN iSignFetchExtension(
194	Allocator			&alloc,
195	TPCertInfo			*tpCert,
196	const CSSM_OID		*fieldOid,		// which extension to fetch
197	iSignExtenInfo		*extnInfo)		// where the info goes
198{
199	CSSM_DATA_PTR	fieldValue;			// mallocd by CL
200	CSSM_RETURN		crtn;
201
202	crtn = tpCert->fetchField(fieldOid, &fieldValue);
203	switch(crtn) {
204		case CSSM_OK:
205			break;
206		case CSSMERR_CL_NO_FIELD_VALUES:
207			/* field not present, OK */
208			return CSSM_OK;
209		default:
210			return crtn;
211	}
212	return tpSetupExtension(alloc,
213			fieldValue,
214			extnInfo);
215}
216
217/*
218 * This function performs a check of an extension marked 'critical'
219 * to see if it's one we understand. Returns CSSM_OK if the extension
220 * is acceptable, CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN if unknown.
221 */
222static CSSM_RETURN iSignVerifyCriticalExtension(
223	CSSM_X509_EXTENSION	*cssmExt)
224{
225	if (!cssmExt || !cssmExt->extnId.Data)
226		return CSSMERR_TP_INVALID_FIELD_POINTER;
227
228	if (!cssmExt->critical)
229		return CSSM_OK;
230
231	/* FIXME: remove when policyConstraints NSS template is fixed */
232	if (!memcmp(cssmExt->extnId.Data, CSSMOID_PolicyConstraints.Data, CSSMOID_PolicyConstraints.Length))
233		return CSSM_OK;
234
235	if (cssmExt->extnId.Length > APPLE_EXTENSION_OID_LENGTH &&
236		!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION.Data, APPLE_EXTENSION_OID_LENGTH)) {
237		/* This extension's OID is under the appleCertificateExtensions arc */
238		return CSSM_OK;
239
240	}
241	return CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN;
242}
243
244/*
245 * Search for all unknown extensions. If we find one which is flagged critical,
246 * flag certInfo->foundUnknownCritical. Only returns error on gross errors.
247 */
248static CSSM_RETURN iSignSearchUnknownExtensions(
249	TPCertInfo			*tpCert,
250	iSignCertInfo		*certInfo)
251{
252	CSSM_RETURN 	crtn;
253	CSSM_DATA_PTR 	fieldValue = NULL;
254	CSSM_HANDLE		searchHand = CSSM_INVALID_HANDLE;
255	uint32 			numFields = 0;
256
257	certInfo->foundPassbookSigning = CSSM_FALSE;
258	certInfo->foundAppleSysInt2Marker = CSSM_FALSE;
259	certInfo->foundEscrowServiceMarker = CSSM_FALSE;
260
261	crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(),
262		tpCert->cacheHand(),
263		&CSSMOID_X509V3CertificateExtensionCStruct,
264		&searchHand,
265		&numFields,
266		&fieldValue);
267	switch(crtn) {
268		case CSSM_OK:
269			/* found one, proceed */
270			break;
271		case CSSMERR_CL_NO_FIELD_VALUES:
272			/* no unknown extensions present, OK */
273			return CSSM_OK;
274		default:
275			return crtn;
276	}
277
278	if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
279		tpPolicyError("iSignSearchUnknownExtensions: malformed CSSM_FIELD");
280		return CSSMERR_TP_UNKNOWN_FORMAT;
281	}
282
283	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
284	if (cssmExt->extnId.Length == APPLE_EXTENSION_CODE_SIGNING_LENGTH+1 &&
285		!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING.Data,
286		APPLE_EXTENSION_CODE_SIGNING_LENGTH+1)) {
287		/* this is the Passbook Signing extension */
288		certInfo->foundPassbookSigning = CSSM_TRUE;
289	}
290	if (cssmExt->extnId.Length == APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH &&
291		!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE.Data,
292		 APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH)) {
293		/* this is the Apple System Integration 2 Signing extension */
294		certInfo->foundAppleSysInt2Marker = CSSM_TRUE;
295	}
296	if (cssmExt->extnId.Length == APPLE_EXTENSION_ESCROW_SERVICE_LENGTH &&
297		!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE.Data,
298		 APPLE_EXTENSION_ESCROW_SERVICE_LENGTH)) {
299		/* this is the Escrow Service Signing extension */
300		certInfo->foundEscrowServiceMarker = CSSM_TRUE;
301	}
302
303	if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
304		/* BRRZAPP! Found an unknown extension marked critical */
305		certInfo->foundUnknownCritical = CSSM_TRUE;
306		goto fini;
307	}
308	CSSM_CL_FreeFieldValue(tpCert->clHand(),
309		&CSSMOID_X509V3CertificateExtensionCStruct,
310		fieldValue);
311	fieldValue = NULL;
312
313	/* process remaining unknown extensions */
314	for(unsigned i=1; i<numFields; i++) {
315		crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(),
316			searchHand,
317			&fieldValue);
318		if(crtn) {
319			/* should never happen */
320			tpPolicyError("searchUnknownExtensions: GetNextCachedFieldValue"
321				"error");
322			break;
323		}
324		if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
325			tpPolicyError("iSignSearchUnknownExtensions: "
326				"malformed CSSM_FIELD");
327			crtn = CSSMERR_TP_UNKNOWN_FORMAT;
328			break;
329		}
330
331		CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
332		if (cssmExt->extnId.Length == APPLE_EXTENSION_CODE_SIGNING_LENGTH+1 &&
333				!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING.Data,
334					APPLE_EXTENSION_CODE_SIGNING_LENGTH+1)) {
335			/* this is the Passbook Signing extension */
336			certInfo->foundPassbookSigning = CSSM_TRUE;
337		}
338		if (cssmExt->extnId.Length == APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH &&
339				!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE.Data,
340					APPLE_EXTENSION_SYSINT2_INTERMEDIATE_LENGTH)) {
341			/* this is the Apple System Integration 2 Signing extension */
342			certInfo->foundAppleSysInt2Marker = CSSM_TRUE;
343		}
344		if (cssmExt->extnId.Length == APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH &&
345				!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_SERVER_AUTHENTICATION.Data,
346					APPLE_EXTENSION_SERVER_AUTHENTICATION_LENGTH)) {
347			/* this is the Apple Server Authentication extension */
348			certInfo->foundAppleServerAuthMarker = CSSM_TRUE;
349		}
350		if (cssmExt->extnId.Length == APPLE_EXTENSION_ESCROW_SERVICE_LENGTH &&
351				!memcmp(cssmExt->extnId.Data, CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE.Data,
352					APPLE_EXTENSION_ESCROW_SERVICE_LENGTH)) {
353			/* this is the Escrow Service Signing extension */
354			certInfo->foundEscrowServiceMarker = CSSM_TRUE;
355		}
356
357		if(iSignVerifyCriticalExtension(cssmExt) != CSSM_OK) {
358			/* BRRZAPP! Found an unknown extension marked critical */
359			certInfo->foundUnknownCritical = CSSM_TRUE;
360			break;
361		}
362		CSSM_CL_FreeFieldValue(tpCert->clHand(),
363			&CSSMOID_X509V3CertificateExtensionCStruct,
364			fieldValue);
365		fieldValue = NULL;
366	} /* for additional fields */
367
368fini:
369	if(fieldValue) {
370		CSSM_CL_FreeFieldValue(tpCert->clHand(),
371			&CSSMOID_X509V3CertificateExtensionCStruct,
372			fieldValue);
373	}
374	if(searchHand != CSSM_INVALID_HANDLE) {
375		CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand);
376	}
377	return crtn;
378}
379
380/*
381 * Check the signature algorithm. If it's known to be untrusted,
382 * flag certInfo->untrustedSigAlg.
383 */
384static void iSignCheckSignatureAlgorithm(
385	TPCertInfo			*tpCert,
386	iSignCertInfo		*certInfo)
387{
388	CSSM_X509_ALGORITHM_IDENTIFIER	*algId = NULL;
389	CSSM_DATA_PTR					valueToFree = NULL;
390
391	algId = tp_CertGetAlgId(tpCert, &valueToFree);
392	if(!algId ||
393		tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2) ||
394		tpCompareCssmData(&algId->algorithm, &CSSMOID_MD2WithRSA) ||
395		tpCompareCssmData(&algId->algorithm, &CSSMOID_MD5) ||
396		tpCompareCssmData(&algId->algorithm, &CSSMOID_MD5WithRSA) ) {
397		certInfo->untrustedSigAlg = CSSM_TRUE;
398	} else {
399		certInfo->untrustedSigAlg = CSSM_FALSE;
400	}
401
402	if (valueToFree) {
403		tp_CertFreeAlgId(tpCert->clHand(), valueToFree);
404	}
405}
406
407/*
408 * Given a TPCertInfo, fetch the associated iSignCertInfo fields.
409 * Returns CSSM_FAIL on error.
410 */
411static CSSM_RETURN iSignGetCertInfo(
412	Allocator			&alloc,
413	TPCertInfo			*tpCert,
414	iSignCertInfo		*certInfo)
415{
416	CSSM_RETURN			crtn;
417
418	/* first grind thru the extensions we're interested in */
419	crtn = iSignFetchExtension(alloc,
420		tpCert,
421		&CSSMOID_AuthorityKeyIdentifier,
422		&certInfo->authorityId);
423	if(crtn) {
424		return crtn;
425	}
426	crtn = iSignFetchExtension(alloc,
427		tpCert,
428		&CSSMOID_SubjectKeyIdentifier,
429		&certInfo->subjectId);
430	if(crtn) {
431		return crtn;
432	}
433	crtn = iSignFetchExtension(alloc,
434		tpCert,
435		&CSSMOID_KeyUsage,
436		&certInfo->keyUsage);
437	if(crtn) {
438		return crtn;
439	}
440	crtn = iSignFetchExtension(alloc,
441		tpCert,
442		&CSSMOID_ExtendedKeyUsage,
443		&certInfo->extendKeyUsage);
444	if(crtn) {
445		return crtn;
446	}
447	crtn = iSignFetchExtension(alloc,
448		tpCert,
449		&CSSMOID_BasicConstraints,
450		&certInfo->basicConstraints);
451	if(crtn) {
452		return crtn;
453	}
454	crtn = iSignFetchExtension(alloc,
455		tpCert,
456		&CSSMOID_NetscapeCertType,
457		&certInfo->netscapeCertType);
458	if(crtn) {
459		return crtn;
460	}
461	crtn = iSignFetchExtension(alloc,
462		tpCert,
463		&CSSMOID_SubjectAltName,
464		&certInfo->subjectAltName);
465	if(crtn) {
466		return crtn;
467	}
468	crtn = iSignFetchExtension(alloc,
469		tpCert,
470		&CSSMOID_CertificatePolicies,
471		&certInfo->certPolicies);
472	if(crtn) {
473		return crtn;
474	}
475	crtn = iSignFetchExtension(alloc,
476		tpCert,
477		&CSSMOID_QC_Statements,
478		&certInfo->qualCertStatements);
479	if(crtn) {
480		return crtn;
481	}
482	crtn = iSignFetchExtension(alloc,
483		tpCert,
484		&CSSMOID_NameConstraints,
485		&certInfo->nameConstraints);
486	if(crtn) {
487		return crtn;
488	}
489	crtn = iSignFetchExtension(alloc,
490		tpCert,
491		&CSSMOID_PolicyMappings,
492		&certInfo->policyMappings);
493	if(crtn) {
494		return crtn;
495	}
496	crtn = iSignFetchExtension(alloc,
497		tpCert,
498		&CSSMOID_PolicyConstraints,
499		&certInfo->policyConstraints);
500	if(crtn) {
501		return crtn;
502	}
503	crtn = iSignFetchExtension(alloc,
504		tpCert,
505		&CSSMOID_InhibitAnyPolicy,
506		&certInfo->inhibitAnyPolicy);
507	if(crtn) {
508		return crtn;
509	}
510	crtn = iSignFetchExtension(alloc,
511		tpCert,
512		&CSSMOID_CertificatePolicies,
513		&certInfo->certificatePolicies);
514	if(crtn) {
515		return crtn;
516	}
517
518	/* check signature algorithm field */
519	iSignCheckSignatureAlgorithm(tpCert, certInfo);
520
521	/* now look for extensions we don't understand - the only thing we're interested
522	 * in is the critical flag. */
523	return iSignSearchUnknownExtensions(tpCert, certInfo);
524}
525
526/*
527 * Free (via CL) the fields allocated in iSignGetCertInfo().
528 */
529static void iSignFreeCertInfo(
530	CSSM_CL_HANDLE	clHand,
531	iSignCertInfo	*certInfo)
532{
533	if(certInfo->authorityId.present) {
534		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier,
535			certInfo->authorityId.valToFree);
536	}
537	if(certInfo->subjectId.present) {
538		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier,
539			certInfo->subjectId.valToFree);
540	}
541	if(certInfo->keyUsage.present) {
542		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage,
543			certInfo->keyUsage.valToFree);
544	}
545	if(certInfo->extendKeyUsage.present) {
546		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage,
547			certInfo->extendKeyUsage.valToFree);
548	}
549	if(certInfo->basicConstraints.present) {
550		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints,
551			certInfo->basicConstraints.valToFree);
552	}
553	if(certInfo->netscapeCertType.present) {
554		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType,
555			certInfo->netscapeCertType.valToFree);
556	}
557	if(certInfo->subjectAltName.present) {
558		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectAltName,
559			certInfo->subjectAltName.valToFree);
560	}
561	if(certInfo->certPolicies.present) {
562		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
563			certInfo->certPolicies.valToFree);
564	}
565//	if(certInfo->policyConstraints.present) {
566//		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_PolicyConstraints,
567//			certInfo->policyConstraints.valToFree);
568//	}
569	if(certInfo->qualCertStatements.present) {
570		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_QC_Statements,
571			certInfo->qualCertStatements.valToFree);
572	}
573	if(certInfo->certificatePolicies.present) {
574		CSSM_CL_FreeFieldValue(clHand, &CSSMOID_CertificatePolicies,
575			certInfo->certificatePolicies.valToFree);
576	}
577}
578
579/*
580 * See if cert's Subject.{commonName,EmailAddress} matches caller-specified
581 * string. Returns CSSM_TRUE if match, else returns CSSM_FALSE.
582 * Also indicates whether *any* of the specified fields were found, regardless
583 * of match state.
584 */
585typedef enum {
586	SN_CommonName,			// CSSMOID_CommonName, host name format
587	SN_Email,				// CSSMOID_EmailAddress
588	SN_UserID,				// CSSMOID_UserID
589	SN_OrgUnit				// CSSMOID_OrganizationalUnitName
590} SubjSubjNameSearchType;
591
592static CSSM_BOOL tpCompareSubjectName(
593	TPCertInfo 				&cert,
594	SubjSubjNameSearchType	searchType,
595	bool					normalizeAll,		// for SN_Email case: lower-case all of
596												// the cert's value, not just the portion
597												// after the '@'
598	const char 				*callerStr,			// already tpToLower'd
599	uint32					callerStrLen,
600	bool					&fieldFound)
601{
602	char 			*certName = NULL;			// from cert's subject name
603	uint32 			certNameLen = 0;
604	CSSM_DATA_PTR 	subjNameData = NULL;
605	CSSM_RETURN 	crtn;
606	CSSM_BOOL		ourRtn = CSSM_FALSE;
607	const CSSM_OID	*oidSrch;
608
609	const char x500_userid_oid[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
610	CSSM_OID X500_UserID_OID = { sizeof(x500_userid_oid), (uint8*)x500_userid_oid };
611
612	fieldFound = false;
613	switch(searchType) {
614		case SN_CommonName:
615			oidSrch = &CSSMOID_CommonName;
616			break;
617		case SN_Email:
618			oidSrch = &CSSMOID_EmailAddress;
619			break;
620		case SN_UserID:
621			oidSrch = &X500_UserID_OID;
622			break;
623		case SN_OrgUnit:
624			oidSrch = &CSSMOID_OrganizationalUnitName;
625			break;
626		default:
627			assert(0);
628			return CSSM_FALSE;
629	}
630	crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
631	if(crtn) {
632		/* should never happen, we shouldn't be here if there is no subject */
633		tpPolicyError("tpCompareSubjectName: error retrieving subject name");
634		return CSSM_FALSE;
635	}
636	CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
637	if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
638		tpPolicyError("tpCompareSubjectName: malformed CSSM_X509_NAME");
639		cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
640		return CSSM_FALSE;
641	}
642
643	/* Now grunge thru the X509 name looking for a common name */
644	CSSM_X509_TYPE_VALUE_PAIR 	*ptvp;
645	CSSM_X509_RDN_PTR    		rdnp;
646	unsigned					rdnDex;
647	unsigned					pairDex;
648
649	for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
650		rdnp = &x509name->RelativeDistinguishedName[rdnDex];
651		for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
652			ptvp = &rdnp->AttributeTypeAndValue[pairDex];
653			if(tpCompareOids(&ptvp->type, oidSrch)) {
654				fieldFound = true;
655				certName = (char *)ptvp->value.Data;
656				certNameLen = (uint32)ptvp->value.Length;
657				switch(searchType) {
658					case SN_CommonName:
659					{
660						/* handle odd encodings that we need to convert to 8-bit */
661						CFStringBuiltInEncodings encoding;
662						CFDataRef cfd = NULL;
663						bool doConvert = false;
664						switch(ptvp->valueType) {
665							case BER_TAG_T61_STRING:
666								/* a.k.a. Teletex */
667								encoding = kCFStringEncodingISOLatin1;
668								doConvert = true;
669								break;
670							case BER_TAG_PKIX_BMP_STRING:
671								encoding = kCFStringEncodingUnicode;
672								doConvert = true;
673								break;
674							/*
675							 * All others - either take as is, or let it fail due to
676							 * illegal/incomprehensible format
677							 */
678							default:
679								break;
680						}
681						if(doConvert) {
682							/* raw data ==> CFString */
683							cfd = CFDataCreate(NULL, (UInt8 *)certName,	certNameLen);
684							if(cfd == NULL) {
685								/* try next component */
686								break;
687							}
688							CFStringRef cfStr = CFStringCreateFromExternalRepresentation(
689								NULL, cfd, encoding);
690							CFRelease(cfd);
691							if(cfStr == NULL) {
692								tpPolicyError("tpCompareSubjectName: bad str (1)");
693								break;
694							}
695
696							/* CFString ==> straight ASCII */
697							cfd = CFStringCreateExternalRepresentation(NULL,
698								cfStr, kCFStringEncodingASCII, 0);
699							CFRelease(cfStr);
700							if(cfd == NULL) {
701								tpPolicyError("tpCompareSubjectName: bad str (2)");
702								break;
703							}
704							certNameLen = (uint32)CFDataGetLength(cfd);
705							certName = (char *)CFDataGetBytePtr(cfd);
706						}
707						ourRtn = tpCompareHostNames(callerStr, callerStrLen,
708							certName, certNameLen);
709						if(doConvert) {
710							assert(cfd != NULL);
711							CFRelease(cfd);
712						}
713						break;
714					}
715					case SN_Email:
716						ourRtn = tpCompareEmailAddr(callerStr, callerStrLen,
717							certName, certNameLen, normalizeAll);
718						break;
719					case SN_UserID:
720					case SN_OrgUnit:
721						/* exact match only here, for now */
722						ourRtn = ((callerStrLen == certNameLen) &&
723								!memcmp(callerStr, certName, certNameLen)) ?
724								CSSM_TRUE : CSSM_FALSE;
725						break;
726				}
727				if(ourRtn) {
728					/* success */
729					break;
730				}
731				/* else keep going, maybe there's another common name */
732			}
733		}
734		if(ourRtn) {
735			break;
736		}
737	}
738	cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
739	return ourRtn;
740}
741
742/*
743 * Compare ASCII form of an IP address to a CSSM_DATA containing
744 * the IP address's numeric components. Returns true on match.
745 */
746static CSSM_BOOL tpCompIpAddrStr(
747	const char *str,
748	unsigned strLen,
749	const CSSM_DATA *numeric)
750{
751	const char *cp = str;
752	const char *nextDot;
753	char buf[100];
754
755	if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) {
756		return CSSM_FALSE;
757	}
758	if(cp[strLen - 1] == '\0') {
759		/* ignore NULL terminator */
760		strLen--;
761	}
762	for(unsigned dex=0; dex<numeric->Length; dex++) {
763		/* cp points to start of current string digit */
764		/* find next dot */
765		const char *lastChar = cp + strLen;
766		nextDot = cp + 1;
767		for( ; nextDot<lastChar; nextDot++) {
768			if(*nextDot == '.') {
769				break;
770			}
771		}
772		if(nextDot == lastChar) {
773			/* legal and required on last digit */
774			if(dex != (numeric->Length - 1)) {
775				return CSSM_FALSE;
776			}
777		}
778		else if(dex == (numeric->Length - 1)) {
779			return CSSM_FALSE;
780		}
781		ptrdiff_t digLen = nextDot - cp;
782		if(digLen >= sizeof(buf)) {
783			/* preposterous */
784			return CSSM_FALSE;
785		}
786		memmove(buf, cp, digLen);
787		buf[digLen] = '\0';
788		/* incr digLen to include the next dot */
789		digLen++;
790		cp += digLen;
791		strLen -= digLen;
792		int digVal = atoi(buf);
793		if(digVal != numeric->Data[dex]) {
794			return CSSM_FALSE;
795		}
796	}
797	return CSSM_TRUE;
798}
799
800/*
801 * See if cert's subjectAltName contains an element matching caller-specified
802 * string, hostname, in the following forms:
803 *
804 * SAN_HostName : dnsName, iPAddress
805 * SAN_Email    : RFC822Name
806 *
807 * Returns CSSM_TRUE if match, else returns CSSM_FALSE.
808 *
809 * Also indicates whether or not a dnsName (search type HostName) or
810 * RFC822Name (search type SAM_Email) was found, regardless of result
811 * of comparison.
812 *
813 * The appStr/appStrLen args are optional - if NULL/0, only the
814 * search for dnsName/RFC822Name is done.
815 */
816typedef enum {
817	SAN_HostName,
818	SAN_Email
819} SubjAltNameSearchType;
820
821static CSSM_BOOL tpCompareSubjectAltName(
822	const iSignExtenInfo	&subjAltNameInfo,
823	const char 				*appStr,			// caller has lower-cased as appropriate
824	uint32					appStrLen,
825	SubjAltNameSearchType 	searchType,
826	bool					normalizeAll,		// for SAN_Email case: lower-case all of
827												// the cert's value, not just the portion
828												// after the '@'
829	bool					&dnsNameFound,		// RETURNED, SAN_HostName case
830	bool					&emailFound)		// RETURNED, SAN_Email case
831{
832	dnsNameFound = false;
833	emailFound = false;
834	if(!subjAltNameInfo.present) {
835		/* common failure, no subjectAltName found */
836		return CSSM_FALSE;
837	}
838
839	CE_GeneralNames *names = &subjAltNameInfo.extnData->subjectAltName;
840	CSSM_BOOL		ourRtn = CSSM_FALSE;
841	char 			*certName;
842	uint32                  certNameLen;
843
844	/* Search thru the CE_GeneralNames looking for the appropriate attribute */
845	for(unsigned dex=0; dex<names->numNames; dex++) {
846		CE_GeneralName *name = &names->generalName[dex];
847		switch(searchType) {
848			case SAN_HostName:
849				switch(name->nameType) {
850					case GNT_IPAddress:
851						if(appStr == NULL) {
852							/* nothing to do here */
853							break;
854						}
855						ourRtn = tpCompIpAddrStr(appStr, appStrLen, &name->name);
856						break;
857
858					case GNT_DNSName:
859						if(name->berEncoded) {
860							tpErrorLog("tpCompareSubjectAltName: malformed "
861								"CE_GeneralName (1)\n");
862							break;
863						}
864						certName = (char *)name->name.Data;
865						if(certName == NULL) {
866							tpErrorLog("tpCompareSubjectAltName: malformed "
867								"CE_GeneralName (2)\n");
868							break;
869						}
870						certNameLen = (uint32)(name->name.Length);
871						dnsNameFound = true;
872						if(appStr != NULL) {
873							/* skip if caller passed in NULL */
874							ourRtn = tpCompareHostNames(appStr, appStrLen,
875								certName, certNameLen);
876						}
877						break;
878
879					default:
880						/* not interested, proceed to next name */
881						break;
882				}
883				break;	/* from case HostName */
884
885			case SAN_Email:
886				if(name->nameType != GNT_RFC822Name) {
887					/* not interested */
888					break;
889				}
890				certName = (char *)name->name.Data;
891				if(certName == NULL) {
892					tpErrorLog("tpCompareSubjectAltName: malformed "
893						"GNT_RFC822Name\n");
894					break;
895				}
896				certNameLen = (uint32)(name->name.Length);
897				emailFound = true;
898				if(appStr != NULL) {
899					ourRtn = tpCompareEmailAddr(appStr, appStrLen, certName,
900						certNameLen, normalizeAll);
901				}
902				break;
903		}
904		if(ourRtn) {
905			/* success */
906			break;
907		}
908	}
909	return ourRtn;
910}
911
912/* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */
913static CSSM_BOOL tpIsNumeric(
914	const char *hostName,
915	unsigned hostNameLen)
916{
917	if(hostName[hostNameLen - 1] == '\0') {
918		/* ignore NULL terminator */
919		hostNameLen--;
920	}
921	for(unsigned i=0; i<hostNameLen; i++) {
922		char c = *hostName++;
923		if(isdigit(c)) {
924			continue;
925		}
926		if(c != '.') {
927			return CSSM_FALSE;
928		}
929	}
930	return CSSM_TRUE;
931}
932
933/*
934 * Convert a typed string represented by a CSSM_X509_TYPE_VALUE_PAIR to a
935 * CFStringRef. Caller owns and must release the result. NULL return means
936 * unconvertible input "string".
937 */
938static CFStringRef tpTvpToCfString(
939	const CSSM_X509_TYPE_VALUE_PAIR	*tvp)
940{
941	CFStringBuiltInEncodings encoding;
942	switch(tvp->valueType) {
943		case BER_TAG_T61_STRING:
944			/* a.k.a. Teletex */
945			encoding = kCFStringEncodingISOLatin1;
946			break;
947		case BER_TAG_PKIX_BMP_STRING:
948			encoding = kCFStringEncodingUnicode;
949			break;
950		case BER_TAG_PRINTABLE_STRING:
951		case BER_TAG_IA5_STRING:
952		case BER_TAG_PKIX_UTF8_STRING:
953			encoding = kCFStringEncodingUTF8;
954			break;
955		default:
956			return NULL;
957	}
958
959	/* raw data ==> CFString */
960	CFDataRef cfd = CFDataCreate(NULL, tvp->value.Data,	tvp->value.Length);
961	if(cfd == NULL) {
962			return NULL;
963	}
964	CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL, cfd, encoding);
965	CFRelease(cfd);
966	return cfStr;
967}
968
969/*
970 * Compare a CFString and a string represented by a CSSM_X509_TYPE_VALUE_PAIR.
971 * Returns CSSM_TRUE if they are equal.
972 */
973static bool tpCompareTvpToCfString(
974	const CSSM_X509_TYPE_VALUE_PAIR	*tvp,
975	CFStringRef refStr,
976	CFOptionFlags flags)		// e.g., kCFCompareCaseInsensitive
977{
978	CFStringRef cfStr = tpTvpToCfString(tvp);
979	if(cfStr == NULL) {
980		return false;
981	}
982	CFComparisonResult res = CFStringCompare(refStr, cfStr, flags);
983	CFRelease(cfStr);
984	if(res == kCFCompareEqualTo) {
985		return true;
986	}
987	else {
988		return false;
989	}
990}
991
992/*
993 * Given one iSignCertInfo, determine whether or not the specified
994 * EKU OID, or - optionally - CSSMOID_ExtendedKeyUsageAny - is present.
995 * Returns true if so, else false.
996 */
997static bool tpVerifyEKU(
998	const iSignCertInfo &certInfo,
999	const CSSM_OID &ekuOid,
1000	bool ekuAnyOK)			// if true, CSSMOID_ExtendedKeyUsageAny counts as "found"
1001{
1002	if(!certInfo.extendKeyUsage.present) {
1003		return false;
1004	}
1005	CE_ExtendedKeyUsage *eku = &certInfo.extendKeyUsage.extnData->extendedKeyUsage;
1006	assert(eku != NULL);
1007
1008	for(unsigned i=0; i<eku->numPurposes; i++) {
1009		const CSSM_OID *foundEku = &eku->purposes[i];
1010		if(tpCompareOids(foundEku, &ekuOid)) {
1011			return true;
1012		}
1013		if(ekuAnyOK && tpCompareOids(foundEku, &CSSMOID_ExtendedKeyUsageAny)) {
1014			return true;
1015		}
1016	}
1017	return false;
1018}
1019
1020/*
1021 * Given one iSignCertInfo, determine whether or not the specified
1022 * Certificate Policy OID, or - optionally - CSSMOID_ANY_POLICY - is present.
1023 * Returns true if so, else false.
1024 */
1025static bool tpVerifyCPE(
1026	const iSignCertInfo &certInfo,
1027	const CSSM_OID &cpOid,
1028	bool anyPolicyOK)		// if true, CSSMOID_ANY_POLICY counts as "found"
1029{
1030	if(!certInfo.certPolicies.present) {
1031		return false;
1032	}
1033	CE_CertPolicies *cp = &certInfo.certPolicies.extnData->certPolicies;
1034	assert(cp != NULL);
1035
1036	for(unsigned i=0; i<cp->numPolicies; i++) {
1037		const CE_PolicyInformation *foundPolicy = &cp->policies[i];
1038		if(tpCompareOids(&foundPolicy->certPolicyId, &cpOid)) {
1039			return true;
1040		}
1041		if(anyPolicyOK && tpCompareOids(&foundPolicy->certPolicyId, &CSSMOID_ANY_POLICY)) {
1042			return true;
1043		}
1044	}
1045	return false;
1046}
1047
1048/*
1049 * Verify iChat handle. We search for a matching (case-insensitive) string
1050 * comprised of:
1051 *
1052 *   -- name component ("dmitch") from subject name's CommonName
1053 *   -- implicit '@'
1054 *   -- domain name from subject name's organizationalUnit
1055 *
1056 * Plus we require an Organization component of "Apple Computer, Inc." or "Apple Inc."
1057 */
1058static bool tpCompareIChatHandleName(
1059	TPCertInfo 				&cert,
1060	const char 				*iChatHandle,		// UTF8
1061	uint32					iChatHandleLen)
1062{
1063	CSSM_DATA_PTR				subjNameData = NULL;		// from fetchField
1064	CSSM_RETURN					crtn;
1065	bool						ourRtn = false;
1066	CSSM_X509_NAME_PTR			x509name;
1067	CSSM_X509_TYPE_VALUE_PAIR 	*ptvp;
1068	CSSM_X509_RDN_PTR    		rdnp;
1069	unsigned					rdnDex;
1070	unsigned					pairDex;
1071
1072	/* search until all of these are true */
1073	CSSM_BOOL	commonNameMatch = CSSM_FALSE;		// name before '@'
1074	CSSM_BOOL	orgUnitMatch = CSSM_FALSE;			// domain after '@
1075	CSSM_BOOL	orgMatch = CSSM_FALSE;				// Apple Computer, Inc. (or Apple Inc.)
1076
1077	/*
1078	 * incoming UTF8 handle ==> two components.
1079	 * First convert to CFString.
1080	 */
1081	if(iChatHandle[iChatHandleLen - 1] == '\0') {
1082	  /* avoid NULL when creating CFStrings */
1083	  iChatHandleLen--;
1084	}
1085	CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)iChatHandle, iChatHandleLen);
1086	if(cfd == NULL) {
1087		return false;
1088	}
1089	CFStringRef handleStr = CFStringCreateFromExternalRepresentation(NULL, cfd,
1090		kCFStringEncodingUTF8);
1091	CFRelease(cfd);
1092	if(handleStr == NULL) {
1093		tpPolicyError("tpCompareIChatHandleName: bad incoming handle (1)");
1094		return false;
1095	}
1096
1097	/*
1098	 * Find the '@' delimiter
1099	 */
1100	CFRange whereIsAt;
1101	whereIsAt = CFStringFind(handleStr, CFSTR("@"), 0);
1102	if(whereIsAt.length == 0) {
1103		tpPolicyError("tpCompareIChatHandleName: bad incoming handle: no @");
1104		CFRelease(handleStr);
1105		return false;
1106	}
1107
1108	/*
1109	 * Two components, before and after delimiter
1110	 */
1111	CFRange r = {0, whereIsAt.location};
1112	CFStringRef	iChatName = CFStringCreateWithSubstring(NULL, handleStr, r);
1113	if(iChatName == NULL) {
1114		tpPolicyError("tpCompareIChatHandleName: bad incoming handle (2)");
1115		CFRelease(handleStr);
1116		return false;
1117	}
1118	r.location = whereIsAt.location + 1;		// after the '@'
1119	r.length = CFStringGetLength(handleStr) - r.location;
1120	CFStringRef iChatDomain = CFStringCreateWithSubstring(NULL, handleStr, r);
1121	CFRelease(handleStr);
1122	if(iChatDomain == NULL) {
1123		tpPolicyError("tpCompareIChatHandleName: bad incoming handle (3)");
1124		CFRelease(iChatName);
1125		return false;
1126	}
1127	/* subsequent errors to errOut: */
1128
1129	/* get subject name in CSSM form, all subsequent ops work on that */
1130	crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
1131	if(crtn) {
1132		/* should never happen, we shouldn't be here if there is no subject */
1133		tpPolicyError("tpCompareIChatHandleName: error retrieving subject name");
1134		goto errOut;
1135	}
1136
1137	x509name = (CSSM_X509_NAME_PTR)subjNameData->Data;
1138	if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) {
1139		tpPolicyError("tpCompareIChatHandleName: malformed CSSM_X509_NAME");
1140		goto errOut;
1141	}
1142
1143	/* Now grunge thru the X509 name looking for three fields */
1144
1145	for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) {
1146		rdnp = &x509name->RelativeDistinguishedName[rdnDex];
1147		for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) {
1148			ptvp = &rdnp->AttributeTypeAndValue[pairDex];
1149			if(!commonNameMatch &&
1150			   tpCompareOids(&ptvp->type, &CSSMOID_CommonName) &&
1151			   tpCompareTvpToCfString(ptvp, iChatName, kCFCompareCaseInsensitive)) {
1152					commonNameMatch = CSSM_TRUE;
1153			}
1154
1155			if(!orgUnitMatch &&
1156			   tpCompareOids(&ptvp->type, &CSSMOID_OrganizationalUnitName) &&
1157			   tpCompareTvpToCfString(ptvp, iChatDomain, kCFCompareCaseInsensitive)) {
1158					orgUnitMatch = CSSM_TRUE;
1159			}
1160
1161			if(!orgMatch &&
1162			   tpCompareOids(&ptvp->type, &CSSMOID_OrganizationName) &&
1163			   /* this one is case sensitive */
1164			   (tpCompareTvpToCfString(ptvp, CFSTR("Apple Computer, Inc."), 0) ||
1165			    tpCompareTvpToCfString(ptvp, CFSTR("Apple Inc."), 0))) {
1166					orgMatch = CSSM_TRUE;
1167			}
1168
1169			if(commonNameMatch && orgUnitMatch && orgMatch) {
1170				/* TA DA */
1171				ourRtn = true;
1172				goto errOut;
1173			}
1174		}
1175	}
1176errOut:
1177	cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1178	CFRelease(iChatName);
1179	CFRelease(iChatDomain);
1180	return ourRtn;
1181}
1182
1183/*
1184 * Verify SSL options. Currently this just consists of matching the
1185 * leaf cert's subject common name against the caller's (optional)
1186 * server name.
1187 */
1188static CSSM_RETURN tp_verifySslOpts(
1189	TPPolicy policy,
1190	TPCertGroup &certGroup,
1191	const CSSM_DATA *sslFieldOpts,
1192	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1193{
1194	const iSignCertInfo &leafCertInfo = certInfo[0];
1195	CSSM_APPLE_TP_SSL_OPTIONS *sslOpts = NULL;
1196	unsigned hostNameLen = 0;
1197	const char *serverName = NULL;
1198	TPCertInfo *leaf = certGroup.certAtIndex(0);
1199	assert(leaf != NULL);
1200
1201	/* CSSM_APPLE_TP_SSL_OPTIONS is optional */
1202	if((sslFieldOpts != NULL) && (sslFieldOpts->Data != NULL)) {
1203		sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)sslFieldOpts->Data;
1204		switch(sslOpts->Version) {
1205			case CSSM_APPLE_TP_SSL_OPTS_VERSION:
1206				if(sslFieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
1207					return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1208				}
1209				break;
1210			/* handle backwards compatibility here if necessary */
1211			default:
1212				return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1213		}
1214		hostNameLen = sslOpts->ServerNameLen;
1215		serverName = sslOpts->ServerName;
1216	}
1217
1218	/* host name check is optional */
1219	if(hostNameLen != 0) {
1220		if(serverName == NULL) {
1221			return CSSMERR_TP_INVALID_POINTER;
1222		}
1223
1224		/* convert caller's hostname string to lower case */
1225		char *hostName = (char *)certGroup.alloc().malloc(hostNameLen);
1226		memmove(hostName, serverName, hostNameLen);
1227		tpToLower(hostName, hostNameLen);
1228
1229		CSSM_BOOL match = CSSM_FALSE;
1230
1231		/* First check subjectAltName... */
1232		bool dnsNameFound = false;
1233		bool dummy;
1234		match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1235			hostName, hostNameLen,
1236			SAN_HostName, false, dnsNameFound, dummy);
1237
1238		/*
1239		 * Then common name, if
1240		 *  -- no match from subjectAltName, AND
1241		 *  -- dnsName was NOT found, AND
1242		 *  -- hostName is not strictly numeric form (1.2.3.4)
1243		 */
1244		if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) {
1245			bool fieldFound;
1246			match = tpCompareSubjectName(*leaf, SN_CommonName, false, hostName, hostNameLen,
1247				fieldFound);
1248		}
1249
1250		/*
1251		 * Limit allowed domains for specific anchors
1252		 */
1253		CSSM_BOOL domainMatch = CSSM_TRUE;
1254		if(match) {
1255			TPCertInfo *tpCert = certGroup.lastCert();
1256			if (tpCert) {
1257				const CSSM_DATA *certData = tpCert->itemData();
1258				unsigned char digest[CC_SHA1_DIGEST_LENGTH];
1259				CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
1260				if (!memcmp(digest, kAppleCorpCASHA1, sizeof(digest))) {
1261					const char *dnlist[] = { "apple.com", "icloud.com" };
1262					unsigned int idx, dncount=2;
1263					domainMatch = CSSM_FALSE;
1264					for(idx=0;idx<dncount;idx++) {
1265						uint32 len=(uint32)strlen(dnlist[idx]);
1266						char *domainName=(char*)certGroup.alloc().malloc(len);
1267						memmove(domainName, (char*)dnlist[idx], len);
1268						if(tpCompareDomainSuffix(hostName, hostNameLen,
1269						   domainName, len)) {
1270							domainMatch = CSSM_TRUE;
1271						}
1272						certGroup.alloc().free(domainName);
1273						if (domainMatch) {
1274							break;
1275						}
1276					}
1277				}
1278			}
1279		}
1280
1281		certGroup.alloc().free(hostName);
1282		if(!match) {
1283			if(leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH)) {
1284				return CSSMERR_APPLETP_HOSTNAME_MISMATCH;
1285			}
1286		}
1287		if(!domainMatch) {
1288			if(leaf->addStatusCode(CSSMERR_APPLETP_CA_PIN_MISMATCH)) {
1289				return CSSMERR_APPLETP_CA_PIN_MISMATCH;
1290			}
1291		}
1292	}
1293
1294	/*
1295	 * Ensure that, if an extendedKeyUsage extension is present in the
1296	 * leaf, that either anyExtendedKeyUsage or the appropriate
1297	 * CSSMOID_{Server,Client}Auth, or a SeverGatedCrypto usage is present.
1298	 */
1299	const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
1300	if(ekuInfo.present) {
1301		bool foundGoodEku = false;
1302		bool isServer = true;
1303		CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1304		assert(eku != NULL);
1305
1306		/*
1307		 * Determine appropriate extended key usage; default is SSL server
1308		 */
1309		const CSSM_OID *extUse = &CSSMOID_ServerAuth;
1310		if((sslOpts != NULL) &&				/* optional, default server side */
1311		   (sslOpts->Version > 0) &&		/* this was added in struct version 1 */
1312		   (sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT)) {
1313		   extUse = &CSSMOID_ClientAuth;
1314		   isServer = false;
1315		}
1316
1317		/* search for that one or for "any" indicator */
1318		for(unsigned i=0; i<eku->numPurposes; i++) {
1319			const CSSM_OID *purpose = &eku->purposes[i];
1320			if(tpCompareOids(purpose, extUse)) {
1321				foundGoodEku = true;
1322				break;
1323			}
1324			if(tpCompareOids(purpose, &CSSMOID_ExtendedKeyUsageAny)) {
1325				foundGoodEku = true;
1326				break;
1327			}
1328			if((policy == kTP_IPSec) && (tpCompareOids(purpose, &CSSMOID_EKU_IPSec))) {
1329				foundGoodEku = true;
1330				break;
1331			}
1332			if(isServer) {
1333				/* server gated crypto: server side only */
1334				if(tpCompareOids(purpose, &CSSMOID_NetscapeSGC)) {
1335					foundGoodEku = true;
1336					break;
1337				}
1338				if(tpCompareOids(purpose, &CSSMOID_MicrosoftSGC)) {
1339					foundGoodEku = true;
1340					break;
1341				}
1342			}
1343		}
1344		if(!foundGoodEku) {
1345			if(leaf->addStatusCode(CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE)) {
1346				return CSSMERR_TP_VERIFY_ACTION_FAILED;
1347			}
1348		}
1349	}
1350
1351	/*
1352	 * Check for additional options flag (2nd lowest bit set) which indicates
1353	 * we must be issued by an Apple intermediate with a particular extension.
1354	 * (This flag is set by SecPolicyCreateAppleSSLService in SecPolicy.cpp.)
1355	 */
1356	if((sslOpts != NULL) &&
1357	   (sslOpts->Version > 0) &&		/* this was added in struct version 1 */
1358	   (sslOpts->Flags & 0x00000002)) {
1359
1360		if (certGroup.numCerts() > 1) {
1361			const iSignCertInfo *isCertInfo = &certInfo[1];
1362			if (!(isCertInfo->foundAppleServerAuthMarker == CSSM_TRUE)) {
1363				TPCertInfo *tpCert = certGroup.certAtIndex(1);
1364				tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
1365				return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1366			}
1367		}
1368		else {
1369			/* we only have the leaf? */
1370			if(leaf->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION)) {
1371				return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1372			}
1373		}
1374	}
1375	return CSSM_OK;
1376}
1377
1378/*
1379 * Verify SMIME and iChat options.
1380 * This deals with both S/MIME and iChat policies; within the iChat domain it
1381 * deals with Apple-specific .mac certs as well as what we call "generic AIM"
1382 * certs, as used in the Windows AIM client.
1383 */
1384#define CE_CIPHER_MASK	(~(CE_KU_EncipherOnly | CE_KU_DecipherOnly))
1385
1386static CSSM_RETURN tp_verifySmimeOpts(
1387	TPPolicy policy,
1388	TPCertGroup &certGroup,
1389	const CSSM_DATA *smimeFieldOpts,
1390	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1391{
1392	const iSignCertInfo &leafCertInfo = certInfo[0];
1393	bool iChat = (policy == kTP_iChat) ? true : false;
1394	/*
1395	 * The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
1396	 */
1397	CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts = NULL;
1398	if(smimeFieldOpts != NULL) {
1399		smimeOpts = (CSSM_APPLE_TP_SMIME_OPTIONS *)smimeFieldOpts->Data;
1400	}
1401	if(smimeOpts != NULL) {
1402		switch(smimeOpts->Version) {
1403			case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
1404				if(smimeFieldOpts->Length !=
1405						sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
1406					return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1407				}
1408				break;
1409			/* handle backwards compatibility here if necessary */
1410			default:
1411				return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
1412		}
1413	}
1414
1415	TPCertInfo *leaf = certGroup.certAtIndex(0);
1416	assert(leaf != NULL);
1417
1418	/* Verify optional email address, a.k.a. handle for iChat policy */
1419	unsigned emailLen = 0;
1420	if(smimeOpts != NULL) {
1421		emailLen = smimeOpts->SenderEmailLen;
1422	}
1423
1424	bool match = false;
1425	bool emailFoundInSAN = false;
1426	bool iChatHandleFound = false;		/* indicates a genuine Apple iChat cert */
1427	bool emailFoundInDN = false;
1428	if(emailLen != 0) {
1429		if(smimeOpts->SenderEmail == NULL) {
1430			return CSSMERR_TP_INVALID_POINTER;
1431		}
1432
1433		/* iChat - first try the Apple custom format */
1434		if(iChat) {
1435			iChatHandleFound = tpCompareIChatHandleName(*leaf,	smimeOpts->SenderEmail,
1436				emailLen);
1437			if(iChatHandleFound) {
1438				match = true;
1439			}
1440
1441		}
1442
1443		if(!match) {
1444			/*
1445			 * normalize caller's email string
1446			 *  SMIME - lowercase only the portion after '@'
1447			 *  iChat - lowercase all of it
1448			 */
1449			char *email = (char *)certGroup.alloc().malloc(emailLen);
1450			memmove(email, smimeOpts->SenderEmail, emailLen);
1451			tpNormalizeAddrSpec(email, emailLen, iChat);
1452
1453
1454			/*
1455			 * First check subjectAltName. The emailFound bool indicates
1456			 * that *some* email address was found, regardless of a match
1457			 * condition.
1458			 */
1459			bool dummy;
1460			match = tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1461				email, emailLen,
1462				SAN_Email, iChat, dummy, emailFoundInSAN);
1463
1464			/*
1465			 * Then subject DN, CSSMOID_EmailAddress, if no match from
1466			 * subjectAltName. In this case the whole email address is
1467			 * case insensitive (RFC 3280, section 4.1.2.6), so
1468			 * renormalize.
1469			 */
1470			if(!match) {
1471				tpNormalizeAddrSpec(email, emailLen, true);
1472				match = tpCompareSubjectName(*leaf, SN_Email, true, email, emailLen,
1473					emailFoundInDN);
1474			}
1475			certGroup.alloc().free(email);
1476
1477			/*
1478			 * Error here if no match found but there was indeed *some*
1479			 * email address in the cert.
1480			 */
1481			if(!match && (emailFoundInSAN || emailFoundInDN)) {
1482				if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND)) {
1483					tpPolicyError("SMIME email addrs in cert but no match");
1484					return CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND;
1485				}
1486			}
1487		}
1488
1489		/*
1490		 * iChat only: error if app specified email address but there was
1491		 * none in the cert.
1492		 */
1493		if(iChat && !emailFoundInSAN && !emailFoundInDN && !iChatHandleFound) {
1494			if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
1495				tpPolicyError("iChat: no email address or handle in cert");
1496				return CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS;
1497			}
1498		}
1499	}
1500
1501	/*
1502	 * Going by the letter of the law, here's what RFC 2632 has to say
1503	 * about the legality of an empty Subject Name:
1504	 *
1505	 *    ...the subject DN in a user's (i.e. end-entity) certificate MAY
1506	 *    be an empty SEQUENCE in which case the subjectAltName extension
1507	 *    will include the subject's identifier and MUST be marked as
1508	 *    critical.
1509	 *
1510	 * OK, first examine the leaf cert's subject name.
1511	 */
1512	CSSM_RETURN crtn;
1513	CSSM_DATA_PTR subjNameData = NULL;
1514	const iSignExtenInfo &kuInfo = leafCertInfo.keyUsage;
1515	const iSignExtenInfo &ekuInfo = leafCertInfo.extendKeyUsage;
1516	const CSSM_X509_NAME *x509Name = NULL;
1517
1518	if(iChat) {
1519		/* empty subject name processing is S/MIME only */
1520		goto checkEku;
1521	}
1522
1523	crtn = leaf->fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData);
1524	if(crtn) {
1525		/* This should really never happen */
1526		tpPolicyError("SMIME policy: error fetching subjectName");
1527		leaf->addStatusCode(CSSMERR_TP_INVALID_CERTIFICATE);
1528		return CSSMERR_TP_INVALID_CERTIFICATE;
1529	}
1530	/* must do a leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct on exit */
1531
1532	x509Name = (const CSSM_X509_NAME *)subjNameData->Data;
1533	if(x509Name->numberOfRDNs == 0) {
1534		/*
1535		 * Empty subject name. If we haven't already seen a valid
1536		 * email address in the subject alternate name (by looking
1537		 * for a specific address specified by app), try to find
1538		 * one now.
1539		 */
1540		if(!emailFoundInSAN &&		// haven't found one, and
1541		   (emailLen == 0)) {		// didn't even look yet
1542			bool dummy;
1543			tpCompareSubjectAltName(leafCertInfo.subjectAltName,
1544					NULL, 0,				// email, emailLen,
1545					SAN_Email, false, dummy,
1546					emailFoundInSAN);		// the variable we're updating
1547		}
1548		if(!emailFoundInSAN) {
1549			tpPolicyError("SMIME policy fail: empty subject name and "
1550				"no Email Addrs in SubjectAltName");
1551			if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS)) {
1552				leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1553				return CSSMERR_TP_VERIFY_ACTION_FAILED;
1554			}
1555			else {
1556				/* have to skip the next block */
1557				goto postSAN;
1558			}
1559		}
1560
1561		/*
1562		 * One more thing: this leaf must indeed have a subjAltName
1563		 * extension and it must be critical. We would not have gotten this
1564		 * far if the subjAltName extension was not actually present....
1565		 */
1566		assert(leafCertInfo.subjectAltName.present);
1567		if(!leafCertInfo.subjectAltName.critical) {
1568			tpPolicyError("SMIME policy fail: empty subject name and "
1569				"no Email Addrs in SubjectAltName");
1570			if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_SUBJ_ALT_NAME_NOT_CRIT)) {
1571				leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1572				return CSSMERR_TP_VERIFY_ACTION_FAILED;
1573			}
1574		}
1575	}
1576postSAN:
1577	leaf->freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData);
1578
1579	/*
1580	 * Enforce the usage of the key associated with the leaf cert.
1581	 * Cert's KeyUsage must be a superset of what the app is trying to do.
1582	 * Note the {en,de}cipherOnly flags are handled separately....
1583	 */
1584	if(kuInfo.present && (smimeOpts != NULL)) {
1585		CE_KeyUsage certKu = *((CE_KeyUsage *)kuInfo.extnData);
1586		CE_KeyUsage appKu = smimeOpts->IntendedUsage;
1587		CE_KeyUsage intersection = certKu & appKu;
1588		if((intersection & CE_CIPHER_MASK) != (appKu & CE_CIPHER_MASK)) {
1589			tpPolicyError("SMIME KeyUsage err: appKu 0x%x  certKu 0x%x",
1590				appKu, certKu);
1591			if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1592				return CSSMERR_TP_VERIFY_ACTION_FAILED;
1593			}
1594		}
1595
1596		/* Now the en/de cipher only bits - for keyAgreement only */
1597		if(appKu & CE_KU_KeyAgreement) {
1598			/*
1599			 * 1. App wants to use this for key agreement; it must
1600			 *    say what it wants to do with the derived key.
1601			 *    In this context, the app's XXXonly bit means that
1602			 *    it wants to use the key for that op - not necessarliy
1603			 *    "only".
1604			 */
1605			if((appKu & (CE_KU_EncipherOnly | CE_KU_DecipherOnly)) == 0) {
1606				tpPolicyError("SMIME KeyUsage err: KeyAgreement with "
1607					"no Encipher or Decipher");
1608				if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1609					return CSSMERR_TP_VERIFY_ACTION_FAILED;
1610				}
1611			}
1612
1613			/*
1614			 * 2. If cert restricts to encipher only make sure the
1615			 *    app isn't trying to decipher.
1616			 */
1617			if((certKu & CE_KU_EncipherOnly) &&
1618			   (appKu & CE_KU_DecipherOnly)) {
1619				tpPolicyError("SMIME KeyUsage err: cert EncipherOnly, "
1620					"app wants to decipher");
1621				if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1622					return CSSMERR_TP_VERIFY_ACTION_FAILED;
1623				}
1624			}
1625
1626			/*
1627			 * 3. If cert restricts to decipher only make sure the
1628			 *    app isn't trying to encipher.
1629			 */
1630			if((certKu & CE_KU_DecipherOnly) &&
1631			   (appKu & CE_KU_EncipherOnly)) {
1632				tpPolicyError("SMIME KeyUsage err: cert DecipherOnly, "
1633					"app wants to encipher");
1634				if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_KEY_USE)) {
1635					return CSSMERR_TP_VERIFY_ACTION_FAILED;
1636				}
1637			}
1638		}
1639	}
1640
1641	/*
1642	 * Extended Key Use verification, which is different for the two policies.
1643	 */
1644checkEku:
1645	if(iChat && !ekuInfo.present) {
1646		/*
1647		 * iChat: whether generic AIM cert or Apple .mac/iChat cert, we must have an
1648		 * extended key use extension.
1649		 */
1650		tpPolicyError("iChat: No extended Key Use");
1651		if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1652			return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1653		}
1654	}
1655
1656	if(!iChatHandleFound) {
1657		/*
1658		 * S/MIME and generic AIM certs when evaluating iChat policy.
1659		 * Look for either emailProtection or anyExtendedKeyUsage usages.
1660		 *
1661		 * S/MIME : the whole extension is optional.
1662		 * iChat  : extension must be there (which we've already covered, above)
1663		 *          and we must find one of those extensions.
1664		 */
1665		if(ekuInfo.present) {
1666			bool foundGoodEku = false;
1667			CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1668			assert(eku != NULL);
1669			for(unsigned i=0; i<eku->numPurposes; i++) {
1670				if(tpCompareOids(&eku->purposes[i], &CSSMOID_EmailProtection)) {
1671					foundGoodEku = true;
1672					break;
1673				}
1674				if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
1675					foundGoodEku = true;
1676					break;
1677				}
1678			}
1679			if(!foundGoodEku) {
1680				tpPolicyError("iChat/SMIME: No appropriate extended Key Use");
1681				if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1682					return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1683				}
1684			}
1685		}
1686	}
1687	else {
1688		/*
1689		 * Apple iChat cert. Look for anyExtendedKeyUsage, iChatSigning,
1690		 * ichatEncrypting - the latter of two which can optionally be
1691		 * required by app.
1692		 */
1693		assert(iChat);	/* or we could not have even looked for an iChat style handle */
1694		assert(ekuInfo.present);	/* checked above */
1695		bool foundAnyEku = false;
1696		bool foundIChatSign = false;
1697		bool foundISignEncrypt = false;
1698		CE_ExtendedKeyUsage *eku = (CE_ExtendedKeyUsage *)ekuInfo.extnData;
1699		assert(eku != NULL);
1700
1701		for(unsigned i=0; i<eku->numPurposes; i++) {
1702			if(tpCompareOids(&eku->purposes[i],
1703					&CSSMOID_APPLE_EKU_ICHAT_SIGNING)) {
1704				foundIChatSign = true;
1705			}
1706			else if(tpCompareOids(&eku->purposes[i],
1707					&CSSMOID_APPLE_EKU_ICHAT_ENCRYPTION)) {
1708				foundISignEncrypt = true;
1709			}
1710			else if(tpCompareOids(&eku->purposes[i], &CSSMOID_ExtendedKeyUsageAny)) {
1711				foundAnyEku = true;
1712			}
1713		}
1714
1715		if(!foundAnyEku && !foundISignEncrypt && !foundIChatSign) {
1716			/* No go - no acceptable uses found */
1717			tpPolicyError("iChat: No valid extended Key Uses found");
1718			if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1719				return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1720			}
1721		}
1722
1723		/* check for specifically required uses */
1724		if((smimeOpts != NULL) && (smimeOpts->IntendedUsage != 0)) {
1725			if(smimeOpts->IntendedUsage & CE_KU_DigitalSignature) {
1726				if(!foundIChatSign) {
1727					tpPolicyError("iChat: ICHAT_SIGNING required, but missing");
1728					if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1729						return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1730					}
1731				}
1732			}
1733			if(smimeOpts->IntendedUsage & CE_KU_DataEncipherment) {
1734				if(!foundISignEncrypt) {
1735					tpPolicyError("iChat: ICHAT_ENCRYPT required, but missing");
1736					if(leaf->addStatusCode(CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE)) {
1737						return CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE;
1738					}
1739				}
1740			}
1741		}	/* checking IntendedUsage */
1742	}	/* iChat cert format */
1743
1744	return CSSM_OK;
1745}
1746
1747/*
1748 * Verify Apple SW Update signing (was Apple Code Signing, pre-Leopard) options.
1749 *
1750 * -- Must have one intermediate cert
1751 * -- intermediate must have basic constraints with path length 0
1752 * -- intermediate has CSSMOID_APPLE_EKU_CODE_SIGNING EKU
1753 * -- leaf cert has either CODE_SIGNING or CODE_SIGN_DEVELOPMENT EKU (the latter of
1754 *    which triggers a CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT error)
1755 */
1756static CSSM_RETURN tp_verifySWUpdateSigningOpts(
1757	TPCertGroup &certGroup,
1758	const CSSM_DATA *fieldOpts,			// currently unused
1759	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1760{
1761	unsigned numCerts = certGroup.numCerts();
1762	const iSignCertInfo *isCertInfo;
1763	TPCertInfo *tpCert;
1764//	const CE_BasicConstraints *bc;		// currently unused
1765	CE_ExtendedKeyUsage *eku;
1766	CSSM_RETURN crtn = CSSM_OK;
1767
1768	if(numCerts != 3) {
1769		if(!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH)) {
1770			tpPolicyError("tp_verifySWUpdateSigningOpts: numCerts %u", numCerts);
1771			return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
1772		}
1773		else if(numCerts < 3) {
1774			/* this error allowed, but no intermediate...check leaf */
1775			goto checkLeaf;
1776		}
1777	}
1778
1779	/* verify intermediate cert */
1780	isCertInfo = &certInfo[1];
1781	tpCert = certGroup.certAtIndex(1);
1782
1783	if(!isCertInfo->basicConstraints.present) {
1784		tpPolicyError("tp_verifySWUpdateSigningOpts: no basicConstraints in intermediate");
1785		if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS)) {
1786			return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
1787		}
1788	}
1789
1790	/* ExtendedKeyUse required, one legal value */
1791	if(!isCertInfo->extendKeyUsage.present) {
1792		tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in intermediate");
1793		if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
1794			return CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
1795		}
1796		else {
1797			goto checkLeaf;
1798		}
1799	}
1800
1801	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
1802	assert(eku != NULL);
1803	if(eku->numPurposes != 1) {
1804		tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes in intermediate (%lu)",
1805			(unsigned long)eku->numPurposes);
1806		if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1807			return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1808		}
1809		else if(eku->numPurposes == 0) {
1810			/* ignore that error but no EKU - skip EKU check */
1811			goto checkLeaf;
1812		}
1813		/* else ignore error and we have an intermediate EKU; proceed */
1814	}
1815
1816	if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
1817		tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU");
1818		if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1819			crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1820		}
1821	}
1822
1823checkLeaf:
1824
1825	/* verify leaf cert */
1826	isCertInfo = &certInfo[0];
1827	tpCert = certGroup.certAtIndex(0);
1828	if(!isCertInfo->extendKeyUsage.present) {
1829		tpPolicyError("tp_verifySWUpdateSigningOpts: no extendedKeyUse in leaf");
1830		if(tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE)) {
1831			return crtn ? crtn : CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
1832		}
1833		else {
1834			/* have to skip remainder */
1835			return CSSM_OK;
1836		}
1837	}
1838
1839	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
1840	assert(eku != NULL);
1841	if(eku->numPurposes != 1) {
1842		tpPolicyError("tp_verifySWUpdateSigningOpts: bad eku->numPurposes (%lu)",
1843			(unsigned long)eku->numPurposes);
1844		if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1845			if(crtn == CSSM_OK) {
1846				crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1847			}
1848		}
1849		return crtn;
1850	}
1851	if(!tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING)) {
1852		if(tpCompareOids(&eku->purposes[0], &CSSMOID_APPLE_EKU_CODE_SIGNING_DEV)) {
1853			tpPolicyError("tp_verifySWUpdateSigningOpts: DEVELOPMENT cert");
1854			if(tpCert->addStatusCode(CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT)) {
1855				if(crtn == CSSM_OK) {
1856					crtn = CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT;
1857				}
1858			}
1859		}
1860		else {
1861			tpPolicyError("tp_verifySWUpdateSigningOpts: bad EKU in leaf");
1862			if(tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1863				if(crtn == CSSM_OK) {
1864					crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1865				}
1866			}
1867		}
1868	}
1869
1870	return crtn;
1871}
1872
1873/*
1874 * Verify Apple Resource Signing options.
1875 *
1876 * -- leaf cert must have CSSMOID_APPLE_EKU_RESOURCE_SIGNING EKU
1877 * -- chain length must be >= 2
1878 * -- mainline code already verified that leaf KeyUsage = digitalSignature (only)
1879 */
1880static CSSM_RETURN tp_verifyResourceSigningOpts(
1881	TPCertGroup &certGroup,
1882	const CSSM_DATA *fieldOpts,			// currently unused
1883	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1884{
1885	unsigned numCerts = certGroup.numCerts();
1886	if(numCerts < 2) {
1887		if(!certGroup.isAllowedError(CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH)) {
1888			tpPolicyError("tp_verifyResourceSigningOpts: numCerts %u", numCerts);
1889			return CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH;
1890		}
1891	}
1892	const iSignCertInfo &leafCert = certInfo[0];
1893	TPCertInfo *leaf = certGroup.certAtIndex(0);
1894
1895	/* leaf ExtendedKeyUse required, one legal value */
1896	if(!tpVerifyEKU(leafCert, CSSMOID_APPLE_EKU_RESOURCE_SIGNING, false)) {
1897		tpPolicyError("tp_verifyResourceSigningOpts: no RESOURCE_SIGNING EKU");
1898		if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1899			return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1900		}
1901	}
1902
1903	return CSSM_OK;
1904}
1905
1906/*
1907 * Common code for Apple Code Signing and Apple Package Signing.
1908 * For now we just require an RFC3280-style CodeSigning EKU in the leaf
1909 * for both policies.
1910 */
1911static CSSM_RETURN tp_verifyCodePkgSignOpts(
1912	TPPolicy policy,
1913	TPCertGroup &certGroup,
1914	const CSSM_DATA *fieldOpts,			// currently unused
1915	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1916{
1917	const iSignCertInfo &leafCert = certInfo[0];
1918
1919	/* leaf ExtendedKeyUse required, one legal value */
1920	if(!tpVerifyEKU(leafCert, CSSMOID_ExtendedUseCodeSigning, false)) {
1921		TPCertInfo *leaf = certGroup.certAtIndex(0);
1922		tpPolicyError("tp_verifyCodePkgSignOpts: no CodeSigning EKU");
1923		if(leaf->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
1924			return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
1925		}
1926	}
1927
1928	return CSSM_OK;
1929
1930}
1931
1932/*
1933 * Verify MacAppStore receipt verification policy options.
1934 *
1935 * -- Must have one intermediate cert
1936 * -- intermediate must be the FairPlay intermediate
1937 * -- leaf cert has the CSSMOID_APPLE_EXTENSION_MACAPPSTORE_RECEIPT marker extension
1938 */
1939static CSSM_RETURN tp_verifyMacAppStoreReceiptOpts(
1940	TPCertGroup &certGroup,
1941	const CSSM_DATA *fieldOpts,			// currently unused
1942	const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
1943{
1944	unsigned numCerts = certGroup.numCerts();
1945	if (numCerts < 3)
1946	{
1947		if (!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH))
1948		{
1949			tpPolicyError("tp_verifyMacAppStoreReceiptOpts: numCerts %u", numCerts);
1950			return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
1951		}
1952	}
1953
1954	const iSignCertInfo *isCertInfo;
1955	TPCertInfo *tpCert;
1956
1957	/* verify intermediate cert */
1958	isCertInfo = &certInfo[1];
1959	tpCert = certGroup.certAtIndex(1);
1960
1961	if (!isCertInfo->basicConstraints.present)
1962	{
1963		tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
1964		if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS))
1965			return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
1966	}
1967
1968	// Now check the leaf
1969	isCertInfo = &certInfo[0];
1970	tpCert = certGroup.certAtIndex(0);
1971	if (certInfo->certificatePolicies.present)
1972	{
1973	//	syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: found certificatePolicies");
1974		const CE_CertPolicies *certPolicies =
1975				&isCertInfo->certificatePolicies.extnData->certPolicies;
1976		if (!certificatePoliciesContainsOID(certPolicies, &CSSMOID_MACAPPSTORE_RECEIPT_CERT_POLICY))
1977			if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1978				return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1979	}
1980	else
1981	{
1982	//	syslog(LOG_ERR, "tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present");	// DEBUG
1983        tpPolicyError("tp_verifyMacAppStoreReceiptOpts: no certificatePolicies present in leaf");
1984        if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
1985			return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
1986	}
1987
1988	return CSSM_OK;
1989}
1990
1991bool certificatePoliciesContainsOID(const CE_CertPolicies *certPolicies, const CSSM_OID *oidToFind)
1992{
1993    // returns true if the given OID is present in the cert policies
1994
1995    if (!certPolicies || !oidToFind)
1996		return false;
1997
1998    const uint32 maxIndex = 100;		// sanity check
1999    for (uint32 policyIndex = 0; policyIndex < certPolicies->numPolicies && policyIndex < maxIndex; policyIndex++)
2000	{
2001        CE_PolicyInformation *certPolicyInfo = &certPolicies->policies[policyIndex];
2002        CSSM_OID_PTR oid = &certPolicyInfo->certPolicyId;
2003		if (oid && tpCompareOids(oid, oidToFind))	// found it
2004			return true;
2005    }
2006
2007	return false;
2008}
2009
2010
2011/*
2012 * Verify Apple ID Sharing options.
2013 *
2014 * -- Do basic cert validation (OCSP-based certs)
2015 * -- Validate that the cert is an Apple ID sharing cert:
2016 *		has a custom extension: OID: Apple ID Sharing Certificate ( 1 2 840 113635 100 4 7 )
2017 *			(CSSMOID_APPLE_EXTENSION_APPLEID_SHARING)
2018 *		EKU should have both client and server authentication
2019 *		chains to the "Apple Application Integration Certification Authority" intermediate
2020 * -- optionally has a client-specified common name, which is the Apple ID account's UUID.
2021
2022 * -- Must have one intermediate cert ("Apple Application Integration Certification Authority")
2023 * -- intermediate must have basic constraints with path length 0
2024 * -- intermediate has CSSMOID_APPLE_EXTENSION_AAI_INTERMEDIATE extension (OID 1 2 840 113635 100 6 2 3)
2025 OR APPLE_EXTENSION_AAI_INTERMEDIATE_2
2026 */
2027
2028static CSSM_RETURN tp_verifyAppleIDSharingOpts(TPCertGroup &certGroup,
2029												const CSSM_DATA *fieldOpts,			// optional Common Name
2030												const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
2031{
2032	unsigned numCerts = certGroup.numCerts();
2033	const iSignCertInfo *isCertInfo;
2034	TPCertInfo *tpCert;
2035	//	const CE_BasicConstraints *bc;		// currently unused
2036	CE_ExtendedKeyUsage *eku;
2037	CSSM_RETURN crtn = CSSM_OK;
2038	unsigned int serverNameLen = 0;
2039	const char *serverName = NULL;
2040
2041	// The CSSM_APPLE_TP_SMIME_OPTIONS pointer is optional as is everything in it.
2042    if (fieldOpts && fieldOpts->Data)
2043    {
2044		CSSM_APPLE_TP_SSL_OPTIONS *sslOpts = (CSSM_APPLE_TP_SSL_OPTIONS *)fieldOpts->Data;
2045        switch (sslOpts->Version)
2046        {
2047        case CSSM_APPLE_TP_SSL_OPTS_VERSION:
2048            if (fieldOpts->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS))
2049                return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
2050            break;
2051            /* handle backwards compatibility here if necessary */
2052        default:
2053            return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
2054		}
2055		serverNameLen = sslOpts->ServerNameLen;
2056		serverName = sslOpts->ServerName;
2057	}
2058
2059	//------------------------------------------------------------------------
2060
2061	if (numCerts != 3)
2062	{
2063		if (!certGroup.isAllowedError(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH))
2064		{
2065			tpPolicyError("tp_verifyAppleIDSharingOpts: numCerts %u", numCerts);
2066			return CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2067		}
2068		else
2069		if (numCerts < 3)
2070		{
2071			/* this error allowed, but no intermediate...check leaf */
2072			goto checkLeaf;
2073		}
2074	}
2075
2076	/* verify intermediate cert */
2077	isCertInfo = &certInfo[1];
2078	tpCert = certGroup.certAtIndex(1);
2079
2080	if (!isCertInfo->basicConstraints.present)
2081	{
2082		tpPolicyError("tp_verifyAppleIDSharingOpts: no basicConstraints in intermediate");
2083		if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS))
2084			return CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS;
2085	}
2086
2087checkLeaf:
2088
2089	/* verify leaf cert */
2090	isCertInfo = &certInfo[0];
2091	tpCert = certGroup.certAtIndex(0);
2092
2093	/* host name check is optional */
2094	if (serverNameLen != 0)
2095	{
2096		if (serverName == NULL)
2097			return CSSMERR_TP_INVALID_POINTER;
2098
2099		/* convert caller's hostname string to lower case */
2100		char *hostName = (char *)certGroup.alloc().malloc(serverNameLen);
2101		memmove(hostName, serverName, serverNameLen);
2102		tpToLower(hostName, serverNameLen);
2103
2104		/* Check common name... */
2105
2106		bool fieldFound;
2107		CSSM_BOOL match = tpCompareSubjectName(*tpCert, SN_CommonName, false, hostName,
2108									 serverNameLen, fieldFound);
2109
2110		certGroup.alloc().free(hostName);
2111		if (!match && tpCert->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH))
2112            return CSSMERR_APPLETP_HOSTNAME_MISMATCH;
2113	}
2114
2115	if (certInfo->certificatePolicies.present)
2116	{
2117		const CE_CertPolicies *certPolicies =
2118				&isCertInfo->certificatePolicies.extnData->certPolicies;
2119		if (!certificatePoliciesContainsOID(certPolicies, &CSSMOID_APPLEID_SHARING_CERT_POLICY))
2120			if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
2121				return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2122	}
2123	else
2124	if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
2125		return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2126
2127	if (!isCertInfo->extendKeyUsage.present)
2128    {
2129		tpPolicyError("tp_verifyAppleIDSharingOpts: no extendedKeyUse in leaf");
2130		if (tpCert->addStatusCode(CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE))
2131			return crtn ? crtn : CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE;
2132
2133        /* have to skip remainder */
2134        return CSSM_OK;
2135	}
2136
2137	// Check that certificate can do Client and Server Authentication (EKU)
2138	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
2139	assert(eku != NULL);
2140	if(eku->numPurposes != 2)
2141	{
2142		tpPolicyError("tp_verifyAppleIDSharingOpts: bad eku->numPurposes (%lu)",
2143					  (unsigned long)eku->numPurposes);
2144		if (tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE))
2145		{
2146			if (crtn == CSSM_OK)
2147				crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2148		}
2149		return crtn;
2150	}
2151	bool canDoClientAuth = false, canDoServerAuth = false, ekuError = false;
2152	for (int ix=0;ix<2;ix++)
2153	{
2154		if (tpCompareOids(&eku->purposes[ix], &CSSMOID_ClientAuth))
2155			canDoClientAuth = true;
2156		else
2157		if (tpCompareOids(&eku->purposes[ix], &CSSMOID_ServerAuth))
2158			canDoServerAuth = true;
2159		else
2160		{
2161			ekuError = true;
2162			break;
2163		}
2164	}
2165
2166	if (!(canDoClientAuth && canDoServerAuth))
2167		ekuError = true;
2168	if (ekuError)
2169	{
2170		tpPolicyError("tp_verifyAppleIDSharingOpts: bad EKU in leaf");
2171		if (tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE))
2172		{
2173			if (crtn == CSSM_OK)
2174				crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2175		}
2176	}
2177
2178	return crtn;
2179}
2180
2181/*
2182 * Verify Time Stamping (RFC3161) policy options.
2183 *
2184 * -- Leaf must contain Extended Key Usage (EKU), marked critical
2185 * -- The EKU must contain the id-kp-timeStamping purpose and no other
2186 */
2187static CSSM_RETURN tp_verifyTimeStampingOpts(TPCertGroup &certGroup,
2188											 const CSSM_DATA *fieldOpts,		// currently unused
2189											 const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
2190{
2191    //unsigned numCerts = certGroup.numCerts();
2192	const iSignCertInfo *isCertInfo;
2193	TPCertInfo *tpCert;
2194	CE_ExtendedKeyUsage *eku;
2195
2196	isCertInfo = &certInfo[0];
2197	tpCert = certGroup.certAtIndex(0);
2198
2199	if (!isCertInfo->extendKeyUsage.present)
2200	{
2201		tpPolicyError("tp_verifyTimeStampingOpts: no extendedKeyUse in leaf");
2202		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2203		return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2204	}
2205
2206	if(!isCertInfo->extendKeyUsage.critical)
2207	{
2208		tpPolicyError("tp_verifyTimeStampingOpts: extended key usage !critical");
2209		tpCert->addStatusCode(CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL);
2210		return CSSMERR_APPLETP_EXT_KEYUSAGE_NOT_CRITICAL;
2211	}
2212
2213	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
2214	assert(eku != NULL);
2215
2216	if(eku->numPurposes != 1)
2217	{
2218		tpPolicyError("tp_verifyTimeStampingOpts: bad eku->numPurposes (%lu)",
2219					  (unsigned long)eku->numPurposes);
2220		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2221		return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2222	}
2223
2224	if(!tpCompareOids(&eku->purposes[0], &CSSMOID_TimeStamping))
2225	{
2226		tpPolicyError("tp_verifyTimeStampingOpts: TimeStamping purpose not found");
2227		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2228		return CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2229	}
2230
2231	return CSSM_OK;
2232}
2233
2234/*
2235 * Verify Passbook Signing policy options.
2236 *
2237 * -- Do basic cert validation (OCSP-based certs)
2238 * -- Chains to the Apple root CA
2239 * -- Has custom marker extension (1.2.840.113635.100.6.1.16)
2240 *		(CSSMOID_APPLE_EXTENSION_PASSBOOK_SIGNING)
2241 * -- EKU contains Passbook Signing purpose (1.2.840.113635.100.4.14)
2242 *		(CSSMOID_APPLE_EKU_PASSBOOK_SIGNING)
2243 * -- UID field of Subject must contain provided card signer string
2244 * -- OU field of Subject must contain provided team identifier string
2245 */
2246static CSSM_RETURN tp_verifyPassbookSigningOpts(TPCertGroup &certGroup,
2247											 const CSSM_DATA *fieldOpts,
2248											 const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
2249{
2250	unsigned numCerts = certGroup.numCerts();
2251	const iSignCertInfo *isCertInfo;
2252	TPCertInfo *tpCert;
2253	CE_ExtendedKeyUsage *eku;
2254	CSSM_RETURN crtn = CSSM_OK;
2255	unsigned int nameLen = 0;
2256	const char *name = NULL;
2257	char *p, *signerName = NULL, *teamIdentifier = NULL;
2258	bool found;
2259
2260	isCertInfo = &certInfo[0];
2261	tpCert = certGroup.certAtIndex(0);
2262
2263	/* The CSSM_APPLE_TP_SMIME_OPTIONS pointer is required. */
2264    if (!fieldOpts || !fieldOpts->Data)
2265		return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
2266    else {
2267		CSSM_APPLE_TP_SMIME_OPTIONS *opts = (CSSM_APPLE_TP_SMIME_OPTIONS *)fieldOpts->Data;
2268        switch (opts->Version)
2269        {
2270        case CSSM_APPLE_TP_SMIME_OPTS_VERSION:
2271            if (fieldOpts->Length != sizeof(CSSM_APPLE_TP_SMIME_OPTIONS))
2272                return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
2273            break;
2274            /* handle backwards compatibility here if necessary */
2275        default:
2276            return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
2277		}
2278		nameLen = opts->SenderEmailLen;
2279		name = opts->SenderEmail;
2280		if (!name || !nameLen)
2281			return CSSMERR_APPLETP_IDENTIFIER_MISSING;
2282	}
2283
2284	/* Split the provided name into signer name and team identifier
2285	 * (allocates memory, which must be freed at end) */
2286	signerName = (char *)certGroup.alloc().malloc(nameLen);
2287	teamIdentifier = (char *)certGroup.alloc().malloc(nameLen);
2288	memmove(signerName, name, nameLen);
2289	teamIdentifier[0] = '\0';
2290	if ((p = strchr(signerName, '\t')) != NULL) {
2291		*p++ = '\0';
2292		memmove(teamIdentifier, p, strlen(p)+1);
2293	}
2294
2295	/* Check signer name in UID field */
2296	if (CSSM_FALSE == tpCompareSubjectName(*tpCert,
2297		SN_UserID, false, signerName, (unsigned int)strlen(signerName), found)) {
2298		tpPolicyError("tp_verifyPassbookSigningOpts: signer name not in subject UID field");
2299		tpCert->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING);
2300		crtn = CSSMERR_APPLETP_IDENTIFIER_MISSING;
2301		goto cleanup;
2302	}
2303
2304	/* Check team identifier in OU field */
2305	if (CSSM_FALSE == tpCompareSubjectName(*tpCert,
2306		SN_OrgUnit, false, teamIdentifier, (unsigned int)strlen(teamIdentifier), found)) {
2307		tpPolicyError("tp_verifyPassbookSigningOpts: team identifier not in subject OU field");
2308		tpCert->addStatusCode(CSSMERR_APPLETP_IDENTIFIER_MISSING);
2309		crtn = CSSMERR_APPLETP_IDENTIFIER_MISSING;
2310		goto cleanup;
2311	}
2312
2313	/* Check that EKU extension is present */
2314	if (!isCertInfo->extendKeyUsage.present) {
2315		tpPolicyError("tp_verifyPassbookSigningOpts: no extendedKeyUse in leaf");
2316		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2317		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2318		goto cleanup;
2319	}
2320
2321	/* Check that EKU contains Passbook Signing purpose */
2322	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
2323	assert(eku != NULL);
2324	found = false;
2325	for (int ix=0;ix<eku->numPurposes;ix++) {
2326		if (tpCompareOids(&eku->purposes[ix], &CSSMOID_APPLE_EKU_PASSBOOK_SIGNING)) {
2327			found = true;
2328			break;
2329		}
2330	}
2331	if (!found) {
2332		tpPolicyError("tp_verifyPassbookSigningOpts: Passbook Signing purpose not found");
2333		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2334		crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2335		goto cleanup;
2336	}
2337
2338	/* Check that Passbook Signing marker extension is present */
2339	if (!(isCertInfo->foundPassbookSigning == CSSM_TRUE)) {
2340		tpPolicyError("tp_verifyPassbookSigningOpts: no Passbook Signing extension in leaf");
2341		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2342		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2343		goto cleanup;
2344	}
2345
2346	/* Check that cert chain is anchored by the Apple Root CA */
2347	if (numCerts < 3) {
2348		tpPolicyError("tp_verifyPassbookSigningOpts: numCerts %u", numCerts);
2349		crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2350		goto cleanup;
2351	}
2352	else {
2353	 	tpCert = certGroup.certAtIndex(numCerts-1);
2354		const CSSM_DATA *certData = tpCert->itemData();
2355		unsigned char digest[CC_SHA1_DIGEST_LENGTH];
2356		CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
2357		if (memcmp(digest, kAppleCASHA1, sizeof(digest))) {
2358			tpPolicyError("tp_verifyPassbookSigningOpts: invalid anchor for policy");
2359			tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
2360			crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2361			goto cleanup;
2362		}
2363	}
2364
2365cleanup:
2366	if (signerName)
2367		certGroup.alloc().free(signerName);
2368	if (teamIdentifier)
2369		certGroup.alloc().free(teamIdentifier);
2370
2371	return crtn;
2372}
2373
2374/*
2375 * Verify Mobile Store policy options.
2376 *
2377 * -- Do basic cert validation.
2378 * -- Chain length must be exactly 3.
2379 * -- Must chain to known Mobile Store root.
2380 * -- Intermediate must have CSSMOID_APPLE_EXTENSION_SYSINT2_INTERMEDIATE marker
2381 *		(1.2.840.113635.100.6.2.10)
2382 * -- Key usage in leaf certificate must be Digital Signature.
2383 * -- Leaf has certificatePolicies extension with appropriate policy:
2384 *		(1.2.840.113635.100.5.12) if testPolicy is false
2385 *		(1.2.840.113635.100.5.12.1) if testPolicy is true
2386 */
2387static CSSM_RETURN tp_verifyMobileStoreSigningOpts(TPCertGroup &certGroup,
2388											 const CSSM_DATA *fieldOpts,
2389											 const iSignCertInfo *certInfo,		// all certs, size certGroup.numCerts()
2390											 bool testPolicy)
2391{
2392	unsigned numCerts = certGroup.numCerts();
2393	const iSignCertInfo *isCertInfo;
2394	TPCertInfo *tpCert;
2395	CE_KeyUsage ku;
2396	CSSM_RETURN crtn = CSSM_OK;
2397
2398	isCertInfo = &certInfo[0];
2399	tpCert = certGroup.certAtIndex(0);
2400
2401	/* Check that KU extension is present */
2402	if (!isCertInfo->keyUsage.present) {
2403		tpPolicyError("tp_verifyMobileStoreSigningOpts: no keyUsage in leaf");
2404		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2405		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2406		goto cleanup;
2407	}
2408
2409	/* Check that KU contains Digital Signature usage */
2410	ku = isCertInfo->keyUsage.extnData->keyUsage;
2411	if (!(ku & CE_KU_DigitalSignature)) {
2412		tpPolicyError("tp_verifyMobileStoreSigningOpts: DigitalSignature usage not found");
2413		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
2414		crtn = CSSMERR_APPLETP_INVALID_KEY_USAGE;
2415		goto cleanup;
2416	}
2417
2418	/* Check that Mobile Store Signing certicate policy is present in leaf */
2419	if (isCertInfo->certificatePolicies.present)
2420	{
2421		const CE_CertPolicies *certPolicies =
2422				&isCertInfo->certificatePolicies.extnData->certPolicies;
2423		const CSSM_OID *policyOID = (testPolicy) ?
2424				&CSSMOID_TEST_MOBILE_STORE_SIGNING_POLICY :
2425				&CSSMOID_MOBILE_STORE_SIGNING_POLICY;
2426		if (!certificatePoliciesContainsOID(certPolicies, policyOID))
2427			if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
2428				return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2429	}
2430	else
2431	{
2432        tpPolicyError("tp_verifyMobileStoreSigningOpts: no certificatePolicies present in leaf");
2433        if (tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION))
2434			return CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2435	}
2436
2437	/* Check that cert chain length is 3 */
2438	if (numCerts != 3) {
2439		tpPolicyError("tp_verifyMobileStoreSigningOpts: numCerts %u", numCerts);
2440		crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2441		goto cleanup;
2442	}
2443
2444	/* Check that cert chain is anchored by a known root */
2445	{
2446		tpCert = certGroup.certAtIndex(numCerts-1);
2447		const CSSM_DATA *certData = tpCert->itemData();
2448		unsigned char digest[CC_SHA1_DIGEST_LENGTH];
2449		CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
2450		if (memcmp(digest, kMobileRootSHA1, sizeof(digest))) {
2451			tpPolicyError("tp_verifyMobileStoreSigningOpts: invalid anchor for policy");
2452			tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
2453			crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2454			goto cleanup;
2455		}
2456	}
2457
2458	/* Check that Apple System Integration 2 marker extension is present in intermediate */
2459	isCertInfo = &certInfo[1];
2460	tpCert = certGroup.certAtIndex(1);
2461	if (!(isCertInfo->foundAppleSysInt2Marker == CSSM_TRUE)) {
2462		tpPolicyError("tp_verifyMobileStoreSigningOpts: intermediate marker extension not found");
2463		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2464		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2465		goto cleanup;
2466	}
2467
2468cleanup:
2469	return crtn;
2470}
2471
2472/*
2473 * Verify Escrow Service policy options.
2474 *
2475 * -- Chain length must be exactly 2.
2476 * -- Must be issued by known escrow root.
2477 * -- Key usage in leaf certificate must be Key Encipherment.
2478 * -- Leaf has CSSMOID_APPLE_EXTENSION_ESCROW_SERVICE_MARKER extension
2479 *		(1.2.840.113635.100.6.23.1)
2480 */
2481static CSSM_RETURN tp_verifyEscrowServiceCommon(TPCertGroup &certGroup,
2482											 const CSSM_DATA *fieldOpts,
2483											 const iSignCertInfo *certInfo,		// all certs, size certGroup.numCerts()
2484											 SecCertificateEscrowRootType rootType)
2485{
2486	unsigned numCerts = certGroup.numCerts();
2487	const iSignCertInfo *isCertInfo;
2488	TPCertInfo *tpCert;
2489	CE_KeyUsage ku;
2490	CSSM_RETURN crtn = CSSM_OK;
2491
2492	isCertInfo = &certInfo[0];
2493	tpCert = certGroup.certAtIndex(0);
2494
2495	/* Check that KU extension is present */
2496	if (!isCertInfo->keyUsage.present) {
2497		tpPolicyError("tp_verifyEscrowServiceCommon: no keyUsage in leaf");
2498		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2499		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2500		goto cleanup;
2501	}
2502
2503	/* Check that KU contains Key Encipherment usage */
2504	ku = isCertInfo->keyUsage.extnData->keyUsage;
2505	if (!(ku & CE_KU_KeyEncipherment)) {
2506		tpPolicyError("tp_verifyEscrowServiceCommon: KeyEncipherment usage not found");
2507		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE);
2508		crtn = CSSMERR_APPLETP_INVALID_KEY_USAGE;
2509		goto cleanup;
2510	}
2511
2512	/* Check that Escrow Service marker extension is present */
2513	if (!(isCertInfo->foundEscrowServiceMarker == CSSM_TRUE)) {
2514		tpPolicyError("tp_verifyEscrowServiceCommon: no Escrow Service extension in leaf");
2515		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2516		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2517		goto cleanup;
2518	}
2519
2520	/* Check that cert chain length is 2 */
2521	if (numCerts != 2) {
2522		tpPolicyError("tp_verifyEscrowServiceCommon: numCerts %u", numCerts);
2523		crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2524		goto cleanup;
2525	}
2526
2527	/* Check that cert chain is anchored by a known root */
2528	{
2529		tpCert = certGroup.certAtIndex(numCerts-1);
2530		const CSSM_DATA *certData = tpCert->itemData();
2531		bool anchorMatch = false;
2532		SecCertificateRef anchor = NULL;
2533		OSStatus status = SecCertificateCreateFromData(certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &anchor);
2534		if (!status) {
2535			CFArrayRef anchors = SecCertificateCopyEscrowRoots(rootType);
2536			CFIndex idx, count = (anchors) ? CFArrayGetCount(anchors) : 0;
2537			for (idx = 0; idx < count; idx++) {
2538				SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(anchors, idx);
2539				if (cert && CFEqual(cert, anchor)) {
2540					anchorMatch = true;
2541					break;
2542				}
2543			}
2544			if (anchors)
2545				CFRelease(anchors);
2546		}
2547		if (anchor)
2548			CFRelease(anchor);
2549
2550		if (!anchorMatch) {
2551			tpPolicyError("tp_verifyEscrowServiceCommon: invalid anchor for policy");
2552			tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
2553			crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2554			goto cleanup;
2555		}
2556	}
2557
2558cleanup:
2559	return crtn;
2560}
2561
2562static CSSM_RETURN tp_verifyEscrowServiceSigningOpts(TPCertGroup &certGroup,
2563											 const CSSM_DATA *fieldOpts,
2564											 const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
2565{
2566	return tp_verifyEscrowServiceCommon(certGroup, fieldOpts, certInfo, kSecCertificateProductionEscrowRoot);
2567}
2568
2569static CSSM_RETURN tp_verifyPCSEscrowServiceSigningOpts(TPCertGroup &certGroup,
2570											 const CSSM_DATA *fieldOpts,
2571											 const iSignCertInfo *certInfo)		// all certs, size certGroup.numCerts()
2572{
2573	return tp_verifyEscrowServiceCommon(certGroup, fieldOpts, certInfo, kSecCertificateProductionPCSEscrowRoot);
2574}
2575
2576/*
2577 * Verify Configuration Profile Signing policy options.
2578 *
2579 * -- Do basic cert validation (OCSP-based certs)
2580 * -- Chains to the Apple root CA
2581 * -- Leaf has EKU extension with appropriate purpose:
2582 *		(1.2.840.113635.100.4.16) if testPolicy is false
2583 *		(1.2.840.113635.100.4.17) if testPolicy is true
2584 */
2585static CSSM_RETURN tp_verifyProfileSigningOpts(TPCertGroup &certGroup,
2586											 const CSSM_DATA *fieldOpts,
2587											 const iSignCertInfo *certInfo,		// all certs, size certGroup.numCerts()
2588											 bool testPolicy)
2589{
2590	unsigned numCerts = certGroup.numCerts();
2591	const iSignCertInfo *isCertInfo;
2592	TPCertInfo *tpCert;
2593	CE_ExtendedKeyUsage *eku;
2594	CSSM_RETURN crtn = CSSM_OK;
2595	bool found;
2596
2597	isCertInfo = &certInfo[0];
2598	tpCert = certGroup.certAtIndex(0);
2599
2600	/* Check that EKU extension is present */
2601	if (!isCertInfo->extendKeyUsage.present) {
2602		tpPolicyError("tp_verifyProfileSigningOpts: no extendedKeyUse in leaf");
2603		tpCert->addStatusCode(CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION);
2604		crtn = CSSMERR_APPLETP_MISSING_REQUIRED_EXTENSION;
2605		goto cleanup;
2606	}
2607
2608	/* Check that EKU contains appropriate Profile Signing purpose */
2609	eku = &isCertInfo->extendKeyUsage.extnData->extendedKeyUsage;
2610	assert(eku != NULL);
2611	found = false;
2612	for (int ix=0;ix<eku->numPurposes;ix++) {
2613		if (tpCompareOids(&eku->purposes[ix], (testPolicy) ?
2614			&CSSMOID_APPLE_EKU_QA_PROFILE_SIGNING :
2615			&CSSMOID_APPLE_EKU_PROFILE_SIGNING)) {
2616			found = true;
2617			break;
2618		}
2619	}
2620	if (!found) {
2621		tpPolicyError("tp_verifyProfileSigningOpts: Profile Signing purpose not found");
2622		tpCert->addStatusCode(CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE);
2623		crtn = CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE;
2624		goto cleanup;
2625	}
2626
2627	/* Check that cert chain is anchored by the Apple Root CA */
2628	if (numCerts < 3) {
2629		tpPolicyError("tp_verifyProfileSigningOpts: numCerts %u", numCerts);
2630		crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2631		goto cleanup;
2632	}
2633	else {
2634		tpCert = certGroup.certAtIndex(numCerts-1);
2635		const CSSM_DATA *certData = tpCert->itemData();
2636		unsigned char digest[CC_SHA1_DIGEST_LENGTH];
2637		CC_SHA1(certData->Data, (CC_LONG)certData->Length, digest);
2638		if (memcmp(digest, kAppleCASHA1, sizeof(digest))) {
2639			tpPolicyError("tp_verifyProfileSigningOpts: invalid anchor for policy");
2640			tpCert->addStatusCode(CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH);
2641			crtn = CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH;
2642			goto cleanup;
2643		}
2644	}
2645
2646cleanup:
2647	return crtn;
2648}
2649
2650/*
2651 * RFC2459 says basicConstraints must be flagged critical for
2652 * CA certs, but Verisign doesn't work that way.
2653 */
2654#define BASIC_CONSTRAINTS_MUST_BE_CRITICAL		0
2655
2656/*
2657 * TP iSign spec says Extended Key Usage required for leaf certs,
2658 * but Verisign doesn't work that way.
2659 */
2660#define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF	0
2661
2662/*
2663 * TP iSign spec says Subject Alternate Name required for leaf certs,
2664 * but Verisign doesn't work that way.
2665 */
2666#define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF		0
2667
2668/*
2669 * TP iSign spec originally required KeyUsage for all certs, but
2670 * Verisign doesn't have that in their roots.
2671 */
2672#define KEY_USAGE_REQUIRED_FOR_ROOT				0
2673
2674/*
2675 * RFC 2632, "S/MIME Version 3 Certificate Handling", section
2676 * 4.4.2, says that KeyUsage extensions MUST be flagged critical,
2677 * but Thawte's intermediate cert (common name "Thawte Personal
2678 * Freemail Issuing CA") does not meet this requirement.
2679 */
2680#define SMIME_KEY_USAGE_MUST_BE_CRITICAL		0
2681
2682/*
2683 * Public routine to perform TP verification on a constructed
2684 * cert group.
2685 * Returns CSSM_OK on success.
2686 * Assumes the chain has passed basic subject/issuer verification. First cert of
2687 * incoming certGroup is end-entity (leaf).
2688 *
2689 * Per-policy details:
2690 *   iSign: Assumes that last cert in incoming certGroup is a root cert.
2691 *			Also assumes a cert group of more than one cert.
2692 *   kTPx509Basic: CertGroup of length one allowed.
2693 */
2694CSSM_RETURN tp_policyVerify(
2695	TPPolicy						policy,
2696	Allocator						&alloc,
2697	CSSM_CL_HANDLE					clHand,
2698	CSSM_CSP_HANDLE					cspHand,
2699	TPCertGroup 					*certGroup,
2700	CSSM_BOOL						verifiedToRoot,		// last cert is good root
2701	CSSM_BOOL						verifiedViaTrustSetting,	// last cert verified via
2702															//     user trust
2703	CSSM_APPLE_TP_ACTION_FLAGS		actionFlags,
2704	const CSSM_DATA					*policyFieldData,	// optional
2705	void							*policyOpts)		// future options
2706{
2707	iSignCertInfo 			*certInfo = NULL;
2708	uint32					numCerts;
2709	iSignCertInfo			*thisCertInfo;
2710	uint16					expUsage;
2711	uint16					actUsage;
2712	unsigned				certDex;
2713	CSSM_BOOL				cA = CSSM_FALSE;		// init for compiler warning
2714	bool					isLeaf;					// end entity
2715	bool					isRoot;					// root cert
2716	CE_ExtendedKeyUsage		*extendUsage;
2717	CE_AuthorityKeyID		*authorityId;
2718	CSSM_KEY_PTR			pubKey;
2719	CSSM_RETURN				outErr = CSSM_OK;		// for gross, non-policy errors
2720	CSSM_BOOL				policyFail = CSSM_FALSE;// generic CSSMERR_TP_VERIFY_ACTION_FAILED
2721	CSSM_RETURN				policyError = CSSM_OK;	// policy-specific failure
2722
2723	/* First, kTPDefault is a nop here */
2724	if(policy == kTPDefault) {
2725		return CSSM_OK;
2726	}
2727
2728	if(certGroup == NULL) {
2729		return CSSMERR_TP_INVALID_CERTGROUP;
2730	}
2731	numCerts = certGroup->numCerts();
2732	if(numCerts == 0) {
2733		return CSSMERR_TP_INVALID_CERTGROUP;
2734	}
2735	if(policy == kTPiSign) {
2736		if(!verifiedToRoot) {
2737			/* no way, this requires a root cert */
2738			return CSSMERR_TP_VERIFY_ACTION_FAILED;
2739		}
2740		if(numCerts <= 1) {
2741			/* nope, not for iSign */
2742			return CSSMERR_TP_VERIFY_ACTION_FAILED;
2743		}
2744	}
2745
2746	/* cook up an iSignCertInfo array */
2747	certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo));
2748	/* subsequent errors to errOut: */
2749
2750	/* fill it with interesting info from parsed certs */
2751	for(certDex=0; certDex<numCerts; certDex++) {
2752		if(iSignGetCertInfo(alloc,
2753				certGroup->certAtIndex(certDex),
2754				&certInfo[certDex])) {
2755			(certGroup->certAtIndex(certDex))->addStatusCode(
2756				CSSMERR_TP_INVALID_CERTIFICATE);
2757			/* this one is fatal (and can't ignore) */
2758			outErr = CSSMERR_TP_INVALID_CERTIFICATE;
2759			goto errOut;
2760		}
2761	}
2762
2763	/*
2764	 * OK, the heart of TP enforcement.
2765	 */
2766	for(certDex=0; certDex<numCerts; certDex++) {
2767		thisCertInfo = &certInfo[certDex];
2768		TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex);
2769
2770		/*
2771		 * First check for presence of required extensions and
2772		 * critical extensions we don't understand.
2773		 */
2774		if(thisCertInfo->foundUnknownCritical) {
2775			/* illegal for all policies */
2776			tpPolicyError("tp_policyVerify: critical flag in unknown extension");
2777			if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN)) {
2778				policyFail = CSSM_TRUE;
2779			}
2780		}
2781
2782		/*
2783		 * Check for unsupported key length, per <rdar://6892837>
2784		 */
2785		if((pubKey=thisTpCertInfo->pubKey()) != NULL) {
2786			CSSM_KEYHEADER *keyHdr = &pubKey->KeyHeader;
2787			if(keyHdr->AlgorithmId == CSSM_ALGID_RSA && keyHdr->LogicalKeySizeInBits < 1024) {
2788				tpPolicyError("tp_policyVerify: RSA key size too small");
2789				if(thisTpCertInfo->addStatusCode(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE)) {
2790					policyFail = CSSM_TRUE;
2791				}
2792			}
2793		}
2794
2795		/*
2796		 * Note it's possible for both of these to be true, for a chain
2797		 * of length one (kTPx509Basic, kCrlPolicy only!)
2798		 * FIXME: should this code work if the last cert in the chain is NOT a root?
2799		 */
2800		isLeaf = thisTpCertInfo->isLeaf();
2801		isRoot = thisTpCertInfo->isSelfSigned(true);
2802
2803		/*
2804		 * BasicConstraints.cA
2805		 * iSign:   	 required in all but leaf and root,
2806		 *          	 for which it is optional (with default values of false
2807		 *         	 	 for leaf and true for root).
2808		 * all others:   always optional, default of false for leaf and
2809		 *				 true for others
2810		 * All:     	 cA must be false for leaf, true for others
2811		 */
2812		if(!thisCertInfo->basicConstraints.present) {
2813			/*
2814			 * No basicConstraints present; infer a cA value if appropriate.
2815			 */
2816			if(isLeaf) {
2817				/* cool, use default; note that kTPx509Basic with
2818				 * certGroup length of one may take this case */
2819				cA = CSSM_FALSE;
2820			}
2821			else if(isRoot) {
2822				/* cool, use default */
2823				cA = CSSM_TRUE;
2824			}
2825			else {
2826				switch(policy) {
2827					default:
2828						/*
2829						 * not present, not leaf, not root....
2830						 * ....RFC2459 says this can not be a CA
2831						 */
2832						cA = CSSM_FALSE;
2833						break;
2834					case kTPiSign:
2835						/* required for iSign in this position */
2836						tpPolicyError("tp_policyVerify: no "
2837								"basicConstraints");
2838						if(thisTpCertInfo->addStatusCode(
2839								CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS)) {
2840							policyFail = CSSM_TRUE;
2841						}
2842						break;
2843				}
2844			}
2845		}	/* inferred a default value */
2846		else {
2847			/* basicConstraints present */
2848			#if		BASIC_CONSTRAINTS_MUST_BE_CRITICAL
2849			/* disabled for verisign compatibility */
2850			if(!thisCertInfo->basicConstraints.critical) {
2851				/* per RFC 2459 */
2852				tpPolicyError("tp_policyVerify: basicConstraints marked "
2853					"not critical");
2854				if(thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED)) {
2855					policyFail = CSSM_TRUE;
2856				}
2857			}
2858			#endif	/* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */
2859
2860			const CE_BasicConstraints *bcp =
2861				&thisCertInfo->basicConstraints.extnData->basicConstraints;
2862
2863			cA = bcp->cA;
2864
2865			/* Verify pathLenConstraint if present */
2866			if(!isLeaf &&							// leaf, certDex=0, don't care
2867			   cA && 								// p.l.c. only valid for CAs
2868			   bcp->pathLenConstraintPresent) {		// present?
2869				/*
2870				 * pathLenConstraint=0 legal for certDex 1 only
2871				 * pathLenConstraint=1 legal for certDex {1,2}
2872				 * etc.
2873				 */
2874				if(certDex > (bcp->pathLenConstraint + 1)) {
2875					tpPolicyError("tp_policyVerify: pathLenConstraint "
2876						"exceeded");
2877					if(thisTpCertInfo->addStatusCode(
2878							CSSMERR_APPLETP_PATH_LEN_CONSTRAINT)) {
2879						policyFail = CSSM_TRUE;
2880					}
2881				}
2882			}
2883		}
2884
2885		if(isLeaf) {
2886			/*
2887			 * Special cases to allow a chain of length 1, leaf and root
2888			 * both true, and for caller to override the "leaf can't be a CA"
2889			 * requirement when a CA cert is explicitly being evaluated as the
2890			 * leaf.
2891			 */
2892			if(cA && !isRoot &&
2893			   !(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
2894				tpPolicyError("tp_policyVerify: cA true for leaf");
2895				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
2896					policyFail = CSSM_TRUE;
2897				}
2898			}
2899		} else if(!cA) {
2900			tpPolicyError("tp_policyVerify: cA false for non-leaf");
2901			if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA)) {
2902				policyFail = CSSM_TRUE;
2903			}
2904		}
2905
2906		/*
2907		 * Authority Key Identifier optional
2908		 * iSign   		: only allowed in !root.
2909		 *           	  If present, must not be critical.
2910		 * all others   : ignored (though used later for chain verification)
2911		 */
2912		if((policy == kTPiSign) && thisCertInfo->authorityId.present) {
2913			if(isRoot) {
2914				tpPolicyError("tp_policyVerify: authorityId in root");
2915				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
2916					policyFail = CSSM_TRUE;
2917				}
2918			}
2919			if(thisCertInfo->authorityId.critical) {
2920				/* illegal per RFC 2459 */
2921				tpPolicyError("tp_policyVerify: authorityId marked "
2922					"critical");
2923				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID)) {
2924					policyFail = CSSM_TRUE;
2925				}
2926			}
2927		}
2928
2929		/*
2930		 * Subject Key Identifier optional
2931		 * iSign   		 : can't be critical.
2932		 * all others    : ignored (though used later for chain verification)
2933		 */
2934		if(thisCertInfo->subjectId.present) {
2935			if((policy == kTPiSign) && thisCertInfo->subjectId.critical) {
2936				tpPolicyError("tp_policyVerify: subjectId marked critical");
2937				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID)) {
2938					policyFail = CSSM_TRUE;
2939				}
2940			}
2941		}
2942
2943		/*
2944		 * Key Usage optional except required as noted
2945		 * iSign    	: required for non-root/non-leaf
2946		 *            	  Leaf cert : if present, usage = digitalSignature
2947		 *				  Exception : if leaf, and keyUsage not present,
2948		 *					          netscape-cert-type must be present, with
2949		 *							  Object Signing bit set
2950		 * kCrlPolicy   : Leaf: usage = CRLSign
2951		 * kTP_SMIME   	: if present, must be critical
2952		 * kTP_SWUpdateSign, kTP_ResourceSign, kTP_CodeSigning, kTP_PackageSigning : Leaf :
2953						  usage = digitalSignature
2954		 * all others   : non-leaf  : usage = keyCertSign
2955		 *			  	  Leaf : don't care
2956		 */
2957		if(thisCertInfo->keyUsage.present) {
2958			/*
2959			 * Leaf cert:
2960			 *    iSign and *Signing: usage = digitalSignature
2961			 *    all others : don't care
2962			 * Others:    usage = keyCertSign
2963			 * We only require that one bit to be set, we ignore others.
2964			 */
2965			if(isLeaf) {
2966				switch(policy) {
2967					case kTPiSign:
2968					case kTP_SWUpdateSign:
2969					case kTP_ResourceSign:
2970					case kTP_CodeSigning:
2971					case kTP_PackageSigning:
2972						expUsage = CE_KU_DigitalSignature;
2973						break;
2974					case kCrlPolicy:
2975						/* if present, this bit must be set */
2976						expUsage = CE_KU_CRLSign;
2977						break;
2978					default:
2979						/* accept whatever's there */
2980						expUsage = thisCertInfo->keyUsage.extnData->keyUsage;
2981						break;
2982				}
2983			}
2984			else {
2985				/* !leaf: this is true for all policies */
2986				expUsage = CE_KU_KeyCertSign;
2987			}
2988			actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
2989			if(!(actUsage & expUsage)) {
2990				tpPolicyError("tp_policyVerify: bad keyUsage (leaf %s; "
2991					"usage 0x%x)",
2992					(certDex == 0) ? "TRUE" : "FALSE", actUsage);
2993				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
2994					policyFail = CSSM_TRUE;
2995				}
2996			}
2997
2998			#if 0
2999			/*
3000			 * Radar 3523221 renders this whole check obsolete, but I'm leaving
3001			 * the code here to document its conspicuous functional absence.
3002			 */
3003			if((policy == kTP_SMIME) && !thisCertInfo->keyUsage.critical) {
3004				/*
3005				 * Per Radar 3410245, allow this for intermediate certs.
3006				 */
3007				if(SMIME_KEY_USAGE_MUST_BE_CRITICAL || isLeaf || isRoot) {
3008					tpPolicyError("tp_policyVerify: key usage, !critical, SMIME");
3009					if(thisTpCertInfo->addStatusCode(
3010							CSSMERR_APPLETP_SMIME_KEYUSAGE_NOT_CRITICAL)) {
3011						policyFail = CSSM_TRUE;
3012					}
3013				}
3014			}
3015			#endif
3016		}
3017		else if(policy == kTPiSign) {
3018			/*
3019			 * iSign requires keyUsage present for non root OR
3020			 * netscape-cert-type/ObjectSigning for leaf
3021			 */
3022			if(isLeaf && thisCertInfo->netscapeCertType.present) {
3023				CE_NetscapeCertType ct =
3024					thisCertInfo->netscapeCertType.extnData->netscapeCertType;
3025
3026				if(!(ct & CE_NCT_ObjSign)) {
3027					tpPolicyError("tp_policyVerify: netscape-cert-type, "
3028						"!ObjectSign");
3029					if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
3030						policyFail = CSSM_TRUE;
3031					}
3032				}
3033			}
3034			else if(!isRoot) {
3035				tpPolicyError("tp_policyVerify: !isRoot, no keyUsage, "
3036					"!(leaf and netscapeCertType)");
3037				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
3038					policyFail = CSSM_TRUE;
3039				}
3040			}
3041		}
3042
3043		/*
3044		 * RFC 3280, 4.1.2.6, says that an empty subject name can only appear in a
3045		 * leaf cert, and only if subjectAltName is present and marked critical.
3046		 */
3047		if(isLeaf && thisTpCertInfo->hasEmptySubjectName()) {
3048			bool badEmptySubject = false;
3049			if(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) {
3050				/*
3051				 * True when evaluating a CA cert as well as when
3052				 * evaluating a CRL's cert chain. Note the odd case of a CRL's
3053				 * signer having an empty subject matching an empty issuer
3054				 * in the CRL. That'll be caught here.
3055				 */
3056				badEmptySubject = true;
3057			}
3058			else if(!thisCertInfo->subjectAltName.present ||	/* no subjectAltName */
3059					!thisCertInfo->subjectAltName.critical) {	/* not critical */
3060				badEmptySubject = true;
3061			}
3062			if(badEmptySubject) {
3063				tpPolicyError("tp_policyVerify: bad empty subject");
3064				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT)) {
3065					policyFail = CSSM_TRUE;
3066				}
3067			}
3068		}
3069
3070		/*
3071 		 * RFC 3739: if this cert has a Qualified Cert Statements extension, and
3072		 * it's Critical, make sure we understand all of the extension's statementIds.
3073		 */
3074		if(thisCertInfo->qualCertStatements.present &&
3075		   thisCertInfo->qualCertStatements.critical) {
3076			CE_QC_Statements *qcss =
3077				&thisCertInfo->qualCertStatements.extnData->qualifiedCertStatements;
3078			uint32 numQcs = qcss->numQCStatements;
3079			for(unsigned qdex=0; qdex<numQcs; qdex++) {
3080				CSSM_OID_PTR qid = &qcss->qcStatements[qdex].statementId;
3081				bool ok = false;
3082				for(unsigned kdex=0; kdex<NUM_KNOWN_QUAL_CERT_STATEMENTS; kdex++) {
3083					if(tpCompareCssmData(qid, knownQualifiedCertStatements[kdex])) {
3084						ok = true;
3085						break;
3086					}
3087				}
3088				if(!ok) {
3089					if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT)) {
3090						policyFail = CSSM_TRUE;
3091						break;
3092					}
3093				}
3094			}
3095		}	/* critical Qualified Cert Statement */
3096
3097		/*
3098		 * Certificate Policies extension validation, per section 1.2 of:
3099		 * http://iase.disa.mil/pki/dod_cp_v10_final_2_mar_09_signed.pdf
3100		 */
3101		if (tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH, false) ||
3102			tpVerifyCPE(*thisCertInfo, CSSMOID_PIV_AUTH_2048, false)) {
3103			/*
3104			 * Certificate asserts one of the PIV-Auth Certificate Policy OIDs;
3105			 * check the required Key Usage extension for compliance.
3106			 *
3107			 * Leaf cert:
3108			 *    usage = digitalSignature (only; no other bits asserted)
3109			 * Others:
3110			 *    usage = keyCertSign (required; other bits ignored)
3111			 */
3112			if(thisCertInfo->keyUsage.present) {
3113				actUsage = thisCertInfo->keyUsage.extnData->keyUsage;
3114			} else {
3115				/* No key usage! Policy fail. */
3116				actUsage = 0;
3117			}
3118			if(!(actionFlags & CSSM_TP_ACTION_LEAF_IS_CA) && (certDex == 0)) {
3119				expUsage = CE_KU_DigitalSignature;
3120			} else {
3121				expUsage = actUsage | CE_KU_KeyCertSign;
3122			}
3123			if(!(actUsage == expUsage)) {
3124				tpPolicyError("tp_policyVerify: bad keyUsage for PIV-Auth policy (leaf %s; "
3125					"usage 0x%x)",
3126					(certDex == 0) ? "TRUE" : "FALSE", actUsage);
3127				if(thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE)) {
3128					policyFail = CSSM_TRUE;
3129				}
3130			}
3131		}	/* Certificate Policies */
3132
3133
3134	}	/* for certDex, checking presence of extensions */
3135
3136	/*
3137	 * Special case checking for leaf (end entity) cert
3138	 *
3139	 * iSign only: Extended key usage, optional for leaf,
3140	 * value CSSMOID_ExtendedUseCodeSigning
3141	 */
3142	if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) {
3143		extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage;
3144		if(extendUsage->numPurposes != 1) {
3145			tpPolicyError("tp_policyVerify: bad extendUsage->numPurposes "
3146				"(%d)",
3147				(int)extendUsage->numPurposes);
3148			if((certGroup->certAtIndex(0))->addStatusCode(
3149					CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
3150				policyFail = CSSM_TRUE;
3151			}
3152		}
3153		if(!tpCompareOids(extendUsage->purposes,
3154				&CSSMOID_ExtendedUseCodeSigning)) {
3155			tpPolicyError("tp_policyVerify: bad extendKeyUsage");
3156			if((certGroup->certAtIndex(0))->addStatusCode(
3157					CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE)) {
3158				policyFail = CSSM_TRUE;
3159			}
3160		}
3161	}
3162
3163	/*
3164	 * Verify authorityId-->subjectId linkage.
3165	 * All optional - skip if needed fields not present.
3166	 * Also, always skip last (root) cert.
3167	 */
3168	for(certDex=0; certDex<(numCerts-1); certDex++) {
3169		if(!certInfo[certDex].authorityId.present ||
3170		   !certInfo[certDex+1].subjectId.present) {
3171		 	continue;
3172		}
3173		authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID;
3174		if(!authorityId->keyIdentifierPresent) {
3175			/* we only know how to compare keyIdentifier */
3176			continue;
3177		}
3178		if(!tpCompareCssmData(&authorityId->keyIdentifier,
3179				&certInfo[certDex+1].subjectId.extnData->subjectKeyID)) {
3180			tpPolicyError("tp_policyVerify: bad key ID linkage");
3181			if((certGroup->certAtIndex(certDex))->addStatusCode(
3182					CSSMERR_APPLETP_INVALID_ID_LINKAGE)) {
3183				policyFail = CSSM_TRUE;
3184			}
3185		}
3186	}
3187
3188	/*
3189	 * Check signature algorithm on all non-root certs,
3190	 * reject if known to be untrusted
3191	 */
3192	for(certDex=0; certDex<(numCerts-1); certDex++) {
3193		if(certInfo[certDex].untrustedSigAlg) {
3194			tpPolicyError("tp_policyVerify: untrusted signature algorithm");
3195			if((certGroup->certAtIndex(certDex))->addStatusCode(
3196					CSSMERR_TP_INVALID_CERTIFICATE)) {
3197				policyFail = CSSM_TRUE;
3198			}
3199		}
3200	}
3201
3202	/* specific per-policy checking */
3203	switch(policy) {
3204		case kTP_SSL:
3205		case kTP_EAP:
3206		case kTP_IPSec:
3207			/*
3208			 * SSL, EAP, IPSec: optionally verify common name; all are identical
3209			 * other than their names.
3210			 * FIXME - should this be before or after the root cert test? How can
3211			 * we return both errors?
3212			 */
3213			policyError = tp_verifySslOpts(policy, *certGroup, policyFieldData, certInfo);
3214			break;
3215
3216		case kTP_iChat:
3217			tpDebug("iChat policy");
3218			/* fall thru */
3219		case kTP_SMIME:
3220			policyError = tp_verifySmimeOpts(policy, *certGroup, policyFieldData, certInfo);
3221			break;
3222		case kTP_SWUpdateSign:
3223			policyError = tp_verifySWUpdateSigningOpts(*certGroup, policyFieldData, certInfo);
3224			break;
3225		case kTP_ResourceSign:
3226			policyError = tp_verifyResourceSigningOpts(*certGroup, policyFieldData, certInfo);
3227			break;
3228		case kTP_CodeSigning:
3229		case kTP_PackageSigning:
3230			policyError = tp_verifyCodePkgSignOpts(policy, *certGroup, policyFieldData, certInfo);
3231			break;
3232		case kTP_MacAppStoreRec:
3233			policyError = tp_verifyMacAppStoreReceiptOpts(*certGroup, policyFieldData, certInfo);
3234			break;
3235		case kTP_AppleIDSharing:
3236			policyError = tp_verifyAppleIDSharingOpts(*certGroup, policyFieldData, certInfo);
3237			break;
3238		case kTP_TimeStamping:
3239			policyError = tp_verifyTimeStampingOpts(*certGroup, policyFieldData, certInfo);
3240			break;
3241		case kTP_PassbookSigning:
3242			policyError = tp_verifyPassbookSigningOpts(*certGroup, policyFieldData, certInfo);
3243			break;
3244		case kTP_MobileStore:
3245			policyError = tp_verifyMobileStoreSigningOpts(*certGroup, policyFieldData, certInfo, false);
3246			break;
3247		case kTP_TestMobileStore:
3248			policyError = tp_verifyMobileStoreSigningOpts(*certGroup, policyFieldData, certInfo, true);
3249			break;
3250		case kTP_EscrowService:
3251			policyError = tp_verifyEscrowServiceSigningOpts(*certGroup, policyFieldData, certInfo);
3252			break;
3253		case kTP_ProfileSigning:
3254			policyError = tp_verifyProfileSigningOpts(*certGroup, policyFieldData, certInfo, false);
3255			break;
3256		case kTP_QAProfileSigning:
3257			policyError = tp_verifyProfileSigningOpts(*certGroup, policyFieldData, certInfo, true);
3258			break;
3259		case kTP_PCSEscrowService:
3260			policyError = tp_verifyPCSEscrowServiceSigningOpts(*certGroup, policyFieldData, certInfo);
3261			break;
3262		case kTPx509Basic:
3263		case kTPiSign:
3264		case kCrlPolicy:
3265		case kTP_PKINIT_Client:
3266		default:
3267			break;
3268
3269	}
3270
3271	if(outErr == CSSM_OK) {
3272		/* policy-specific error takes precedence here */
3273		if(policyError != CSSM_OK) {
3274			outErr = policyError;
3275		}
3276		else if(policyFail) {
3277			/* plain vanilla error return from this module */
3278			outErr = CSSMERR_TP_VERIFY_ACTION_FAILED;
3279		}
3280	}
3281errOut:
3282	/* free resources */
3283	for(certDex=0; certDex<numCerts; certDex++) {
3284		thisCertInfo = &certInfo[certDex];
3285		iSignFreeCertInfo(clHand, thisCertInfo);
3286	}
3287	tpFree(alloc, certInfo);
3288	return outErr;
3289}
3290
3291/*
3292 * Obtain policy-specific User Trust parameters
3293 */
3294void tp_policyTrustSettingParams(
3295	TPPolicy				policy,
3296	const CSSM_DATA			*policyData,		// optional
3297	/* returned values - not mallocd */
3298	const char				**policyStr,
3299	uint32					*policyStrLen,
3300	SecTrustSettingsKeyUsage	*keyUse)
3301{
3302	/* default values */
3303	*policyStr = NULL;
3304	*keyUse = kSecTrustSettingsKeyUseAny;
3305
3306	if((policyData == NULL) || (policyData->Data == NULL)) {
3307		/* currently, no further action possible */
3308		return;
3309	}
3310	switch(policy) {
3311		case kTP_SSL:
3312		case kTP_EAP:
3313		case kTP_IPSec:
3314		{
3315			if(policyData->Length != sizeof(CSSM_APPLE_TP_SSL_OPTIONS)) {
3316				/* this error will be caught later */
3317				return;
3318			}
3319			CSSM_APPLE_TP_SSL_OPTIONS *sslOpts =
3320				(CSSM_APPLE_TP_SSL_OPTIONS *)policyData->Data;
3321			*policyStr = sslOpts->ServerName;
3322			*policyStrLen = sslOpts->ServerNameLen;
3323			if(sslOpts->Flags & CSSM_APPLE_TP_SSL_CLIENT) {
3324				/*
3325				 * Client signs with its priv key. Server end,
3326				 * which (also) verifies the client cert, verifies.
3327				 */
3328				*keyUse = kSecTrustSettingsKeyUseSignature;
3329			}
3330			else {
3331				/* server decrypts */
3332				*keyUse = kSecTrustSettingsKeyUseEnDecryptKey;
3333			}
3334			return;
3335		}
3336
3337		case kTP_iChat:
3338		case kTP_SMIME:
3339		{
3340			if(policyData->Length != sizeof(CSSM_APPLE_TP_SMIME_OPTIONS)) {
3341				/* this error will be caught later */
3342				return;
3343			}
3344			CSSM_APPLE_TP_SMIME_OPTIONS *smimeOpts =
3345				(CSSM_APPLE_TP_SMIME_OPTIONS *)policyData->Data;
3346			*policyStr = smimeOpts->SenderEmail;
3347			*policyStrLen = smimeOpts->SenderEmailLen;
3348			SecTrustSettingsKeyUsage ku = 0;
3349			CE_KeyUsage smimeKu = smimeOpts->IntendedUsage;
3350			if(smimeKu & (CE_KU_DigitalSignature | CE_KU_KeyCertSign | CE_KU_CRLSign)) {
3351				ku |= kSecTrustSettingsKeyUseSignature;
3352			}
3353			if(smimeKu & (CE_KU_KeyEncipherment | CE_KU_DataEncipherment)) {
3354				ku |= kSecTrustSettingsKeyUseEnDecryptKey;
3355			}
3356			*keyUse = ku;
3357			return;
3358		}
3359
3360		default:
3361			/* no other options */
3362			return;
3363	}
3364}
3365
3366#pragma clang diagnostic pop
3367