1/*
2 * Copyright (c) 2000-2001, 2011 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	return false;
473}
474
475/*-----------------------------------------------------------------------------
476 * CertGroupVerify
477 *
478 * Description:
479 *   -- Construct a cert chain using TP_CertGroupConstruct.
480 *   -- Attempt to verify that cert chain against one of the known
481 *      good certs passed in AnchorCerts.
482 *   -- Optionally enforces additional policies (TBD) when verifying the cert chain.
483 *   -- Optionally returns the entire cert chain constructed in
484 *      TP_CertGroupConstruct and here, all the way to an anchor cert or as
485 *      far as we were able to go, in *Evidence.
486 *
487 * Parameters:
488 *   Two handles - to an open CL and CSP. The CSP must be capable of
489 *   dealing with the signature algorithms used by the certs. The CL must be
490 *   an X.509-savvy CL.
491 *
492 *   RawCerts, an unordered array of raw certs in the form of a
493 *   CSSM_CERTGROUP_PTR. The first cert of this list is the subject cert
494 *   which is eventually to be verified. The other certs can be in any order
495 *   and may not even have any relevance to the cert chain being constructed.
496 *   They may also be invalid certs.
497 *
498 *   DBList, a list of DB/DL handles which may contain certs necessary to
499 *   complete the desired cert chain. (Currently not implemented.)
500 *
501 *   AnchorCerts, a list of known trusted certs.
502 *   NumberOfAnchorCerts, size of AnchorCerts array.
503 *
504 *   PolicyIdentifiers, Optional policy OID. NULL indicates default
505 *		X.509 trust policy.
506 *
507 *	 Supported Policies:
508 *			CSSMOID_APPLE_ISIGN
509 *			CSSMOID_APPLE_X509_BASIC
510 *
511 *			For both of these, the associated FieldValue must be {0, NULL},
512 *
513 *   NumberOfPolicyIdentifiers, size of PolicyIdentifiers array, must be
514 *      zero or one.
515 *
516 *   All other arguments must be zero/NULL.
517 *
518 *   Returns:
519 *      CSSM_OK : cert chain verified all the way back to an AnchorCert.
520 *      CSSMERR_TP_INVALID_ANCHOR_CERT : In this case, the cert chain
521 *   		was validated back to a self-signed (root) cert found in either
522 *   		CertToBeVerified or in one of the DBs in DBList, but that root cert
523 *   		was *NOT* found in the AnchorCert list.
524 *		CSSMERR_TP_NOT_TRUSTED: no root cert was found and no AnchorCert
525 *   		verified the end of the constructed cert chain.
526 *		CSSMERR_TP_VERIFICATION_FAILURE: a root cert was found which does
527 *   		not self-verify.
528 *   	CSSMERR_TP_VERIFY_ACTION_FAILED: indicates a failure of the requested
529 *			policy action.
530 *   	CSSMERR_TP_INVALID_CERTIFICATE: indicates a bad leaf cert.
531 *		CSSMERR_TP_INVALID_REQUEST_INPUTS : no incoming VerifyContext.
532 *		CSSMERR_TP_CERT_EXPIRED and CSSMERR_TP_CERT_NOT_VALID_YET: see comments
533 *			for CertGroupConstruct.
534 *		CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial
535 *			public key, rendering full verification impossible.
536 *   	CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial
537 *			public key and which failed to perform subsequent signature
538 *			verification.
539 *---------------------------------------------------------------------------*/
540
541void AppleTPSession::CertGroupVerify(CSSM_CL_HANDLE clHand,
542		CSSM_CSP_HANDLE cspHand,
543		const CSSM_CERTGROUP &CertGroupToBeVerified,
544		const CSSM_TP_VERIFY_CONTEXT *VerifyContext,
545		CSSM_TP_VERIFY_CONTEXT_RESULT_PTR VerifyContextResult)
546{
547	CSSM_BOOL				verifiedToRoot = CSSM_FALSE;
548	CSSM_BOOL				verifiedToAnchor = CSSM_FALSE;
549	CSSM_BOOL				verifiedViaTrustSetting = CSSM_FALSE;
550	CSSM_RETURN				constructReturn = CSSM_OK;
551	CSSM_RETURN				policyReturn = CSSM_OK;
552	const CSSM_TP_CALLERAUTH_CONTEXT *cred;
553	/* declare volatile as compiler workaround to avoid caching in CR4 */
554	const CSSM_APPLE_TP_ACTION_DATA * volatile actionData = NULL;
555	CSSM_TIMESTRING			cssmTimeStr;
556	CSSM_APPLE_TP_ACTION_FLAGS	actionFlags = 0;
557	CSSM_TP_STOP_ON 		tpStopOn = 0;
558
559	/* keep track of whether we did policy checking; if not, we do defaults */
560	bool					didCertPolicy = false;
561	bool					didRevokePolicy = false;
562
563	/* user trust parameters */
564	CSSM_OID				utNullPolicy = {0, NULL};
565	const CSSM_OID			*utPolicyOid = NULL;
566	const char				*utPolicyStr = NULL;
567	uint32					utPolicyStrLen = 0;
568	SecTrustSettingsKeyUsage	utKeyUse = 0;
569	bool					utTrustSettingEnabled = false;
570
571	if(VerifyContextResult) {
572		memset(VerifyContextResult, 0, sizeof(*VerifyContextResult));
573	}
574
575	/* verify input args, skipping the ones checked by CertGroupConstruct */
576	if((VerifyContext == NULL) || (VerifyContext->Cred == NULL)) {
577		/* the spec says that this is optional but we require it */
578			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
579	}
580	cred = VerifyContext->Cred;
581
582	/* Optional ActionData affecting all policies */
583	actionData = (CSSM_APPLE_TP_ACTION_DATA * volatile)VerifyContext->ActionData.Data;
584	if(actionData != NULL) {
585		switch(actionData->Version) {
586			case CSSM_APPLE_TP_ACTION_VERSION:
587				if(VerifyContext->ActionData.Length !=
588						sizeof(CSSM_APPLE_TP_ACTION_DATA)) {
589					CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA);
590				}
591				break;
592			/* handle backwards versions here if we ever go beyond version 0 */
593			default:
594				CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA);
595		}
596		actionFlags = actionData->ActionFlags;
597		if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) {
598			utTrustSettingEnabled = true;
599		}
600	}
601
602	/* optional, may be NULL */
603	cssmTimeStr = cred->VerifyTime;
604
605	tpStopOn = cred->VerificationAbortOn;
606	switch(tpStopOn) {
607		/* the only two we support */
608		case CSSM_TP_STOP_ON_NONE:
609		case CSSM_TP_STOP_ON_FIRST_FAIL:
610			break;
611		/* default maps to stop on first fail */
612		case CSSM_TP_STOP_ON_POLICY:
613			tpStopOn = CSSM_TP_STOP_ON_FIRST_FAIL;
614			break;
615		default:
616			CssmError::throwMe(CSSMERR_TP_INVALID_STOP_ON_POLICY);
617	}
618
619	/* now the args we can't deal with */
620	if(cred->CallerCredentials != NULL) {
621			CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
622	}
623	/* ...any others? */
624
625	/* set up for optional user trust evaluation */
626	if(utTrustSettingEnabled) {
627		const CSSM_TP_POLICYINFO *pinfo = &cred->Policy;
628		TPPolicy utPolicy = kTPx509Basic;
629
630		/* default policy OID in case caller hasn't specified one */
631		utPolicyOid = &utNullPolicy;
632		if(pinfo->NumberOfPolicyIds == 0) {
633			tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies (1)");
634			/* keep going, I guess - no policy-specific info - use kTPx509Basic */
635		}
636		else {
637			CSSM_FIELD_PTR utPolicyField = &pinfo->PolicyIds[0];
638			utPolicyOid = &utPolicyField->FieldOid;
639			bool foundPolicy = checkPolicyOid(*utPolicyOid, utPolicy);
640			if(!foundPolicy) {
641				tpTrustSettingsDbg("CertGroupVerify: User trust enabled but no policies");
642				/* keep going, I guess - no policy-specific info - use kTPx509Basic */
643			}
644			else {
645				/* get policy-specific info */
646				tp_policyTrustSettingParams(utPolicy, &utPolicyField->FieldValue,
647					&utPolicyStr, &utPolicyStrLen, &utKeyUse);
648			}
649		}
650	}
651
652	/* get verified (possibly partial) outCertGroup - error is fatal */
653	/* BUT: we still return partial evidence if asked to...from now on. */
654	TPCertGroup outCertGroup(*this,
655		TGO_Caller);		// certs are owned by inCertGroup
656	TPCertGroup inCertGroup(CertGroupToBeVerified, clHand, cspHand, *this,
657		cssmTimeStr, 		// optional 'this' time
658		true, 				// firstCertMustBeValid
659		TGO_Group);
660
661	/* set up for disposal of TPCertInfos created by CertGroupConstructPriv */
662	TPCertGroup	gatheredCerts(*this, TGO_Group);
663
664	try {
665		CertGroupConstructPriv(
666			clHand,
667			cspHand,
668			inCertGroup,
669			cred->DBList,
670			cssmTimeStr,
671			cred->NumberOfAnchorCerts,
672			cred->AnchorCerts,
673			actionFlags,
674			utPolicyOid,
675			utPolicyStr,
676			utPolicyStrLen,
677			utKeyUse,
678			gatheredCerts,
679			verifiedToRoot,
680			verifiedToAnchor,
681			verifiedViaTrustSetting,
682			outCertGroup);
683	}
684	catch(const CssmError &cerr) {
685		constructReturn = cerr.error;
686		/* abort if no certs found */
687		if(outCertGroup.numCerts() == 0) {
688			CssmError::throwMe(constructReturn);
689		}
690		/* else press on, collecting as much info as we can */
691	}
692	/* others are way fatal */
693	assert(outCertGroup.numCerts() >= 1);
694
695	/* Infer interim status from return values */
696	switch(constructReturn) {
697		/* these values do not get overridden */
698		case CSSMERR_TP_CERTIFICATE_CANT_OPERATE:
699		case CSSMERR_TP_INVALID_CERT_AUTHORITY:
700		case CSSMERR_APPLETP_TRUST_SETTING_DENY:
701		case errSecInvalidTrustSettings:
702			break;
703		default:
704			/* infer status from these values... */
705			if(verifiedToAnchor || verifiedViaTrustSetting) {
706				/* full success; anchor doesn't have to be root */
707				constructReturn = CSSM_OK;
708			}
709			else if(verifiedToRoot) {
710				if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) {
711					constructReturn = CSSM_OK;
712				}
713				else {
714					/* verified to root which is not an anchor */
715					constructReturn = CSSMERR_TP_INVALID_ANCHOR_CERT;
716				}
717			}
718			else {
719				/* partial chain, no root, not verifiable by anchor */
720				constructReturn = CSSMERR_TP_NOT_TRUSTED;
721			}
722
723			/*
724 			 * Those errors can be allowed, cert-chain-wide, per individual
725			 * certs' allowedErrors
726			 */
727			if((constructReturn != CSSM_OK) &&
728			    outCertGroup.isAllowedError(constructReturn)) {
729				constructReturn = CSSM_OK;
730			}
731			break;
732	}
733
734	/*
735	 * Parameters passed to tp_policyVerify() and which vary per policy
736	 * in the loop below
737	 */
738	TPPolicy tpPolicy;
739	const CSSM_APPLE_TP_SSL_OPTIONS	*sslOpts;
740	CSSM_RETURN thisPolicyRtn = CSSM_OK;	// returned from tp_policyVerify()
741
742	/* common CRL verify parameters */
743	TPCrlGroup *crlGroup = NULL;
744	try {
745		crlGroup = new TPCrlGroup(&VerifyContext->Crls,
746			clHand, cspHand,
747			*this,				// alloc
748			NULL, 				// cssmTimeStr - we want CRLs that are valid 'now'
749			TGO_Group);
750	}
751	catch(const CssmError &cerr) {
752		CSSM_RETURN cr = cerr.error;
753		/* I don't see a straightforward way to report this error,
754		 * other than adding it to the leaf cert's status... */
755		outCertGroup.certAtIndex(0)->addStatusCode(cr);
756		tpDebug("CertGroupVerify: error constructing CrlGroup; continuing\n");
757	}
758	/* others are way fatal */
759
760	TPVerifyContext revokeVfyContext(*this,
761		clHand,
762		cspHand,
763		cssmTimeStr,
764		cred->NumberOfAnchorCerts,
765		cred->AnchorCerts,
766		&inCertGroup,
767		crlGroup,
768		/*
769		 * This may consist of certs gathered from the net (which is the purpose
770		 * of this argument) and from DLDBs (a side-effect optimization).
771		 */
772		gatheredCerts,
773		cred->DBList,
774		kRevokeNone,		// policy
775		actionFlags,
776		NULL,				// CRL options
777		NULL,				// OCSP options
778		utPolicyOid,
779		utPolicyStr,
780		utPolicyStrLen,
781		utKeyUse);
782
783	/* true if we're to execute tp_policyVerify at end of loop */
784	bool doPolicyVerify;
785	/* true if we're to execute a revocation policy at end of loop */
786	bool doRevocationPolicy;
787
788	/* grind thru each policy */
789	for(uint32 polDex=0; polDex<cred->Policy.NumberOfPolicyIds; polDex++) {
790		if(cred->Policy.PolicyIds == NULL) {
791			policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
792			break;
793		}
794		CSSM_FIELD_PTR policyId = &cred->Policy.PolicyIds[polDex];
795		const CSSM_DATA *fieldVal = &policyId->FieldValue;
796		const CSSM_OID	*oid = &policyId->FieldOid;
797		thisPolicyRtn = CSSM_OK;
798		doPolicyVerify = false;
799		doRevocationPolicy = false;
800		sslOpts = NULL;
801
802		/* first the basic cert policies */
803		doPolicyVerify = checkPolicyOid(*oid, tpPolicy);
804		if(doPolicyVerify) {
805			/* some basic checks... */
806			bool policyAbort = false;
807			switch(tpPolicy) {
808				case kTPx509Basic:
809				case kTPiSign:
810				case kTP_PKINIT_Client:
811				case kTP_PKINIT_Server:
812					if(fieldVal->Data != NULL) {
813						policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
814						policyAbort = true;
815						break;
816					}
817					break;
818				default:
819					break;
820			}
821			if(policyAbort) {
822				break;
823			}
824			#if		TP_PKINIT_SERVER_HACK
825			if(tpPolicy == kTP_PKINIT_Server) {
826				/* possible override of "root not in anchors" */
827				if(constructReturn == CSSMERR_TP_INVALID_ANCHOR_CERT) {
828					if(tpCheckPkinitServerCert(outCertGroup)) {
829						constructReturn = CSSM_OK;
830					}
831				}
832			}
833			#endif	/* TP_PKINIT_SERVER_HACK */
834		}
835
836		/*
837		 * Now revocation policies. Note some fields in revokeVfyContext can
838		 * accumulate across multiple policy calls, e.g., signerCerts.
839		 */
840		else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_CRL)) {
841			/* CRL-specific options */
842			const CSSM_APPLE_TP_CRL_OPTIONS *crlOpts;
843			crlOpts = (CSSM_APPLE_TP_CRL_OPTIONS *)fieldVal->Data;
844			thisPolicyRtn = CSSM_OK;
845			if(crlOpts != NULL) {
846				switch(crlOpts->Version) {
847					case CSSM_APPLE_TP_CRL_OPTS_VERSION:
848						if(fieldVal->Length !=
849								sizeof(CSSM_APPLE_TP_CRL_OPTIONS)) {
850							thisPolicyRtn =
851								CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
852							break;
853						}
854						break;
855					/* handle backwards compatibility here if necessary */
856					default:
857						thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
858						break;
859				}
860				if(thisPolicyRtn != CSSM_OK) {
861					policyReturn = thisPolicyRtn;
862					break;
863				}
864			}
865			revokeVfyContext.policy = kRevokeCrlBasic;
866			revokeVfyContext.crlOpts = crlOpts;
867			doRevocationPolicy = true;
868		}
869		else if(tpCompareOids(oid, &CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
870			/* OCSP-specific options */
871			const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts;
872			ocspOpts = (CSSM_APPLE_TP_OCSP_OPTIONS *)fieldVal->Data;
873			thisPolicyRtn = CSSM_OK;
874			if(ocspOpts != NULL) {
875				switch(ocspOpts->Version) {
876					case CSSM_APPLE_TP_OCSP_OPTS_VERSION:
877						if(fieldVal->Length !=
878								sizeof(CSSM_APPLE_TP_OCSP_OPTIONS)) {
879							thisPolicyRtn =
880								CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
881							break;
882						}
883						break;
884					/* handle backwards compatibility here if necessary */
885					default:
886						thisPolicyRtn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
887						break;
888				}
889				if(thisPolicyRtn != CSSM_OK) {
890					policyReturn = thisPolicyRtn;
891					break;
892				}
893			}
894			revokeVfyContext.policy = kRevokeOcsp;
895			revokeVfyContext.ocspOpts = ocspOpts;
896			doRevocationPolicy = true;
897		}
898		/* etc. - add more policies here */
899		else {
900			/* unknown TP policy OID */
901			policyReturn = CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
902			break;
903		}
904
905		/* common cert policy call */
906		if(doPolicyVerify) {
907			assert(!doRevocationPolicy);	// one at a time
908			thisPolicyRtn = tp_policyVerify(tpPolicy,
909				*this,
910				clHand,
911				cspHand,
912				&outCertGroup,
913				verifiedToRoot,
914				verifiedViaTrustSetting,
915				actionFlags,
916				fieldVal,
917				cred->Policy.PolicyControl);	// not currently used
918			didCertPolicy = true;
919		}
920		/* common revocation policy call */
921		if(doRevocationPolicy) {
922			assert(!doPolicyVerify);	// one at a time
923			thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext, outCertGroup);
924			didRevokePolicy = true;
925		}
926		/* See if possible error is allowed, cert-chain-wide. */
927		if((thisPolicyRtn != CSSM_OK) &&
928		    outCertGroup.isAllowedError(thisPolicyRtn)) {
929			thisPolicyRtn = CSSM_OK;
930		}
931		if(thisPolicyRtn) {
932			/* Now remember the error if it's the first policy
933			 * error we've seen. */
934			if(policyReturn == CSSM_OK) {
935				policyReturn = thisPolicyRtn;
936			}
937			/* Keep going? */
938			if(tpStopOn == CSSM_TP_STOP_ON_FIRST_FAIL) {
939				/* Nope; we're done with policy evaluation */
940				break;
941			}
942		}
943	}	/* for each policy */
944
945	/*
946	 * Upon completion of the above loop, perform default policy ops if
947	 * appropriate.
948	 */
949	if((policyReturn == CSSM_OK) || (tpStopOn == CSSM_TP_STOP_ON_NONE)) {
950		if(!didCertPolicy) {
951			policyReturn = tp_policyVerify(kTPDefault,
952				*this,
953				clHand,
954				cspHand,
955				&outCertGroup,
956				verifiedToRoot,
957				verifiedViaTrustSetting,
958				actionFlags,
959				NULL,							// policyFieldData
960				cred->Policy.PolicyControl);	// not currently used
961			/* See if error is allowed, cert-chain-wide. */
962			if((policyReturn != CSSM_OK) &&
963				outCertGroup.isAllowedError(policyReturn)) {
964				policyReturn = CSSM_OK;
965			}
966		}
967		if( !didRevokePolicy &&							// no revoke policy yet
968			( (policyReturn == CSSM_OK || 				// default cert policy OK
969		      (tpStopOn == CSSM_TP_STOP_ON_NONE)) 		// keep going anyway
970			)
971		  ) {
972			revokeVfyContext.policy = TP_CRL_POLICY_DEFAULT;
973			CSSM_RETURN thisPolicyRtn = tpRevocationPolicyVerify(revokeVfyContext,
974				outCertGroup);
975			if((thisPolicyRtn != CSSM_OK) &&
976				outCertGroup.isAllowedError(thisPolicyRtn)) {
977				thisPolicyRtn = CSSM_OK;
978			}
979			if((thisPolicyRtn != CSSM_OK) && (policyReturn == CSSM_OK)) {
980				policyReturn = thisPolicyRtn;
981			}
982
983		}
984	}	/* default policy opts */
985
986	delete crlGroup;
987
988	/* return evidence - i.e., constructed chain - if asked to */
989	if(VerifyContextResult != NULL) {
990		/*
991		 * VerifyContextResult->Evidence[0] : CSSM_TP_APPLE_EVIDENCE_HEADER
992		 * VerifyContextResult->Evidence[1] : CSSM_CERTGROUP
993		 * VerifyContextResult->Evidence[2] : CSSM_TP_APPLE_EVIDENCE_INFO
994		 */
995		VerifyContextResult->NumberOfEvidences = 3;
996		VerifyContextResult->Evidence =
997			(CSSM_EVIDENCE_PTR)calloc(3, sizeof(CSSM_EVIDENCE));
998
999		CSSM_TP_APPLE_EVIDENCE_HEADER *hdr =
1000			(CSSM_TP_APPLE_EVIDENCE_HEADER *)malloc(
1001				sizeof(CSSM_TP_APPLE_EVIDENCE_HEADER));
1002		hdr->Version = CSSM_TP_APPLE_EVIDENCE_VERSION;
1003		CSSM_EVIDENCE_PTR ev = &VerifyContextResult->Evidence[0];
1004		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_HEADER;
1005		ev->Evidence = hdr;
1006
1007		ev = &VerifyContextResult->Evidence[1];
1008		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERTGROUP;
1009		ev->Evidence = outCertGroup.buildCssmCertGroup();
1010
1011		ev = &VerifyContextResult->Evidence[2];
1012		ev->EvidenceForm = CSSM_EVIDENCE_FORM_APPLE_CERT_INFO;
1013		ev->Evidence = outCertGroup.buildCssmEvidenceInfo();
1014	}
1015	else {
1016		/* caller responsible for freeing these if they are for evidence.... */
1017		outCertGroup.freeDbRecords();
1018	}
1019	CSSM_RETURN outErr = outCertGroup.getReturnCode(constructReturn, policyReturn,
1020		actionFlags);
1021
1022	if(outErr) {
1023		CssmError::throwMe(outErr);
1024	}
1025}
1026
1027
1028