1/*
2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19/*
20 * tpCertGroup.cpp - Cert group functions (construct, verify)
21 */
22
23#include "AppleTPSession.h"
24#include "certGroupUtils.h"
25#include "TPCertInfo.h"
26#include "TPCrlInfo.h"
27#include "tpPolicies.h"
28#include "tpdebugging.h"
29#include "tpCrlVerify.h"
30#include <Security/oidsalg.h>
31#include <Security/cssmapple.h>
32
33/*
34 * This is a temporary hack to allow verification of PKINIT server certs
35 * which are self-signed and not in the system anchors list. If the self-
36 * signed cert is in a magic keychain (whose location is not published),
37 * we'll allow it as if it were indeed a full-fledged anchor cert.
38 */
39#define TP_PKINIT_SERVER_HACK	1
40#if		TP_PKINIT_SERVER_HACK
41
42#include <Security/SecKeychain.h>
43#include <Security/SecKeychainSearch.h>
44#include <Security/SecCertificate.h>
45#include <Security/oidscert.h>
46#include <sys/types.h>
47#include <pwd.h>
48
49#define CFRELEASE(cf)	if(cf) { CFRelease(cf); }
50
51/*
52 * Returns true if we are to allow/trust the specified
53 * cert as a PKINIT-only anchor.
54 */
55static bool tpCheckPkinitServerCert(
56	TPCertGroup &certGroup)
57{
58	/*
59	 * Basic requirement: exactly one cert, self-signed.
60	 * The numCerts == 1 requirement might change...
61	 */
62	unsigned numCerts = certGroup.numCerts();
63	if(numCerts != 1) {
64		tpDebug("tpCheckPkinitServerCert: too many certs");
65		return false;
66	}
67	/* end of chain... */
68	TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1);
69	if(!theCert->isSelfSigned()) {
70		tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed");
71		return false;
72	}
73	const CSSM_DATA *subjectName = theCert->subjectName();
74
75	/*
76	 * Open the magic keychain.
77	 * We're going up and over the Sec layer here, not generally
78	 * kosher, but this is a hack.
79	 */
80	OSStatus ortn;
81	SecKeychainRef kcRef = NULL;
82	string fullPathName;
83	const char *homeDir = getenv("HOME");
84	if (homeDir == NULL)
85	{
86		// If $HOME is unset get the current user's home directory
87		// from the passwd file.
88		uid_t uid = geteuid();
89		if (!uid) uid = getuid();
90		struct passwd *pw = getpwuid(uid);
91		if (!pw) {
92			return false;
93		}
94		homeDir = pw->pw_dir;
95	}
96	fullPathName = homeDir;
97	fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain";
98	ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef);
99	if(ortn) {
100		tpDebug("tpCheckPkinitServerCert: keychain not found (1)");
101		return false;
102	}
103	/* subsequent errors to errOut: */
104
105	bool ourRtn = false;
106	SecKeychainStatus kcStatus;
107	CSSM_DATA_PTR subjSerial = NULL;
108	CSSM_RETURN crtn;
109	SecKeychainSearchRef		srchRef = NULL;
110	SecKeychainAttributeList	attrList;
111	SecKeychainAttribute		attrs[2];
112	SecKeychainItemRef			foundItem = NULL;
113
114	ortn = SecKeychainGetStatus(kcRef, &kcStatus);
115	if(ortn) {
116		tpDebug("tpCheckPkinitServerCert: keychain not found (2)");
117		goto errOut;
118	}
119
120	/*
121	 * We already have this cert's normalized name; get its
122	 * serial number.
123	 */
124	crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
125	if(crtn) {
126		/* should never happen */
127		tpDebug("tpCheckPkinitServerCert: error fetching serial number");
128		goto errOut;
129	}
130
131	attrs[0].tag    = kSecSubjectItemAttr;
132	attrs[0].length = (UInt32)subjectName->Length;
133	attrs[0].data   = subjectName->Data;
134	attrs[1].tag    = kSecSerialNumberItemAttr;
135	attrs[1].length = (UInt32)subjSerial->Length;
136	attrs[1].data   = subjSerial->Data;
137	attrList.count  = 2;
138	attrList.attr   = attrs;
139
140	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
141		kSecCertificateItemClass,
142		&attrList,
143		&srchRef);
144	if(ortn) {
145		tpDebug("tpCheckPkinitServerCert: search failure");
146		goto errOut;
147	}
148	for(;;) {
149		ortn = SecKeychainSearchCopyNext(srchRef, &foundItem);
150		if(ortn) {
151			tpDebug("tpCheckPkinitServerCert: end search");
152			break;
153		}
154
155		/* found a matching cert; do byte-for-byte compare */
156		CSSM_DATA certData;
157		ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData);
158		if(ortn) {
159			tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure");
160			continue;
161		}
162		if(tpCompareCssmData(&certData, theCert->itemData())){
163			tpDebug("tpCheckPkinitServerCert: FOUND CERT");
164			ourRtn = true;
165			break;
166		}
167		tpDebug("tpCheckPkinitServerCert: skipping matching cert");
168		CFRelease(foundItem);
169		foundItem = NULL;
170	}
171errOut:
172	CFRELEASE(kcRef);
173	CFRELEASE(srchRef);
174	CFRELEASE(foundItem);
175	if(subjSerial != NULL) {
176		theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
177	}
178	return ourRtn;
179}
180#endif	/* TP_PKINIT_SERVER_HACK */
181
182
183/*-----------------------------------------------------------------------------
184 * CertGroupConstruct
185 *
186 * Description:
187 *   This function returns a pointer to a mallocd CSSM_CERTGROUP which
188 *   refers to a mallocd list of raw ordered X.509 certs which verify back as
189 *   far as the TP is able to go. The first cert of the returned list is the
190 *   subject cert. The TP will attempt to search thru the DBs passed in
191 *   DBList in order to complete the chain. The chain is completed when a
192 *   self-signed (root) cert is found in the chain. The root cert may be
193 *   present in the input CertGroupFrag, or it may have been obtained from
194 *   one of the DBs passed in DBList. It is not an error if no root cert is
195 *   found.
196 *
197 *   The error conditions are:
198 *   -- The first cert of CertGroupFrag is an invalid cert. NULL is returned,
199 *		err = CSSM_TP_INVALID_CERTIFICATE.
200 *   -- The root cert (if found) fails to verify. Valid certgroup is returned,
201 *      err = CSSMERR_TP_VERIFICATION_FAILURE.
202 *   -- Any cert in the (possibly partially) constructed chain has expired or
203 *      isn't valid yet, err = CSSMERR_TP_CERT_EXPIRED or
204 *      CSSMERR_TP_CERT_NOT_VALID_YET. A CertGroup is returned.
205 *   -- CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET. If one of these
206 *		conditions obtains for the first (leaf) cert, the function throws this
207 *		error immediately and the outgoing cert group is empty. For subsequent certs,
208 *		the temporal validity of a cert is only tested AFTER a cert successfully
209 *		meets the cert chaining criteria (subject/issuer match and signature
210 *		verify). A cert in a chain with this error is not added to the outgoing
211 *		cert group.
212 *   -- the usual errors like bad handle or memory failure.
213 *
214 * Parameters:
215 *   Two handles - to an open CL and CSP. The CSP must be capable of
216 *   dealing with the signature algorithms used by the certs. The CL must be
217 *   an X.509-savvy CL.
218 *
219 *   CertGroupFrag, an unordered array of raw X.509 certs in the form of a
220 *   CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert
221 *   which is eventually to be verified. The other certs can be in any order
222 *   and may not even have any relevance to the cert chain being constructed.
223 *   They may also be invalid certs.
224 *
225 *   DBList, a list of DB/DL handles which may contain certs necessary to
226 *   complete the desired cert chain. (Not currently implemented.)
227 *
228 *---------------------------------------------------------------------------*/
229
230/* public version */
231void AppleTPSession::CertGroupConstruct(CSSM_CL_HANDLE clHand,
232		CSSM_CSP_HANDLE cspHand,
233		const CSSM_DL_DB_LIST &DBList,
234		const void *ConstructParams,
235		const CSSM_CERTGROUP &CertGroupFrag,
236		CSSM_CERTGROUP_PTR &CertGroup)
237{
238	TPCertGroup outCertGroup(*this, TGO_Caller);
239	TPCertGroup inCertGroup(CertGroupFrag,
240		clHand,
241		cspHand,
242		*this,
243		NULL,		// cssmTimeStr
244		true, 		// firstCertMustBeValid
245		TGO_Group);
246
247	/* set up for disposal of TPCertInfos created by CertGroupConstructPriv */
248	TPCertGroup			gatheredCerts(*this, TGO_Group);
249
250	CSSM_RETURN constructReturn = CSSM_OK;
251	CSSM_APPLE_TP_ACTION_FLAGS	actionFlags = 0;
252	CSSM_BOOL verifiedToRoot;		// not used
253	CSSM_BOOL verifiedToAnchor;		// not used
254	CSSM_BOOL verifiedViaTrustSetting;	// not used
255
256	try {
257		CertGroupConstructPriv(clHand,
258			cspHand,
259			inCertGroup,
260			&DBList,
261			NULL,				// cssmTimeStr
262			/* no anchors */
263			0, NULL,
264			actionFlags,
265			/* no user trust */
266			NULL, NULL, 0, 0,
267			gatheredCerts,
268			verifiedToRoot,
269			verifiedToAnchor,
270			verifiedViaTrustSetting,
271			outCertGroup);
272	}
273	catch(const CssmError &cerr) {
274		constructReturn = cerr.error;
275		/* abort if no certs found */
276		if(outCertGroup.numCerts() == 0) {
277			CssmError::throwMe(constructReturn);
278		}
279	}
280	CertGroup = outCertGroup.buildCssmCertGroup();
281	/* caller of this function never gets evidence... */
282	outCertGroup.freeDbRecords();
283
284	if(constructReturn) {
285		CssmError::throwMe(constructReturn);
286	}
287}
288
289
290/*
291 * Private version of CertGroupConstruct, used by CertGroupConstruct and
292 * CertGroupVerify. Populates a TP-style TPCertGroup for further processing.
293 * This only throws CSSM-style exceptions in the following cases:
294 *
295 *  -- input parameter errors
296 *  -- the first (leaf) cert is bad (doesn't parse, expired, not valid yet).
297 *  -- root found but it doesn't self-verify
298 *
299 *  All other cert-related errors simply result in the bad cert being ignored.
300 *  Other exceptions are gross system errors like malloc failure.
301 */
302void AppleTPSession::CertGroupConstructPriv(CSSM_CL_HANDLE clHand,
303		CSSM_CSP_HANDLE 		cspHand,
304		TPCertGroup 			&inCertGroup,
305		const CSSM_DL_DB_LIST 	*DBList,			// optional here
306		const char 				*cssmTimeStr,		// optional
307
308		/* trusted anchors, optional */
309		/* FIXME - maybe this should be a TPCertGroup */
310		uint32 					numAnchorCerts,
311		const CSSM_DATA			*anchorCerts,
312
313		/* CSSM_TP_ACTION_FETCH_CERT_FROM_NET, CSSM_TP_ACTION_TRUST_SETTINGS */
314		CSSM_APPLE_TP_ACTION_FLAGS	actionFlags,
315
316		/* optional user trust parameters */
317		const CSSM_OID			*policyOid,
318		const char				*policyStr,
319		uint32					policyStrLen,
320		SecTrustSettingsKeyUsage	keyUse,
321
322		/*
323		 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
324		 * as a result of using a cert from anchorCerts or dbList) are added
325		 * to this group.
326		 */
327		TPCertGroup				&certsToBeFreed,
328
329		/* returned */
330		CSSM_BOOL				&verifiedToRoot,		// end of chain self-verifies
331		CSSM_BOOL				&verifiedToAnchor,		// end of chain in anchors
332		CSSM_BOOL				&verifiedViaTrustSetting,	// chain ends per User Trust setting
333		TPCertGroup 			&outCertGroup)			// RETURNED
334{
335	TPCertInfo			*subjectCert;				// the one we're working on
336	CSSM_RETURN			outErr = CSSM_OK;
337
338	/* this'll be the first subject cert in the main loop */
339	subjectCert = inCertGroup.certAtIndex(0);
340
341	/* Append leaf cert to outCertGroup */
342	outCertGroup.appendCert(subjectCert);
343	subjectCert->isLeaf(true);
344	subjectCert->isFromInputCerts(true);
345	outCertGroup.setAllUnused();
346	subjectCert->used(true);
347
348	outErr = outCertGroup.buildCertGroup(
349		*subjectCert,
350		&inCertGroup,
351		DBList,
352		clHand,
353		cspHand,
354		cssmTimeStr,
355		numAnchorCerts,
356		anchorCerts,
357		certsToBeFreed,
358		&certsToBeFreed,	// gatheredCerts to accumulate net/DB fetches
359		CSSM_TRUE,			// subjectIsInGroup - enables root check on
360							//    subject cert
361		actionFlags,
362		policyOid,
363		policyStr,
364		policyStrLen,
365		keyUse,
366
367		verifiedToRoot,
368		verifiedToAnchor,
369		verifiedViaTrustSetting);
370	if(outErr) {
371		CssmError::throwMe(outErr);
372	}
373}
374
375/*
376 * Map a policy OID to one of the standard (non-revocation) policies.
377 * Returns true if it's a standard policy.
378 */
379static bool checkPolicyOid(
380	const CSSM_OID	&oid,
381	TPPolicy		&tpPolicy)		/* RETURNED */
382{
383	if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SSL)) {
384		tpPolicy = kTP_SSL;
385		return true;
386	}
387	else if(tpCompareOids(&oid, &CSSMOID_APPLE_X509_BASIC)) {
388		tpPolicy = kTPx509Basic;
389		return true;
390	}
391	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SMIME)) {
392		tpPolicy = kTP_SMIME;
393		return true;
394	}
395	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_EAP)) {
396		tpPolicy = kTP_EAP;
397		return true;
398	}
399	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_SW_UPDATE_SIGNING)) {
400		/* note: this was CSSMOID_APPLE_TP_CODE_SIGN until 8/15/06 */
401		tpPolicy = kTP_SWUpdateSign;
402		return true;
403	}
404	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_RESOURCE_SIGN)) {
405		tpPolicy = kTP_ResourceSign;
406		return true;
407	}
408	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_IP_SEC)) {
409		tpPolicy = kTP_IPSec;
410		return true;
411	}
412	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_ICHAT)) {
413		tpPolicy = kTP_iChat;
414		return true;
415	}
416	else if(tpCompareOids(&oid, &CSSMOID_APPLE_ISIGN)) {
417		tpPolicy = kTPiSign;
418		return true;
419	}
420	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PKINIT_CLIENT)) {
421		tpPolicy = kTP_PKINIT_Client;
422		return true;
423	}
424	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PKINIT_SERVER)) {
425		tpPolicy = kTP_PKINIT_Server;
426		return true;
427	}
428	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_CODE_SIGNING)) {
429		tpPolicy = kTP_CodeSigning;
430		return true;
431	}
432	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PACKAGE_SIGNING)) {
433		tpPolicy = kTP_PackageSigning;
434		return true;
435	}
436	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_MACAPPSTORE_RECEIPT)) {
437		tpPolicy = kTP_MacAppStoreRec;
438		return true;
439	}
440	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_APPLEID_SHARING)) {
441		tpPolicy = kTP_AppleIDSharing;
442		return true;
443	}
444	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_TIMESTAMPING)) {
445		tpPolicy = kTP_TimeStamping;
446		return true;
447	}
448	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PASSBOOK_SIGNING)) {
449		tpPolicy = kTP_PassbookSigning;
450		return true;
451	}
452	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_MOBILE_STORE)) {
453		tpPolicy = kTP_MobileStore;
454		return true;
455	}
456	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_TEST_MOBILE_STORE)) {
457		tpPolicy = kTP_TestMobileStore;
458		return true;
459	}
460	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_ESCROW_SERVICE)) {
461		tpPolicy = kTP_EscrowService;
462		return true;
463	}
464	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PROFILE_SIGNING)) {
465		tpPolicy = kTP_ProfileSigning;
466		return true;
467	}
468	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_QA_PROFILE_SIGNING)) {
469		tpPolicy = kTP_QAProfileSigning;
470		return true;
471	}
472	else if(tpCompareOids(&oid, &CSSMOID_APPLE_TP_PCS_ESCROW_SERVICE)) {
473		tpPolicy = kTP_PCSEscrowService;
474		return true;
475	}
476	return false;
477}
478
479/*-----------------------------------------------------------------------------
480 * CertGroupVerify
481 *
482 * Description:
483 *   -- Construct a cert chain using TP_CertGroupConstruct.
484 *   -- Attempt to verify that cert chain against one of the known
485 *      good certs passed in AnchorCerts.
486 *   -- Optionally enforces additional policies (TBD) when verifying the cert chain.
487 *   -- Optionally returns the entire cert chain constructed in
488 *      TP_CertGroupConstruct and here, all the way to an anchor cert or as
489 *      far as we were able to go, in *Evidence.
490 *
491 * Parameters:
492 *   Two handles - to an open CL and CSP. The CSP must be capable of
493 *   dealing with the signature algorithms used by the certs. The CL must be
494 *   an X.509-savvy CL.
495 *
496 *   RawCerts, an unordered array of raw certs in the form of a
497 *   CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert
498 *   which is eventually to be verified. The other certs can be in any order
499 *   and may not even have any relevance to the cert chain being constructed.
500 *   They may also be invalid certs.
501 *
502 *   DBList, a list of DB/DL handles which may contain certs necessary to
503 *   complete the desired cert chain. (Currently not implemented.)
504 *
505 *   AnchorCerts, a list of known trusted certs.
506 *   NumberOfAnchorCerts, size of AnchorCerts array.
507 *
508 *   PolicyIdentifiers, Optional policy OID. NULL indicates default
509 *		X.509 trust policy.
510 *
511 *	 Supported Policies:
512 *			CSSMOID_APPLE_ISIGN
513 *			CSSMOID_APPLE_X509_BASIC
514 *
515 *			For both of these, the associated FieldValue must be {0, NULL},
516 *
517 *   NumberOfPolicyIdentifiers, size of PolicyIdentifiers array, must be
518 *      zero or one.
519 *
520 *   All other arguments must be zero/NULL.
521 *
522 *   Returns:
523 *      CSSM_OK : cert chain verified all the way back to an AnchorCert.
524 *      CSSMERR_TP_INVALID_ANCHOR_CERT : In this case, the cert chain
525 *   		was validated back to a self-signed (root) cert found in either
526 *   		CertToBeVerified or in one of the DBs in DBList, but that root cert
527 *   		was *NOT* found in the AnchorCert list.
528 *		CSSMERR_TP_NOT_TRUSTED: no root cert was found and no AnchorCert
529 *   		verified the end of the constructed cert chain.
530 *		CSSMERR_TP_VERIFICATION_FAILURE: a root cert was found which does
531 *   		not self-verify.
532 *   	CSSMERR_TP_VERIFY_ACTION_FAILED: indicates a failure of the requested
533 *			policy action.
534 *   	CSSMERR_TP_INVALID_CERTIFICATE: indicates a bad leaf cert.
535 *		CSSMERR_TP_INVALID_REQUEST_INPUTS : no incoming VerifyContext.
536 *		CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET: see comments
537 *			for CertGroupConstruct.
538 *		CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial
539 *			public key, rendering full verification impossible.
540 *   	CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial
541 *			public key and which failed to perform subsequent signature
542 *			verification.
543 *---------------------------------------------------------------------------*/
544
545void AppleTPSession::CertGroupVerify(CSSM_CL_HANDLE clHand,
546		CSSM_CSP_HANDLE cspHand,
547		const CSSM_CERTGROUP &CertGroupToBeVerified,
548		const CSSM_TP_VERIFY_CONTEXT *VerifyContext,
549		CSSM_TP_VERIFY_CONTEXT_RESULT_PTR VerifyContextResult)
550{
551	CSSM_BOOL				verifiedToRoot = CSSM_FALSE;
552	CSSM_BOOL				verifiedToAnchor = CSSM_FALSE;
553	CSSM_BOOL				verifiedViaTrustSetting = CSSM_FALSE;
554	CSSM_RETURN				constructReturn = CSSM_OK;
555	CSSM_RETURN				policyReturn = CSSM_OK;
556	const CSSM_TP_CALLERAUTH_CONTEXT *cred;
557	/* declare volatile as compiler workaround to avoid caching in CR4 */
558	const CSSM_APPLE_TP_ACTION_DATA * volatile actionData = NULL;
559	CSSM_TIMESTRING			cssmTimeStr;
560	CSSM_APPLE_TP_ACTION_FLAGS	actionFlags = 0;
561	CSSM_TP_STOP_ON 		tpStopOn = 0;
562
563	/* keep track of whether we did policy checking; if not, we do defaults */
564	bool					didCertPolicy = false;
565	bool					didRevokePolicy = false;
566
567	/* user trust parameters */
568	CSSM_OID				utNullPolicy = {0, NULL};
569	const CSSM_OID			*utPolicyOid = NULL;
570	const char				*utPolicyStr = NULL;
571	uint32					utPolicyStrLen = 0;
572	SecTrustSettingsKeyUsage	utKeyUse = 0;
573	bool					utTrustSettingEnabled = false;
574
575	if(VerifyContextResult) {
576		memset(VerifyContextResult, 0, sizeof(*VerifyContextResult));
577	}
578
579	/* verify input args, skipping the ones checked by CertGroupConstruct */
580	if((VerifyContext == NULL) || (VerifyContext->Cred == NULL)) {
581		/* the spec says that this is optional but we require it */
582			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
583	}
584	cred = VerifyContext->Cred;
585
586	/* Optional ActionData affecting all policies */
587	actionData = (CSSM_APPLE_TP_ACTION_DATA * volatile)VerifyContext->ActionData.Data;
588	if(actionData != NULL) {
589		switch(actionData->Version) {
590			case CSSM_APPLE_TP_ACTION_VERSION:
591				if(VerifyContext->ActionData.Length !=
592						sizeof(CSSM_APPLE_TP_ACTION_DATA)) {
593					CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA);
594				}
595				break;
596			/* handle backwards versions here if we ever go beyond version 0 */
597			default:
598				CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA);
599		}
600		actionFlags = actionData->ActionFlags;
601		if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) {
602			utTrustSettingEnabled = true;
603		}
604	}
605
606	/* optional, may be NULL */
607	cssmTimeStr = cred->VerifyTime;
608
609	tpStopOn = cred->VerificationAbortOn;
610	switch(tpStopOn) {
611		/* the only two we support */
612		case CSSM_TP_STOP_ON_NONE:
613		case CSSM_TP_STOP_ON_FIRST_FAIL:
614			break;
615		/* default maps to stop on first fail */
616		case CSSM_TP_STOP_ON_POLICY:
617			tpStopOn = CSSM_TP_STOP_ON_FIRST_FAIL;
618			break;
619		default:
620			CssmError::throwMe(CSSMERR_TP_INVALID_STOP_ON_POLICY);
621	}
622
623	/* now the args we can't deal with */
624	if(cred->CallerCredentials != NULL) {
625			CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
626	}
627	/* ...any others? */
628
629	/* set up for optional user trust evaluation */
630	if(utTrustSettingEnabled) {
631		const CSSM_TP_POLICYINFO *pinfo = &cred->Policy;
632		TPPolicy utPolicy = kTPx509Basic;
633
634		/* default policy OID in case caller hasn't specified one */
635		utPolicyOid = &utNullPolicy;
636		if(pinfo->NumberOfPolicyIds == 0) {
637			tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies (1)");
638			/* keep going, I guess - no policy-specific info - use kTPx509Basic */
639		}
640		else {
641			CSSM_FIELD_PTR utPolicyField = &pinfo->PolicyIds[0];
642			utPolicyOid = &utPolicyField->FieldOid;
643			bool foundPolicy = checkPolicyOid(*utPolicyOid, utPolicy);
644			if(!foundPolicy) {
645				tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies");
646				/* keep going, I guess - no policy-specific info - use kTPx509Basic */
647			}
648			else {
649				/* get policy-specific info */
650				tp_policyTrustSettingParams(utPolicy, &utPolicyField->FieldValue,
651					&utPolicyStr, &utPolicyStrLen, &utKeyUse);
652			}
653		}
654	}
655
656	/* get verified (possibly partial) outCertGroup - error is fatal */
657	/* BUT: we still return partial evidence if asked to...from now on. */
658	TPCertGroup outCertGroup(*this,
659		TGO_Caller);		// certs are owned by inCertGroup
660	TPCertGroup inCertGroup(CertGroupToBeVerified, clHand, cspHand, *this,
661		cssmTimeStr, 		// optional 'this' time
662		true, 				// firstCertMustBeValid
663		TGO_Group);
664
665	/* set up for disposal of TPCertInfos created by CertGroupConstructPriv */
666	TPCertGroup	gatheredCerts(*this, TGO_Group);
667
668	try {
669		CertGroupConstructPriv(
670			clHand,
671			cspHand,
672			inCertGroup,
673			cred->DBList,
674			cssmTimeStr,
675			cred->NumberOfAnchorCerts,
676			cred->AnchorCerts,
677			actionFlags,
678			utPolicyOid,
679			utPolicyStr,
680			utPolicyStrLen,
681			utKeyUse,
682			gatheredCerts,
683			verifiedToRoot,
684			verifiedToAnchor,
685			verifiedViaTrustSetting,
686			outCertGroup);
687	}
688	catch(const CssmError &cerr) {
689		constructReturn = cerr.error;
690		/* abort if no certs found */
691		if(outCertGroup.numCerts() == 0) {
692			CssmError::throwMe(constructReturn);
693		}
694		/* else press on, collecting as much info as we can */
695	}
696	/* others are way fatal */
697	assert(outCertGroup.numCerts() >= 1);
698
699	/* Infer interim status from return values */
700	switch(constructReturn) {
701		/* these values do not get overridden */
702		case CSSMERR_TP_CERTIFICATE_CANT_OPERATE:
703		case CSSMERR_TP_INVALID_CERT_AUTHORITY:
704		case CSSMERR_APPLETP_TRUST_SETTING_DENY:
705		case errSecInvalidTrustSettings:
706			break;
707		default:
708			/* infer status from these values... */
709			if(verifiedToAnchor || verifiedViaTrustSetting) {
710				/* full success; anchor doesn't have to be root */
711				constructReturn = CSSM_OK;
712			}
713			else if(verifiedToRoot) {
714				if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) {
715					constructReturn = CSSM_OK;
716				}
717				else {
718					/* verified to root which is not an anchor */
719					constructReturn = CSSMERR_TP_INVALID_ANCHOR_CERT;
720				}
721			}
722			else {
723				/* partial chain, no root, not verifiable by anchor */
724				constructReturn = CSSMERR_TP_NOT_TRUSTED;
725			}
726
727			/*
728 			 * Those errors can be allowed, cert-chain-wide, per individual
729			 * certs' allowedErrors
730			 */
731			if((constructReturn != CSSM_OK) &&
732			    outCertGroup.isAllowedError(constructReturn)) {
733				constructReturn = CSSM_OK;
734			}
735			break;
736	}
737
738	/*
739	 * Parameters passed to tp_policyVerify() and which vary per policy
740	 * in the loop below
741	 */
742	TPPolicy tpPolicy;
743	const CSSM_APPLE_TP_SSL_OPTIONS	*sslOpts;
744	CSSM_RETURN thisPolicyRtn = CSSM_OK;	// returned from tp_policyVerify()
745
746	/* common CRL verify parameters */
747	TPCrlGroup *crlGroup = NULL;
748	try {
749		crlGroup = new TPCrlGroup(&VerifyContext->Crls,
750			clHand, cspHand,
751			*this,				// alloc
752			NULL, 				// cssmTimeStr - we want CRLs that are valid 'now'
753			TGO_Group);
754	}
755	catch(const CssmError &cerr) {
756		CSSM_RETURN cr = cerr.error;
757		/* I don't see a straightforward way to report this error,
758		 * other than adding it to the leaf cert's status... */
759		outCertGroup.certAtIndex(0)->addStatusCode(cr);
760		tpDebug("CertGroupVerify: error constructing CrlGroup; continuing\n");
761	}
762	/* others are way fatal */
763
764	TPVerifyContext revokeVfyContext(*this,
765		clHand,
766		cspHand,
767		cssmTimeStr,
768		cred->NumberOfAnchorCerts,
769		cred->AnchorCerts,
770		&inCertGroup,
771		crlGroup,
772		/*
773		 * This may consist of certs gathered from the net (which is the purpose
774		 * of this argument) and from DLDBs (a side-effect optimization).
775		 */
776		gatheredCerts,
777		cred->DBList,
778		kRevokeNone,		// policy
779		actionFlags,
780		NULL,				// CRL options
781		NULL,				// OCSP options
782		utPolicyOid,
783		utPolicyStr,
784		utPolicyStrLen,
785		utKeyUse);
786
787	/* true if we're to execute tp_policyVerify at end of loop */
788	bool doPolicyVerify;
789	/* true if we're to execute a revocation policy at end of loop */
790	bool doRevocationPolicy;
791
792	/* grind thru each policy */
793	for(uint32 polDex=0; polDex<cred->Policy.NumberOfPolicyIds; polDex++) {
794		if(cred->Policy.PolicyIds == NULL) {
795			policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
796			break;
797		}
798		CSSM_FIELD_PTR policyId = &cred->Policy.PolicyIds[polDex];
799		const CSSM_DATA *fieldVal = &policyId->FieldValue;
800		const CSSM_OID	*oid = &policyId->FieldOid;
801		thisPolicyRtn = CSSM_OK;
802		doPolicyVerify = false;
803		doRevocationPolicy = false;
804		sslOpts = NULL;
805
806		/* first the basic cert policies */
807		doPolicyVerify = checkPolicyOid(*oid, tpPolicy);
808		if(doPolicyVerify) {
809			/* some basic checks... */
810			bool policyAbort = false;
811			switch(tpPolicy) {
812				case kTPx509Basic:
813				case kTPiSign:
814				case kTP_PKINIT_Client:
815				case kTP_PKINIT_Server:
816					if(fieldVal->Data != NULL) {
817						policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
818						policyAbort = true;
819						break;
820					}
821					break;
822				default:
823					break;
824			}
825			if(policyAbort) {
826				break;
827			}
828			#if		TP_PKINIT_SERVER_HACK
829			if(tpPolicy == kTP_PKINIT_Server) {
830				/* possible override of "root not in anchors" */
831				if(constructReturn == CSSMERR_TP_INVALID_ANCHOR_CERT) {
832					if(tpCheckPkinitServerCert(outCertGroup)) {
833						constructReturn = CSSM_OK;
834					}
835				}
836			}
837			#endif	/* TP_PKINIT_SERVER_HACK */
838		}
839
840		/*
841		 * Now revocation policies. Note some fields in revokeVfyContext can
842		 * accumulate across multiple policy calls, e.g., signerCerts.
843		 */
844		else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_CRL)) {
845			/* CRL-specific options */
846			const CSSM_APPLE_TP_CRL_OPTIONS *crlOpts;
847			crlOpts = (CSSM_APPLE_TP_CRL_OPTIONS *)fieldVal->Data;
848			thisPolicyRtn = CSSM_OK;
849			if(crlOpts != NULL) {
850				switch(crlOpts->Version) {
851					case CSSM_APPLE_TP_CRL_OPTS_VERSION:
852						if(fieldVal->Length !=
853								sizeof(CSSM_APPLE_TP_CRL_OPTIONS)) {
854							thisPolicyRtn =
855								CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
856							break;
857						}
858						break;
859					/* handle backwards compatibility here if necessary */
860					default:
861						thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
862						break;
863				}
864				if(thisPolicyRtn != CSSM_OK) {
865					policyReturn = thisPolicyRtn;
866					break;
867				}
868			}
869			revokeVfyContext.policy = kRevokeCrlBasic;
870			revokeVfyContext.crlOpts = crlOpts;
871			doRevocationPolicy = true;
872		}
873		else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
874			/* OCSP-specific options */
875			const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts;
876			ocspOpts = (CSSM_APPLE_TP_OCSP_OPTIONS *)fieldVal->Data;
877			thisPolicyRtn = CSSM_OK;
878			if(ocspOpts != NULL) {
879				switch(ocspOpts->Version) {
880					case CSSM_APPLE_TP_OCSP_OPTS_VERSION:
881						if(fieldVal->Length !=
882								sizeof(CSSM_APPLE_TP_OCSP_OPTIONS)) {
883							thisPolicyRtn =
884								CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
885							break;
886						}
887						break;
888					/* handle backwards compatibility here if necessary */
889					default:
890						thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
891						break;
892				}
893				if(thisPolicyRtn != CSSM_OK) {
894					policyReturn = thisPolicyRtn;
895					break;
896				}
897			}
898			revokeVfyContext.policy = kRevokeOcsp;
899			revokeVfyContext.ocspOpts = ocspOpts;
900			doRevocationPolicy = true;
901		}
902		/* etc. - add more policies here */
903		else {
904			/* unknown TP policy OID */
905			policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
906			break;
907		}
908
909		/* common cert policy call */
910		if(doPolicyVerify) {
911			assert(!doRevocationPolicy);	// one at a time
912			thisPolicyRtn = tp_policyVerify(tpPolicy,
913				*this,
914				clHand,
915				cspHand,
916				&outCertGroup,
917				verifiedToRoot,
918				verifiedViaTrustSetting,
919				actionFlags,
920				fieldVal,
921				cred->Policy.PolicyControl);	// not currently used
922			didCertPolicy = true;
923		}
924		/* common revocation policy call */
925		if(doRevocationPolicy) {
926			assert(!doPolicyVerify);	// one at a time
927			thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext, outCertGroup);
928			didRevokePolicy = true;
929		}
930		/* See if possible error is allowed, cert-chain-wide. */
931		if((thisPolicyRtn != CSSM_OK) &&
932		    outCertGroup.isAllowedError(thisPolicyRtn)) {
933			thisPolicyRtn = CSSM_OK;
934		}
935		if(thisPolicyRtn) {
936			/* Now remember the error if it's the first policy
937			 * error we've seen. */
938			if(policyReturn == CSSM_OK) {
939				policyReturn = thisPolicyRtn;
940			}
941			/* Keep going? */
942			if(tpStopOn == CSSM_TP_STOP_ON_FIRST_FAIL) {
943				/* Nope; we're done with policy evaluation */
944				break;
945			}
946		}
947	}	/* for each policy */
948
949	/*
950	 * Upon completion of the above loop, perform default policy ops if
951	 * appropriate.
952	 */
953	if((policyReturn == CSSM_OK) || (tpStopOn == CSSM_TP_STOP_ON_NONE)) {
954		if(!didCertPolicy) {
955			policyReturn = tp_policyVerify(kTPDefault,
956				*this,
957				clHand,
958				cspHand,
959				&outCertGroup,
960				verifiedToRoot,
961				verifiedViaTrustSetting,
962				actionFlags,
963				NULL,							// policyFieldData
964				cred->Policy.PolicyControl);	// not currently used
965			/* See if error is allowed, cert-chain-wide. */
966			if((policyReturn != CSSM_OK) &&
967				outCertGroup.isAllowedError(policyReturn)) {
968				policyReturn = CSSM_OK;
969			}
970		}
971		if( !didRevokePolicy &&							// no revoke policy yet
972			( (policyReturn == CSSM_OK || 				// default cert policy OK
973		      (tpStopOn == CSSM_TP_STOP_ON_NONE)) 		// keep going anyway
974			)
975		  ) {
976			revokeVfyContext.policy = TP_CRL_POLICY_DEFAULT;
977			CSSM_RETURN thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext,
978				outCertGroup);
979			if((thisPolicyRtn != CSSM_OK) &&
980				outCertGroup.isAllowedError(thisPolicyRtn)) {
981				thisPolicyRtn = CSSM_OK;
982			}
983			if((thisPolicyRtn != CSSM_OK) && (policyReturn == CSSM_OK)) {
984				policyReturn = thisPolicyRtn;
985			}
986
987		}
988	}	/* default policy opts */
989
990	delete crlGroup;
991
992	/* return evidence - i.e., constructed chain - if asked to */
993	if(VerifyContextResult != NULL) {
994		/*
995		 * VerifyContextResult->Evidence[0] : CSSM_TP_APPLE_EVIDENCE_HEADER
996		 * VerifyContextResult->Evidence[1] : CSSM_CERTGROUP
997		 * VerifyContextResult->Evidence[2] : CSSM_TP_APPLE_EVIDENCE_INFO
998		 */
999		VerifyContextResult->NumberOfEvidences = 3;
1000		VerifyContextResult->Evidence =
1001			(CSSM_EVIDENCE_PTR)calloc(3, sizeof(CSSM_EVIDENCE));
1002
1003		CSSM_TP_APPLE_EVIDENCE_HEADER *hdr =
1004			(CSSM_TP_APPLE_EVIDENCE_HEADER *)malloc(
1005				sizeof(CSSM_TP_APPLE_EVIDENCE_HEADER));
1006		hdr->Version = CSSM_TP_APPLE_EVIDENCE_VERSION;
1007		CSSM_EVIDENCE_PTR ev = &VerifyContextResult->Evidence[0];
1008		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_HEADER;
1009		ev->Evidence = hdr;
1010
1011		ev = &VerifyContextResult->Evidence[1];
1012		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERTGROUP;
1013		ev->Evidence = outCertGroup.buildCssmCertGroup();
1014
1015		ev = &VerifyContextResult->Evidence[2];
1016		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERT_INFO;
1017		ev->Evidence = outCertGroup.buildCssmEvidenceInfo();
1018	}
1019	else {
1020		/* caller responsible for freeing these if they are for evidence.... */
1021		outCertGroup.freeDbRecords();
1022	}
1023	CSSM_RETURN outErr = outCertGroup.getReturnCode(constructReturn, policyReturn,
1024		actionFlags);
1025
1026	if(outErr) {
1027		CssmError::throwMe(outErr);
1028	}
1029}
1030
1031
1032