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