1/*
2 * Copyright (c) 2002-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 * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
21 */
22
23#include "TPNetwork.h"
24#include "tpdebugging.h"
25#include "tpTime.h"
26#include "cuEnc64.h"
27#include <Security/cssmtype.h>
28#include <Security/cssmapple.h>
29#include <Security/oidscert.h>
30#include <security_utilities/logging.h>
31#include <security_ocspd/ocspdClient.h>
32
33#define CA_ISSUERS_OID              OID_PKIX, 0x30, 0x02
34#define CA_ISSUERS_OID_LEN          OID_PKIX_LENGTH + 2
35
36static const uint8 	OID_CA_ISSUERS[]      = {CA_ISSUERS_OID};
37const CSSM_OID	CSSMOID_CA_ISSUERS        = {CA_ISSUERS_OID_LEN, (uint8 *)OID_CA_ISSUERS};
38
39typedef enum {
40	LT_Crl = 1,
41	LT_Cert
42} LF_Type;
43
44static CSSM_RETURN tpDecodeCert(
45	Allocator		&alloc,
46	CSSM_DATA		&rtnBlob)		// will be reallocated if needed
47{
48	const unsigned char *inbuf = (const unsigned char *)rtnBlob.Data;
49	unsigned inlen = (unsigned)rtnBlob.Length;
50	unsigned char *outbuf = NULL;
51	unsigned outlen = 0;
52	CSSM_RETURN ortn = cuConvertPem(inbuf, inlen, &outbuf, &outlen);
53
54	if(ortn == 0 && outbuf != NULL) {
55		/* Decoded result needs to be malloc'd via input allocator */
56		unsigned char *rtnP = (unsigned char *) alloc.malloc(outlen);
57		if(rtnP != NULL) {
58			memcpy(rtnP, outbuf, outlen);
59			rtnBlob.Data = rtnP;
60			rtnBlob.Length = outlen;
61		}
62		free(outbuf);
63		alloc.free((void *)inbuf);
64	}
65	return ortn;
66}
67
68static CSSM_RETURN tpFetchViaNet(
69	const CSSM_DATA &url,
70	const CSSM_DATA *issuer,		// optional
71	LF_Type 		lfType,
72	CSSM_TIMESTRING verifyTime,		// CRL only
73	Allocator		&alloc,
74	CSSM_DATA		&rtnBlob)		// mallocd and RETURNED
75{
76	if(lfType == LT_Crl) {
77		return ocspdCRLFetch(alloc, url, issuer,
78			true, true,				// cache r/w both enable
79			verifyTime, rtnBlob);
80	}
81	else {
82		CSSM_RETURN result = ocspdCertFetch(alloc, url, rtnBlob);
83		if(result == CSSM_OK) {
84			/* The data might be in PEM format; if so, convert it here */
85			(void)tpDecodeCert(alloc, rtnBlob);
86		}
87		return result;
88	}
89}
90
91static CSSM_RETURN tpCrlViaNet(
92	const CSSM_DATA &url,
93	const CSSM_DATA *issuer,	// optional, only if cert and CRL have same issuer
94	TPVerifyContext &vfyCtx,
95	TPCertInfo &forCert,		// for verifyWithContext
96	TPCrlInfo *&rtnCrl)
97{
98	TPCrlInfo *crl = NULL;
99	CSSM_DATA crlData;
100	CSSM_RETURN crtn;
101	Allocator &alloc = Allocator::standard();
102	char cssmTime[CSSM_TIME_STRLEN+1];
103
104	rtnCrl = NULL;
105
106	/* verifyTime: we want a CRL that's valid right now. */
107	{
108		StLock<Mutex> _(tpTimeLock());
109		timeAtNowPlus(0, TIME_CSSM, cssmTime);
110	}
111
112	crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
113	if(crtn) {
114		return crtn;
115	}
116	try {
117		crl = new TPCrlInfo(vfyCtx.clHand,
118			vfyCtx.cspHand,
119			&crlData,
120			TIC_CopyData,
121			NULL); 			// verifyTime = Now
122	}
123	catch(...) {
124		alloc.free(crlData.Data);
125
126		/*
127		 * There is a slight possibility of recovering from this error. In case
128		 * the CRL came from disk cache, flush the cache and try to get the CRL
129		 * from the net.
130		 */
131		tpDebug("   bad CRL; flushing from cache and retrying");
132		ocspdCRLFlush(url);
133		crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
134		if(crtn == CSSM_OK) {
135			try {
136				crl = new TPCrlInfo(vfyCtx.clHand,
137					vfyCtx.cspHand,
138					&crlData,
139					TIC_CopyData,
140					NULL);
141				tpDebug("   RECOVERY: good CRL obtained from net");
142			}
143			catch(...) {
144				alloc.free(crlData.Data);
145				tpDebug("   bad CRL; recovery FAILED (1)");
146				return CSSMERR_APPLETP_CRL_NOT_FOUND;
147			}
148		}
149		else {
150			/* it was in cache but we can't find it on the net */
151			tpDebug("   bad CRL; recovery FAILED (2)");
152			return CSSMERR_APPLETP_CRL_NOT_FOUND;
153		}
154	}
155	alloc.free(crlData.Data);
156
157	/*
158 	 * Full CRL verify.
159 	 * The verify time in the TPVerifyContext is the time at which various
160	 * entities (CRL and its own cert chain) are to be verified; that's
161	 * NULL for "right now". The current vfyCtx.verifyTime is the time at
162	 * which the cert's revocation status to be determined; this call to
163	 * verifyWithContextNow() doesn't do that.
164	 */
165	crtn = crl->verifyWithContextNow(vfyCtx, &forCert);
166	if(crtn == CSSM_OK) {
167		crl->uri(url);
168	}
169	else {
170		delete crl;
171		crl = NULL;
172	}
173	rtnCrl = crl;
174	return crtn;
175}
176
177static CSSM_RETURN tpIssuerCertViaNet(
178	const CSSM_DATA &url,
179	CSSM_CL_HANDLE	clHand,
180	CSSM_CSP_HANDLE	cspHand,
181	const char		*verifyTime,
182	TPCertInfo 		&subject,
183	TPCertInfo 		*&rtnCert)
184{
185	TPCertInfo *issuer = NULL;
186	CSSM_DATA certData;
187	CSSM_RETURN crtn;
188	Allocator &alloc = Allocator::standard();
189
190	crtn = tpFetchViaNet(url, NULL, LT_Cert, NULL, alloc, certData);
191	if(crtn) {
192		tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
193		return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
194	}
195	try {
196		issuer = new TPCertInfo(clHand,
197			cspHand,
198			&certData,
199			TIC_CopyData,
200			verifyTime);
201	}
202	catch(...) {
203		tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
204		alloc.free(certData.Data);
205		rtnCert = NULL;
206		return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
207	}
208	alloc.free(certData.Data);
209
210	/* subject/issuer match? */
211	if(!issuer->isIssuerOf(subject)) {
212		tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
213		crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
214	}
215	else {
216		/* yep, do a sig verify */
217		crtn = subject.verifyWithIssuer(issuer);
218		if(crtn) {
219			tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
220				"fetch\n");
221			crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
222		}
223	}
224	if(crtn) {
225		assert(issuer != NULL);
226		delete issuer;
227		issuer = NULL;
228	}
229	rtnCert = issuer;
230	return crtn;
231}
232
233/*
234 * Fetch a CRL or a cert via a GeneralNames.
235 * Shared by cert and CRL code to avoid duplicating GeneralNames traversal
236 * code, despite the awkward interface for this function.
237 */
238static CSSM_RETURN tpFetchViaGeneralNames(
239	const CE_GeneralNames	*names,
240	TPCertInfo 				&forCert,
241	const CSSM_DATA			*issuer,			// optional, and only for CRLs
242	TPVerifyContext			*verifyContext,		// only for CRLs
243	CSSM_CL_HANDLE			clHand,				// only for certs
244	CSSM_CSP_HANDLE			cspHand,			// only for certs
245	const char				*verifyTime,		// optional
246	/* exactly one must be non-NULL, that one is returned */
247	TPCertInfo				**certInfo,
248	TPCrlInfo				**crlInfo)
249{
250	assert(certInfo || crlInfo);
251	assert(!certInfo || !crlInfo);
252	CSSM_RETURN crtn;
253
254	for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) {
255		CE_GeneralName *name = &names->generalName[nameDex];
256		switch(name->nameType) {
257			case GNT_URI:
258				if(name->name.Length < 5) {
259					continue;
260				}
261				if(strncmp((char *)name->name.Data, "ldap:", 5) &&
262				   strncmp((char *)name->name.Data, "http:", 5) &&
263				   strncmp((char *)name->name.Data, "https:", 6)) {
264					/* eventually handle other schemes here */
265					continue;
266				}
267				if(certInfo) {
268					tpDebug("   fetching cert via net");
269					crtn = tpIssuerCertViaNet(name->name,
270						clHand,
271						cspHand,
272						verifyTime,
273						forCert,
274						*certInfo);
275				}
276				else {
277					tpDebug("   fetching CRL via net");
278					assert(verifyContext != NULL);
279					crtn = tpCrlViaNet(name->name,
280						issuer,
281						*verifyContext,
282						forCert,
283						*crlInfo);
284				}
285				switch(crtn) {
286					case CSSM_OK:
287					case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:	// caller handles
288						return crtn;
289					default:
290						break;
291				}
292				/* not found/no good; try again */
293				break;
294			default:
295				tpCrlDebug("  tpFetchCrlFromNet: unknown"
296					"nameType (%u)", (unsigned)name->nameType);
297				break;
298		}	/* switch nameType */
299	}	/* for each name */
300	if(certInfo) {
301		return CSSMERR_TP_CERTGROUP_INCOMPLETE;
302	}
303	else {
304		return CSSMERR_APPLETP_CRL_NOT_FOUND;
305	}
306}
307
308/*
309 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
310 * extension.
311 *
312 * Return values:
313 *   CSSM_OK - found and returned fully verified CRL
314 *   CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint
315 *   Anything else - gross error, typically from last LDAP/HTTP attempt
316 *
317 * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime
318 * is non-NULL. How are we supposed to get the CRL which was valid at
319 * a specified time in the past?
320 */
321CSSM_RETURN tpFetchCrlFromNet(
322	TPCertInfo 			&cert,
323	TPVerifyContext		&vfyCtx,
324	TPCrlInfo			*&crl)			// RETURNED
325{
326	/* does the cert have a cRlDistributionPoint? */
327	CSSM_DATA_PTR fieldValue;			// mallocd by CL
328
329	CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
330		&fieldValue);
331	switch(crtn) {
332		case CSSM_OK:
333			break;
334		case CSSMERR_CL_NO_FIELD_VALUES:
335			/* field not present */
336			return CSSMERR_APPLETP_CRL_NOT_FOUND;
337		default:
338			/* gross error */
339			return crtn;
340	}
341	if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
342		tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
343		return CSSMERR_TP_UNKNOWN_FORMAT;
344	}
345	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
346	CE_CRLDistPointsSyntax *dps =
347		(CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
348	TPCrlInfo *rtnCrl = NULL;
349
350	/* default return if we don't find anything */
351	crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
352	for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
353		CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
354		if(dp->distPointName == NULL) {
355			continue;
356		}
357		/*
358		 * FIXME if this uses an indirect CRL, we need to follow the
359		 * crlIssuer field... TBD.
360		 */
361		switch(dp->distPointName->nameType) {
362			case CE_CDNT_NameRelativeToCrlIssuer:
363				/* not yet */
364				tpErrorLog("tpFetchCrlFromNet: "
365					"CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
366				break;
367
368			case CE_CDNT_FullName:
369			{
370				/*
371				 * Since we don't support indirect CRLs (yet), we always pass
372				 * the cert-to-be-verified's issuer as the CRL issuer for
373				 * cache lookup.
374				 */
375				CE_GeneralNames *names = dp->distPointName->dpn.fullName;
376				crtn = tpFetchViaGeneralNames(names,
377					cert,
378					cert.issuerName(),
379					&vfyCtx,
380					0,			// clHand, use the one in vfyCtx
381					0,			// cspHand, ditto
382					vfyCtx.verifyTime,
383					NULL,
384					&rtnCrl);
385				break;
386			}	/* CE_CDNT_FullName */
387
388			default:
389				/* not yet */
390				tpErrorLog("tpFetchCrlFromNet: "
391					"unknown distPointName->nameType (%u)\n",
392						(unsigned)dp->distPointName->nameType);
393				break;
394		}	/* switch distPointName->nameType */
395		if(crtn == CSSM_OK) {
396			/* i.e., tpFetchViaGeneralNames SUCCEEDED */
397			break;
398		}
399	}	/* for each distPoints */
400
401	cert.freeField(&CSSMOID_CrlDistributionPoints,	fieldValue);
402	if(crtn == CSSM_OK) {
403		assert(rtnCrl != NULL);
404		crl = rtnCrl;
405	}
406	return crtn;
407}
408
409/*
410 * Fetch issuer cert of specified cert if the cert has an issuerAltName
411 * with a URI. If non-NULL cert is returned, it has passed subject/issuer
412 * name comparison and signature verification with target cert.
413 *
414 * Return values:
415 *   CSSM_OK - found and returned issuer cert
416 *   CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName
417 *   CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer
418 *      cert, but signature verification needs subsequent retry.
419 *   Anything else - gross error, typically from last LDAP/HTTP attempt
420 */
421CSSM_RETURN tpFetchIssuerFromNet(
422	TPCertInfo			&subject,
423	CSSM_CL_HANDLE		clHand,
424	CSSM_CSP_HANDLE		cspHand,
425	const char			*verifyTime,
426	TPCertInfo			*&issuer)		// RETURNED
427{
428	CSSM_OID_PTR fieldOid = NULL;
429	CSSM_DATA_PTR fieldValue = NULL;	// mallocd by CL
430	CSSM_RETURN crtn;
431	bool hasAIA = false;
432
433	/* look for the Authority Info Access extension first */
434	fieldOid = (CSSM_OID_PTR)&CSSMOID_AuthorityInfoAccess;
435	crtn = subject.fetchField(fieldOid,
436		&fieldValue);
437	hasAIA = (crtn == CSSM_OK);
438	if (!hasAIA) {
439		/* fall back to Issuer Alternative Name extension */
440		fieldOid = (CSSM_OID_PTR)&CSSMOID_IssuerAltName;
441		crtn = subject.fetchField(fieldOid,
442								  &fieldValue);
443	}
444	switch(crtn) {
445		case CSSM_OK:
446			break;
447		case CSSMERR_CL_NO_FIELD_VALUES:
448			/* field not present */
449			return CSSMERR_TP_CERTGROUP_INCOMPLETE;
450		default:
451			/* gross error */
452			return crtn;
453	}
454	if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
455		tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
456		return CSSMERR_TP_UNKNOWN_FORMAT;
457	}
458	CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
459	CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
460	TPCertInfo *rtnCert = NULL;
461	if (hasAIA) {	/* authority info access */
462		CE_AuthorityInfoAccess *access = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
463		for (uint32 index = 0; access && index < access->numAccessDescriptions; index++) {
464			CE_AccessDescription *accessDesc = &access->accessDescriptions[index];
465			CSSM_OID_PTR methodOid = (CSSM_OID_PTR)&accessDesc->accessMethod;
466			/* look for the CA Issuers method */
467			if(methodOid->Data != NULL && methodOid->Length == CSSMOID_CA_ISSUERS.Length &&
468			   !memcmp(methodOid->Data, CSSMOID_CA_ISSUERS.Data, methodOid->Length)) {
469				CE_GeneralNames aiaNames = { 1, &accessDesc->accessLocation };
470				/* attempt to fetch cert from named location */
471				crtn = tpFetchViaGeneralNames(&aiaNames,
472											  subject,
473											  NULL,		// issuer - not used
474											  NULL,		// verifyContext
475											  clHand,
476											  cspHand,
477											  verifyTime,
478											  &rtnCert,
479											  NULL);
480				if (crtn == CSSM_OK ||
481					crtn == CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE) {
482					break; // got one
483				}
484			}
485		}
486		subject.freeField(fieldOid,	fieldValue);
487	}
488	else {  /* issuer alt name */
489		crtn = tpFetchViaGeneralNames(names,
490						subject,
491						NULL,		// issuer - not used
492						NULL,		// verifyContext
493						clHand,
494						cspHand,
495						verifyTime,
496						&rtnCert,
497						NULL);
498		subject.freeField(fieldOid,	fieldValue);
499	}
500	switch(crtn) {
501		case CSSM_OK:
502		case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
503			issuer = rtnCert;
504			break;
505		default:
506			break;
507	}
508	return crtn;
509}
510
511
512