1/*
2 * Copyright (c) 2002 Apple Computer, 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 * tpCredRequest.cpp - credential request functions SubmitCredRequest,
21 *                     RetrieveCredResult
22 *
23 * Created 1/24/2002 by Doug Mitchell.
24 */
25
26#include "AppleTPSession.h"
27#include "certGroupUtils.h"
28#include "tpdebugging.h"
29#include "tpTime.h"
30#include <Security/oidsalg.h>
31#include <Security/oidsattr.h>
32#include <Security/oidscert.h>
33#include <Security/cssmapple.h>
34#include <security_utilities/debugging.h>
35#include <Security/cssmapple.h>
36#include <assert.h>
37
38#define tpCredDebug(args...)	secdebug("tpCred", ## args)
39
40/*
41 * Build up a CSSM_X509_NAME from an arbitrary list of name/OID pairs.
42 * We do one a/v pair per RDN.
43 */
44CSSM_X509_NAME * AppleTPSession::buildX509Name(
45	const CSSM_APPLE_TP_NAME_OID *nameArray,
46	unsigned numNames)
47{
48	CSSM_X509_NAME *top = (CSSM_X509_NAME *)malloc(sizeof(CSSM_X509_NAME));
49	top->numberOfRDNs = numNames;
50	if(numNames == 0) {
51		/* legal! */
52		top->RelativeDistinguishedName = NULL;
53		return top;
54	}
55	top->RelativeDistinguishedName =
56		(CSSM_X509_RDN_PTR)malloc(sizeof(CSSM_X509_RDN) * numNames);
57	CSSM_X509_RDN_PTR rdn;
58	const CSSM_APPLE_TP_NAME_OID *nameOid;
59	unsigned nameDex;
60	for(nameDex=0; nameDex<numNames; nameDex++) {
61		rdn = &top->RelativeDistinguishedName[nameDex];
62		nameOid = &nameArray[nameDex];
63		rdn->numberOfPairs = 1;
64		rdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR)
65			malloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR));
66		CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = rdn->AttributeTypeAndValue;
67		tpCopyCssmData(*this, nameOid->oid, &atvp->type);
68		atvp->value.Length = strlen(nameOid->string);
69		if(tpCompareOids(&CSSMOID_CountryName, nameOid->oid)) {
70			/*
71			 * Country handled differently per RFC 3280 - must be printable,
72			 * max of two characters in length
73			 */
74			if(atvp->value.Length > 2) {
75				CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
76			}
77			for(unsigned dex=0; dex<atvp->value.Length; dex++) {
78				int c = nameOid->string[dex];
79				if(!isprint(c) || (c == EOF)) {
80					CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
81				}
82			}
83			atvp->valueType = BER_TAG_PRINTABLE_STRING;
84		}
85		/* other special cases per RFC 3280 */
86		else if(tpCompareOids(&CSSMOID_DNQualifier, nameOid->oid)) {
87			atvp->valueType = BER_TAG_PRINTABLE_STRING;
88		}
89		else if(tpCompareOids(&CSSMOID_SerialNumber, nameOid->oid)) {
90			atvp->valueType = BER_TAG_PRINTABLE_STRING;
91		}
92		else if(tpCompareOids(&CSSMOID_EmailAddress, nameOid->oid)) {
93			atvp->valueType = BER_TAG_IA5_STRING;
94		}
95		else {
96			/* Default type */
97			atvp->valueType = BER_TAG_PKIX_UTF8_STRING;
98		}
99		atvp->value.Data = (uint8 *)malloc(atvp->value.Length);
100		memmove(atvp->value.Data, nameOid->string, atvp->value.Length);
101	}
102	return top;
103}
104
105/* free the CSSM_X509_NAME obtained from buildX509Name */
106void AppleTPSession::freeX509Name(
107	CSSM_X509_NAME *top)
108{
109	if(top == NULL) {
110		return;
111	}
112	unsigned nameDex;
113	CSSM_X509_RDN_PTR rdn;
114	for(nameDex=0; nameDex<top->numberOfRDNs; nameDex++) {
115		rdn = &top->RelativeDistinguishedName[nameDex];
116		if(rdn->AttributeTypeAndValue) {
117			for(unsigned aDex=0; aDex<rdn->numberOfPairs; aDex++) {
118				CSSM_X509_TYPE_VALUE_PAIR_PTR atvp =
119					&rdn->AttributeTypeAndValue[aDex];
120				free(atvp->type.Data);
121				free(atvp->value.Data);
122			}
123			free(rdn->AttributeTypeAndValue);
124		}
125	}
126	free(top->RelativeDistinguishedName);
127	free(top);
128}
129
130/* Obtain a CSSM_X509_TIME representing "now" plus specified seconds */
131
132/*
133 * Although RFC 2459, *the* spec for X509 certs, allows for not before/after
134 * times to be expressed in ther generalized (4-digit year) or UTC (2-digit year
135 * with implied century rollover), IE 5 on Mac will not accept the generalized
136 * format.
137 */
138#define TP_FOUR_DIGIT_YEAR		0
139#if		TP_FOUR_DIGIT_YEAR
140#define TP_TIME_FORMAT 	TIME_GEN
141#define TP_TIME_TAG		BER_TAG_GENERALIZED_TIME
142#else
143#define TP_TIME_FORMAT 	TIME_UTC
144#define TP_TIME_TAG		BER_TAG_UTC_TIME
145#endif	/* TP_FOUR_DIGIT_YEAR */
146
147CSSM_X509_TIME * AppleTPSession::buildX509Time(
148	unsigned secondsFromNow)
149{
150	CSSM_X509_TIME *xtime = (CSSM_X509_TIME *)malloc(sizeof(CSSM_X509_TIME));
151	xtime->timeType = TP_TIME_TAG;
152	char *ts = (char *)malloc(GENERALIZED_TIME_STRLEN + 1);
153	{
154		StLock<Mutex> _(tpTimeLock());
155		timeAtNowPlus(secondsFromNow, TP_TIME_FORMAT, ts);
156	}
157	xtime->time.Data = (uint8 *)ts;
158	xtime->time.Length = strlen(ts);
159	return xtime;
160}
161
162/* Free CSSM_X509_TIME obtained in buildX509Time */
163void AppleTPSession::freeX509Time(
164	CSSM_X509_TIME	*xtime)
165{
166	if(xtime == NULL) {
167		return;
168	}
169	free((char *)xtime->time.Data);
170	free(xtime);
171}
172
173/*
174 * Cook up a CSSM_DATA with specified integer, DER style (minimum number of
175 * bytes, big-endian).
176 */
177static void intToDER(
178	CSSM_INTPTR theInt,
179	CSSM_DATA &DER_Data,
180	Allocator &alloc)
181{
182	/*
183 	 * Calculate length in bytes of encoded integer, minimum length of 1.
184	 */
185	DER_Data.Length = 1;
186	uintptr_t unsignedInt = (uintptr_t)theInt;
187	while(unsignedInt > 0xff) {
188		DER_Data.Length++;
189		unsignedInt >>= 8;
190	}
191
192	/*
193	 * DER encoding requires top bit to be zero, else it's a negative number.
194	 * Even though we're passing around integers as CSSM_INTPTR, they really are
195 	 * always unsigned.
196	 * unsignedInt contains the m.s. byte of theInt in its l.s. byte.
197	 */
198	if(unsignedInt & 0x80) {
199		DER_Data.Length++;
200	}
201
202	DER_Data.Data = (uint8 *)alloc.malloc(DER_Data.Length);
203	uint8 *dst = DER_Data.Data + DER_Data.Length - 1;
204	unsignedInt = (uintptr_t)theInt;
205	for(unsigned dex=0; dex<DER_Data.Length; dex++) {
206		*dst-- = unsignedInt & 0xff;
207		/* this shifts off to zero if we're adding a zero at the top */
208		unsignedInt >>= 8;
209	}
210}
211
212/* The reverse of the above. */
213static CSSM_INTPTR DERToInt(
214	const CSSM_DATA &DER_Data)
215{
216	CSSM_INTPTR rtn = 0;
217	uint8 *bp = DER_Data.Data;
218	for(unsigned dex=0; dex<DER_Data.Length; dex++) {
219		rtn <<= 8;
220		rtn |= *bp++;
221	}
222	return rtn;
223}
224
225/* Convert a reference key to a raw key. */
226void AppleTPSession::refKeyToRaw(
227	CSSM_CSP_HANDLE	cspHand,
228	const CSSM_KEY	*refKey,
229	CSSM_KEY_PTR	rawKey)			// RETURNED
230{
231	CSSM_CC_HANDLE		ccHand;
232	CSSM_RETURN			crtn;
233	CSSM_ACCESS_CREDENTIALS	creds;
234
235	memset(rawKey, 0, sizeof(CSSM_KEY));
236	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
237	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
238			CSSM_ALGID_NONE,
239			CSSM_ALGMODE_NONE,
240			&creds,				// passPhrase
241			NULL,				// wrapping key
242			NULL,				// init vector
243			CSSM_PADDING_NONE,	// Padding
244			0,					// Params
245			&ccHand);
246	if(crtn) {
247		tpCredDebug("AppleTPSession::refKeyToRaw: context err");
248		CssmError::throwMe(crtn);
249	}
250
251	crtn = CSSM_WrapKey(ccHand,
252		&creds,
253		refKey,
254		NULL,			// DescriptiveData
255		rawKey);
256	if(crtn != CSSM_OK) {
257		tpCredDebug("AppleTPSession::refKeyToRaw: wrapKey err");
258		CssmError::throwMe(crtn);
259	}
260	CSSM_DeleteContext(ccHand);
261}
262
263
264/*
265 * Cook up an unsigned cert.
266 * This is just a wrapper for CSSM_CL_CertCreateTemplate().
267 */
268void AppleTPSession::makeCertTemplate(
269	/* required */
270	CSSM_CL_HANDLE			clHand,
271	CSSM_CSP_HANDLE			cspHand,		// for converting ref to raw key
272	uint32					serialNumber,
273	const CSSM_X509_NAME	*issuerName,
274	const CSSM_X509_NAME	*subjectName,
275	const CSSM_X509_TIME	*notBefore,
276	const CSSM_X509_TIME	*notAfter,
277	const CSSM_KEY			*subjectPubKey,
278	const CSSM_OID			&sigOid,		// e.g., CSSMOID_SHA1WithRSA
279	/* optional */
280	const CSSM_DATA			*subjectUniqueId,
281	const CSSM_DATA			*issuerUniqueId,
282	CSSM_X509_EXTENSION		*extensions,
283	unsigned				numExtensions,
284	CSSM_DATA_PTR			&rawCert)
285{
286	CSSM_FIELD		*certTemp;
287	unsigned		fieldDex = 0;			// index into certTemp
288	CSSM_DATA		serialDER = {0, NULL};	// serial number, DER format
289	CSSM_DATA		versionDER = {0, NULL};
290	unsigned		extNum;
291	CSSM_X509_ALGORITHM_IDENTIFIER algId;
292	const CSSM_KEY	*actPubKey;
293	CSSM_KEY		rawPubKey;
294	CSSM_BOOL		freeRawKey = CSSM_FALSE;
295
296	rawCert = NULL;
297	algId.algorithm = sigOid;
298	algId.parameters.Data = NULL;
299	algId.parameters.Length = 0;
300
301	/*
302	 * Convert possible ref public key to raw format as required by CL.
303	 */
304	switch(subjectPubKey->KeyHeader.BlobType) {
305		case CSSM_KEYBLOB_RAW:
306			actPubKey = subjectPubKey;
307			break;
308		case CSSM_KEYBLOB_REFERENCE:
309			refKeyToRaw(cspHand, subjectPubKey, &rawPubKey);
310			actPubKey = &rawPubKey;
311			freeRawKey = CSSM_TRUE;
312			break;
313		default:
314			tpCredDebug("CSSM_CL_CertCreateTemplate: bad key blob type (%u)",
315				(unsigned)subjectPubKey->KeyHeader.BlobType);
316			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
317	}
318
319
320	/*
321	 * version, always 2 (X509v3)
322	 * serialNumber thru subjectPubKey
323	 */
324	unsigned numFields = 8 + numExtensions;
325	if(subjectUniqueId) {
326		numFields++;
327	}
328	if(issuerUniqueId) {
329		numFields++;
330	}
331
332	certTemp = (CSSM_FIELD *)malloc(sizeof(CSSM_FIELD) * numFields);
333
334
335	/* version */
336	intToDER(2, versionDER, *this);
337	certTemp[fieldDex].FieldOid = CSSMOID_X509V1Version;
338	certTemp[fieldDex++].FieldValue = versionDER;
339
340	/* serial number */
341	intToDER(serialNumber, serialDER, *this);
342	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SerialNumber;
343	certTemp[fieldDex++].FieldValue = serialDER;
344
345	/* subject and issuer name  */
346	certTemp[fieldDex].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
347	certTemp[fieldDex].FieldValue.Data = (uint8 *)issuerName;
348	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
349
350	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
351	certTemp[fieldDex].FieldValue.Data = (uint8 *)subjectName;
352	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
353
354	/* not before/after */
355	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotBefore;
356	certTemp[fieldDex].FieldValue.Data = (uint8 *)notBefore;
357	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);
358
359	certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotAfter;
360	certTemp[fieldDex].FieldValue.Data = (uint8 *)notAfter;
361	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);
362
363	/* the subject key */
364	certTemp[fieldDex].FieldOid = CSSMOID_CSSMKeyStruct;
365	certTemp[fieldDex].FieldValue.Data = (uint8 *)actPubKey;
366	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_KEY);
367
368	/* signature algorithm */
369	certTemp[fieldDex].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
370	certTemp[fieldDex].FieldValue.Data = (uint8 *)&algId;
371	certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
372
373	/* subject/issuer unique IDs */
374	if(subjectUniqueId != 0) {
375		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateSubjectUniqueId;
376		certTemp[fieldDex++].FieldValue = *subjectUniqueId;
377	}
378	if(issuerUniqueId != 0) {
379		certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateIssuerUniqueId;
380		certTemp[fieldDex++].FieldValue = *issuerUniqueId;
381	}
382
383	for(extNum=0; extNum<numExtensions; extNum++) {
384		CSSM_X509_EXTENSION_PTR ext = &extensions[extNum];
385		if(ext->format == CSSM_X509_DATAFORMAT_PARSED) {
386			certTemp[fieldDex].FieldOid = ext->extnId;
387		}
388		else {
389			certTemp[fieldDex].FieldOid = CSSMOID_X509V3CertificateExtensionCStruct;
390		}
391		certTemp[fieldDex].FieldValue.Data = (uint8 *)ext;
392		certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
393	}
394	assert(fieldDex == numFields);
395
396	/*
397	 * OK, here we go
398	 */
399	rawCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
400	rawCert->Data = NULL;
401	rawCert->Length = 0;
402	CSSM_RETURN crtn = CSSM_CL_CertCreateTemplate(clHand,
403		fieldDex,
404		certTemp,
405		rawCert);
406	if(crtn) {
407		tpCredDebug("CSSM_CL_CertCreateTemplate returned %ld", (long)crtn);
408		free(rawCert->Data);
409		free(rawCert);
410		rawCert = NULL;
411	}
412
413	/* free the stuff we mallocd to get here */
414	free(serialDER.Data);
415	free(versionDER.Data);
416	free(certTemp);
417	if(freeRawKey) {
418		tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
419	}
420	if(crtn) {
421		CssmError::throwMe(crtn);
422	}
423}
424
425/* given a cert and a ReferenceIdentifier, fill in ReferenceIdentifier and
426 * add it and the cert to tpCredMap. */
427void AppleTPSession::addCertToMap(
428	const CSSM_DATA		*cert,
429	CSSM_DATA_PTR		refId)
430{
431	StLock<Mutex> _(tpCredMapLock);
432
433	TpCredHandle hand = reinterpret_cast<TpCredHandle>(cert);
434	intToDER(hand, *refId, *this);
435	tpCredMap[hand] = cert;
436}
437
438/* given a ReferenceIdentifier, obtain associated cert and remove from the map */
439CSSM_DATA_PTR AppleTPSession::getCertFromMap(
440	const CSSM_DATA *refId)
441{
442	StLock<Mutex> _(tpCredMapLock);
443	CSSM_DATA_PTR rtn = NULL;
444
445	if((refId == NULL) || (refId->Data == NULL)) {
446		return NULL;
447	}
448	TpCredHandle hand = DERToInt(*refId);
449	credMap::iterator it = tpCredMap.find(hand);
450	if(it == tpCredMap.end()) {
451		return NULL;
452	}
453	rtn = const_cast<CSSM_DATA *>(it->second);
454	tpCredMap.erase(hand);
455	return rtn;
456}
457
458/*
459 * SubmitCredRequest, CSR form.
460 */
461void AppleTPSession::SubmitCsrRequest(
462	const CSSM_TP_REQUEST_SET &RequestInput,
463	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
464	sint32 &EstimatedTime,						// RETURNED
465	CssmData &ReferenceIdentifier)				// RETURNED
466{
467	CSSM_DATA_PTR	csrPtr = NULL;
468	CSSM_CC_HANDLE 	sigHand = 0;
469	CSSM_APPLE_CL_CSR_REQUEST csrReq;
470
471	memset(&csrReq, 0, sizeof(csrReq));
472
473	/* for now we're using the same struct for input as the the normal
474	 * X509 cert request. */
475	CSSM_APPLE_TP_CERT_REQUEST *certReq =
476		(CSSM_APPLE_TP_CERT_REQUEST *)RequestInput.Requests;
477	if((certReq->cspHand == 0) ||
478	   (certReq->clHand == 0) ||
479	   (certReq->certPublicKey == NULL) ||
480	   (certReq->issuerPrivateKey == NULL) ||
481	   (certReq->signatureOid.Data == NULL)) {
482		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
483	}
484
485	/* convert ref public key to raw per CL requirements */
486	const CSSM_KEY *subjectPubKey = certReq->certPublicKey;
487	const CSSM_KEY *actPubKey = NULL;
488	CSSM_BOOL freeRawKey = CSSM_FALSE;
489	CSSM_KEY rawPubKey;
490
491	switch(subjectPubKey->KeyHeader.BlobType) {
492		case CSSM_KEYBLOB_RAW:
493			actPubKey = subjectPubKey;
494			break;
495		case CSSM_KEYBLOB_REFERENCE:
496			refKeyToRaw(certReq->cspHand, subjectPubKey, &rawPubKey);
497			actPubKey = &rawPubKey;
498			freeRawKey = CSSM_TRUE;
499			break;
500		default:
501			tpCredDebug("SubmitCsrRequest: bad key blob type (%u)",
502				(unsigned)subjectPubKey->KeyHeader.BlobType);
503			CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
504	}
505
506	/* cook up a CL-passthrough-specific request */
507	csrReq.subjectNameX509 	 = buildX509Name(certReq->subjectNames,
508											certReq->numSubjectNames);
509	csrReq.signatureAlg 	 = certReq->signatureAlg;
510	csrReq.signatureOid 	 = certReq->signatureOid;
511	csrReq.cspHand 			 = certReq->cspHand;
512	csrReq.subjectPublicKey  = actPubKey;
513	csrReq.subjectPrivateKey = certReq->issuerPrivateKey;
514	csrReq.challengeString   = certReq->challengeString;
515
516	/* A crypto handle to pass to the CL */
517	CSSM_RETURN crtn;
518	crtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
519			certReq->signatureAlg,
520			(CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
521			certReq->issuerPrivateKey,
522			&sigHand);
523	if(crtn) {
524		tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)crtn);
525		goto abort;
526	}
527
528	/* down to the CL to do the actual work */
529	crtn = CSSM_CL_PassThrough(certReq->clHand,
530		sigHand,
531		CSSM_APPLEX509CL_OBTAIN_CSR,
532		&csrReq,
533		(void **)&csrPtr);
534	if(crtn) {
535		tpCredDebug("CSSM_CL_PassThrough returned %ld", (long)crtn);
536		goto abort;
537	}
538
539	/* save it for retrieval by RetrieveCredResult */
540	addCertToMap(csrPtr, &ReferenceIdentifier);
541	EstimatedTime = 0;
542
543abort:
544	/* free local resources */
545	if(csrReq.subjectNameX509) {
546		freeX509Name(csrReq.subjectNameX509);
547	}
548	if(sigHand) {
549		CSSM_DeleteContext(sigHand);
550	}
551	if(freeRawKey) {
552		tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
553	}
554	if(crtn) {
555		CssmError::throwMe(crtn);
556	}
557}
558
559/*
560 * Submit cred (cert) request. Currently the only form of request we
561 * handle is the basis "sign this cert with key right now", with policy OI
562 * CSSMOID_APPLE_TP_LOCAL_CERT_GEN.
563 */
564void AppleTPSession::SubmitCredRequest(
565	const CSSM_TP_AUTHORITY_ID *PreferredAuthority,
566	CSSM_TP_AUTHORITY_REQUEST_TYPE RequestType,
567	const CSSM_TP_REQUEST_SET &RequestInput,
568	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
569	sint32 &EstimatedTime,
570	CssmData &ReferenceIdentifier)
571{
572	/* free all of these on return if non-NULL */
573	CSSM_DATA_PTR certTemplate = NULL;
574	CSSM_X509_TIME_PTR notBeforeX509 = NULL;
575	CSSM_X509_TIME_PTR notAfterX509 = NULL;
576	CSSM_X509_NAME_PTR subjectX509 = NULL;
577	CSSM_X509_NAME_PTR issuerX509 = NULL;
578	CSSM_X509_EXTENSION_PTR extens509 = NULL;
579	CSSM_CC_HANDLE sigContext = 0;
580
581	/* this gets saved on success */
582	CSSM_DATA_PTR signedCert = NULL;
583
584	/* validate rather limited set of input args */
585	if(PreferredAuthority != NULL) {
586		CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
587	}
588	if(RequestType != CSSM_TP_AUTHORITY_REQUEST_CERTISSUE) {
589		CssmError::throwMe(CSSMERR_TP_UNSUPPORTED_SERVICE);
590	}
591	if(CallerAuthContext == NULL) {
592		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
593	}
594	if((RequestInput.NumberOfRequests != 1) ||
595	   (RequestInput.Requests == NULL)) {
596		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
597	}
598
599	/* Apple-specific args */
600	const CSSM_TP_POLICYINFO *tpPolicy = &CallerAuthContext->Policy;
601	if((tpPolicy->NumberOfPolicyIds != 1) ||
602	   (tpPolicy->PolicyIds == NULL)) {
603		CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
604	}
605	if(tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
606		&CSSMOID_APPLE_TP_CSR_GEN)) {
607		/* break out to CSR-specific code */
608		SubmitCsrRequest(RequestInput, CallerAuthContext, EstimatedTime, ReferenceIdentifier);
609		return;
610	}
611	else if(!tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
612		&CSSMOID_APPLE_TP_LOCAL_CERT_GEN)) {
613		CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
614	}
615
616	CSSM_APPLE_TP_CERT_REQUEST *certReq =
617		(CSSM_APPLE_TP_CERT_REQUEST *)RequestInput.Requests;
618	if((certReq->cspHand == 0) ||
619	   (certReq->clHand == 0) ||
620	   (certReq->certPublicKey == NULL) ||
621	   (certReq->issuerPrivateKey == NULL)) {
622		CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
623	}
624	if((certReq->numExtensions != 0) & (certReq->extensions == NULL)) {
625		CssmError::throwMe(CSSMERR_TP_INVALID_POINTER);
626	}
627
628	CSSM_RETURN ourRtn = CSSM_OK;
629
630	try {
631		/* convert caller's friendly names and times to CDSA style */
632		subjectX509 = buildX509Name(certReq->subjectNames, certReq->numSubjectNames);
633		if(certReq->issuerNames != NULL) {
634			issuerX509 = buildX509Name(certReq->issuerNames, certReq->numIssuerNames);
635		}
636		else if(certReq->issuerNameX509) {
637			/* caller obtained this from an existing signer's cert */
638			issuerX509 = certReq->issuerNameX509;
639		}
640		else {
641			/* self-signed */
642			issuerX509 = subjectX509;
643		}
644		notBeforeX509 = buildX509Time(certReq->notBefore);
645		notAfterX509 = buildX509Time(certReq->notAfter);
646
647		if(certReq->numExtensions != 0) {
648			/* convert extensions array from CE_DataAndType to CSSM_X509_EXTENSION */
649			extens509 = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION) *
650					certReq->numExtensions);
651			memset(extens509, 0, sizeof(CSSM_X509_EXTENSION) *
652					certReq->numExtensions);
653			for(unsigned dex=0; dex<certReq->numExtensions; dex++) {
654				CSSM_X509_EXTENSION *extn = &extens509[dex];
655				CE_DataAndType *cdt = &certReq->extensions[dex];
656				void *parsedValue;
657				CSSM_OID extnId;
658
659				switch(cdt->type) {
660					case DT_AuthorityKeyID:
661						parsedValue = &cdt->extension.authorityKeyID;
662						extnId = CSSMOID_AuthorityKeyIdentifier;
663						break;
664					case DT_SubjectKeyID:
665						parsedValue = &cdt->extension.subjectKeyID;
666						extnId = CSSMOID_SubjectKeyIdentifier;
667						break;
668					case DT_KeyUsage:
669						parsedValue = &cdt->extension.keyUsage;
670						extnId = CSSMOID_KeyUsage;
671						break;
672					case DT_SubjectAltName:
673						parsedValue = &cdt->extension.subjectAltName;
674						extnId = CSSMOID_SubjectAltName;
675						break;
676					case DT_IssuerAltName:
677						parsedValue = &cdt->extension.issuerAltName;
678						extnId = CSSMOID_IssuerAltName;
679						break;
680					case DT_ExtendedKeyUsage:
681						parsedValue = &cdt->extension.extendedKeyUsage;
682						extnId = CSSMOID_ExtendedKeyUsage;
683						break;
684					case DT_BasicConstraints:
685						parsedValue = &cdt->extension.basicConstraints;
686						extnId = CSSMOID_BasicConstraints;
687						break;
688					case DT_CertPolicies:
689						parsedValue = &cdt->extension.certPolicies;
690						extnId = CSSMOID_CertificatePolicies;
691						break;
692					case DT_NetscapeCertType:
693						parsedValue = &cdt->extension.netscapeCertType;
694						extnId = CSSMOID_NetscapeCertType;
695						break;
696					case DT_CrlDistributionPoints:
697						parsedValue = &cdt->extension.crlDistPoints;
698						extnId = CSSMOID_CrlDistributionPoints;
699						break;
700					case DT_AuthorityInfoAccess:
701						parsedValue = &cdt->extension.authorityInfoAccess;
702						extnId = CSSMOID_AuthorityInfoAccess;
703						break;
704					case DT_Other:
705					default:
706						tpCredDebug("SubmitCredRequest: DT_Other not supported");
707						CssmError::throwMe(CSSMERR_TP_UNKNOWN_TAG);
708						// NOT REACHED
709				}
710				extn->extnId   			= extnId;
711				extn->critical 			= cdt->critical;
712				extn->format   			= CSSM_X509_DATAFORMAT_PARSED;
713				extn->value.parsedValue 	= parsedValue;
714				extn->BERvalue.Data = NULL;
715				extn->BERvalue.Length = 0;
716			}	/* for each extension */
717		} 		/* converting extensions */
718
719		/* cook up the unsigned template */
720		makeCertTemplate(certReq->clHand,
721			certReq->cspHand,
722			certReq->serialNumber,
723			issuerX509,
724			subjectX509,
725			notBeforeX509,
726			notAfterX509,
727			certReq->certPublicKey,
728			certReq->signatureOid,
729			NULL,				// subjectUniqueID, not used here (yet)
730			NULL,				// issuerUniqueId
731			extens509,
732			certReq->numExtensions,
733			certTemplate);
734
735		/* create signature context */
736		ourRtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
737				certReq->signatureAlg,
738				(CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
739				certReq->issuerPrivateKey,
740				&sigContext);
741		if(ourRtn) {
742			tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)ourRtn);
743			CssmError::throwMe(ourRtn);
744		}
745
746		signedCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
747		signedCert->Data = NULL;
748		signedCert->Length = 0;
749		ourRtn = CSSM_CL_CertSign(certReq->clHand,
750			sigContext,
751			certTemplate,		// CertToBeSigned
752			NULL,				// SignScope
753			0,					// ScopeSize,
754			signedCert);
755		if(ourRtn) {
756			tpCredDebug("CSSM_CL_CertSign returned %ld", (long)ourRtn);
757			CssmError::throwMe(ourRtn);
758		}
759
760		/* save it for retrieval by RetrieveCredResult */
761		addCertToMap(signedCert, &ReferenceIdentifier);
762		EstimatedTime = 0;
763	}
764	catch (const CssmError &cerr) {
765		tpCredDebug("SubmitCredRequest: CSSM error %ld", (long)cerr.error);
766		ourRtn = cerr.error;
767	}
768	catch(...) {
769		tpCredDebug("SubmitCredRequest: unknown exception");
770		ourRtn = CSSMERR_TP_INTERNAL_ERROR;	// ??
771	}
772
773	/* free reources */
774	tpFreeCssmData(*this, certTemplate, CSSM_TRUE);
775	freeX509Name(subjectX509);
776	if(certReq->issuerNames) {
777		freeX509Name(issuerX509);
778	}
779	/* else same as subject */
780	freeX509Time(notBeforeX509);
781	freeX509Time(notAfterX509);
782	if(extens509) {
783		free(extens509);
784	}
785	if(sigContext != 0) {
786		CSSM_DeleteContext(sigContext);
787	}
788	if(ourRtn) {
789		CssmError::throwMe(ourRtn);
790	}
791}
792
793void AppleTPSession::RetrieveCredResult(
794	const CssmData &ReferenceIdentifier,
795	const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthCredentials,
796	sint32 &EstimatedTime,
797	CSSM_BOOL &ConfirmationRequired,
798	CSSM_TP_RESULT_SET_PTR &RetrieveOutput)
799{
800	CSSM_DATA *cert = getCertFromMap(&ReferenceIdentifier);
801
802	if(cert == NULL) {
803		tpCredDebug("RetrieveCredResult: refId not found");
804		CssmError::throwMe(CSSMERR_TP_INVALID_IDENTIFIER);
805	}
806
807	/* CSSM_TP_RESULT_SET.Results points to a CSSM_ENCODED_CERT */
808	CSSM_ENCODED_CERT *encCert = (CSSM_ENCODED_CERT *)malloc(sizeof(CSSM_ENCODED_CERT));
809	encCert->CertType = CSSM_CERT_X_509v3;
810	encCert->CertEncoding = CSSM_CERT_ENCODING_DER;
811
812	/*
813	 * caller must free all three:
814	 *   CSSM_TP_RESULT_SET_PTR RetrieveOutput
815	 *   RetrieveOutput->Results (CSSM_ENCODED_CERT *encCert)
816	 *   encCert->CertBlob.Data (the actual cert)
817	 * We free:
818	 * 	 cert 					-- mallocd in SubmitCredRequest
819	 */
820	encCert->CertBlob = *cert;
821	RetrieveOutput = (CSSM_TP_RESULT_SET_PTR)malloc(
822		sizeof(CSSM_TP_RESULT_SET));
823	RetrieveOutput->Results = encCert;
824	RetrieveOutput->NumberOfResults = 1;
825	ConfirmationRequired = CSSM_FALSE;
826	free(cert);
827	EstimatedTime = 0;
828}
829