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