]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_apple_x509_tp/lib/tpCredRequest.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / tpCredRequest.cpp
diff --git a/OSX/libsecurity_apple_x509_tp/lib/tpCredRequest.cpp b/OSX/libsecurity_apple_x509_tp/lib/tpCredRequest.cpp
new file mode 100644 (file)
index 0000000..be25dae
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2002,2011,2014 Apple Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+ * tpCredRequest.cpp - credential request functions SubmitCredRequest, 
+ *                     RetrieveCredResult 
+ *
+ */
+#include "AppleTPSession.h"
+#include "certGroupUtils.h"
+#include "tpdebugging.h"
+#include "tpTime.h"
+#include <Security/oidsalg.h>
+#include <Security/oidsattr.h>
+#include <Security/oidscert.h>
+#include <Security/cssmapple.h>
+#include <security_utilities/debugging.h>
+#include <Security/cssmapple.h>
+#include <assert.h>
+
+#define tpCredDebug(args...)   secdebug("tpCred", ## args)
+
+/*
+ * Build up a CSSM_X509_NAME from an arbitrary list of name/OID pairs. 
+ * We do one a/v pair per RDN. 
+ */
+CSSM_X509_NAME * AppleTPSession::buildX509Name(
+       const CSSM_APPLE_TP_NAME_OID *nameArray,
+       unsigned numNames)
+{
+       CSSM_X509_NAME *top = (CSSM_X509_NAME *)malloc(sizeof(CSSM_X509_NAME));
+       top->numberOfRDNs = numNames;
+       if(numNames == 0) {
+               /* legal! */
+               top->RelativeDistinguishedName = NULL;
+               return top;
+       }
+       top->RelativeDistinguishedName = 
+               (CSSM_X509_RDN_PTR)malloc(sizeof(CSSM_X509_RDN) * numNames);
+       CSSM_X509_RDN_PTR rdn;
+       const CSSM_APPLE_TP_NAME_OID *nameOid;
+       unsigned nameDex;
+       for(nameDex=0; nameDex<numNames; nameDex++) {
+               rdn = &top->RelativeDistinguishedName[nameDex];
+               nameOid = &nameArray[nameDex];
+               rdn->numberOfPairs = 1;
+               rdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR)
+                       malloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR));
+               CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = rdn->AttributeTypeAndValue;
+               tpCopyCssmData(*this, nameOid->oid, &atvp->type);
+               atvp->value.Length = strlen(nameOid->string);
+               if(tpCompareOids(&CSSMOID_CountryName, nameOid->oid)) {
+                       /* 
+                        * Country handled differently per RFC 3280 - must be printable,
+                        * max of two characters in length
+                        */
+                       if(atvp->value.Length > 2) {
+                               CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
+                       }
+                       for(unsigned dex=0; dex<atvp->value.Length; dex++) {
+                               int c = nameOid->string[dex];
+                               if(!isprint(c) || (c == EOF)) {
+                                       CssmError::throwMe(CSSMERR_TP_INVALID_DATA);
+                               }
+                       }
+                       atvp->valueType = BER_TAG_PRINTABLE_STRING;
+               }
+               /* other special cases per RFC 3280 */
+               else if(tpCompareOids(&CSSMOID_DNQualifier, nameOid->oid)) {
+                       atvp->valueType = BER_TAG_PRINTABLE_STRING;
+               }
+               else if(tpCompareOids(&CSSMOID_SerialNumber, nameOid->oid)) {
+                       atvp->valueType = BER_TAG_PRINTABLE_STRING;
+               }
+               else if(tpCompareOids(&CSSMOID_EmailAddress, nameOid->oid)) {
+                       atvp->valueType = BER_TAG_IA5_STRING;
+               }
+               else {
+                       /* Default type */
+                       atvp->valueType = BER_TAG_PKIX_UTF8_STRING;
+               }
+               atvp->value.Data = (uint8 *)malloc(atvp->value.Length);
+               memmove(atvp->value.Data, nameOid->string, atvp->value.Length);
+       }
+       return top;
+}
+
+/* free the CSSM_X509_NAME obtained from buildX509Name */
+void AppleTPSession::freeX509Name(
+       CSSM_X509_NAME *top)
+{
+       if(top == NULL) {
+               return;
+       }
+       unsigned nameDex;
+       CSSM_X509_RDN_PTR rdn;
+       for(nameDex=0; nameDex<top->numberOfRDNs; nameDex++) {
+               rdn = &top->RelativeDistinguishedName[nameDex];
+               if(rdn->AttributeTypeAndValue) {
+                       for(unsigned aDex=0; aDex<rdn->numberOfPairs; aDex++) {
+                               CSSM_X509_TYPE_VALUE_PAIR_PTR atvp = 
+                                       &rdn->AttributeTypeAndValue[aDex];
+                               free(atvp->type.Data);
+                               free(atvp->value.Data);
+                       }
+                       free(rdn->AttributeTypeAndValue);
+               }
+       }
+       free(top->RelativeDistinguishedName);
+       free(top);
+}
+
+/* Obtain a CSSM_X509_TIME representing "now" plus specified seconds */
+
+/* 
+ * Although RFC 2459, *the* spec for X509 certs, allows for not before/after
+ * times to be expressed in ther generalized (4-digit year) or UTC (2-digit year
+ * with implied century rollover), IE 5 on Mac will not accept the generalized
+ * format.
+ */
+#define TP_FOUR_DIGIT_YEAR             0
+#if            TP_FOUR_DIGIT_YEAR
+#define TP_TIME_FORMAT         TIME_GEN
+#define TP_TIME_TAG            BER_TAG_GENERALIZED_TIME
+#else
+#define TP_TIME_FORMAT         TIME_UTC
+#define TP_TIME_TAG            BER_TAG_UTC_TIME
+#endif /* TP_FOUR_DIGIT_YEAR */
+
+CSSM_X509_TIME * AppleTPSession::buildX509Time(
+       unsigned secondsFromNow)
+{
+       CSSM_X509_TIME *xtime = (CSSM_X509_TIME *)malloc(sizeof(CSSM_X509_TIME));
+       xtime->timeType = TP_TIME_TAG;
+       char *ts = (char *)malloc(GENERALIZED_TIME_STRLEN + 1);
+       {
+               StLock<Mutex> _(tpTimeLock());
+               timeAtNowPlus(secondsFromNow, TP_TIME_FORMAT, ts);
+       }
+       xtime->time.Data = (uint8 *)ts;
+       xtime->time.Length = strlen(ts);
+       return xtime;
+}
+
+/* Free CSSM_X509_TIME obtained in buildX509Time */
+void AppleTPSession::freeX509Time(
+       CSSM_X509_TIME  *xtime)
+{
+       if(xtime == NULL) {
+               return;
+       }
+       free((char *)xtime->time.Data);
+       free(xtime);
+}
+
+/*
+ * Cook up a CSSM_DATA with specified integer, DER style (minimum number of
+ * bytes, big-endian).
+ */
+static void intToDER(
+       CSSM_INTPTR theInt,
+       CSSM_DATA &DER_Data,
+       Allocator &alloc)
+{
+       /*
+        * Calculate length in bytes of encoded integer, minimum length of 1. 
+        */
+       DER_Data.Length = 1;
+       uintptr_t unsignedInt = (uintptr_t)theInt;
+       while(unsignedInt > 0xff) {
+               DER_Data.Length++;
+               unsignedInt >>= 8;
+       }
+
+       /*
+        * DER encoding requires top bit to be zero, else it's a negative number. 
+        * Even though we're passing around integers as CSSM_INTPTR, they really are
+        * always unsigned. 
+        * unsignedInt contains the m.s. byte of theInt in its l.s. byte. 
+        */
+       if(unsignedInt & 0x80) {
+               DER_Data.Length++;
+       }
+       
+       DER_Data.Data = (uint8 *)alloc.malloc(DER_Data.Length);
+       uint8 *dst = DER_Data.Data + DER_Data.Length - 1;
+       unsignedInt = (uintptr_t)theInt;
+       for(unsigned dex=0; dex<DER_Data.Length; dex++) {
+               *dst-- = unsignedInt & 0xff;
+               /* this shifts off to zero if we're adding a zero at the top */
+               unsignedInt >>= 8;
+       }
+}
+
+/* The reverse of the above. */
+static CSSM_INTPTR DERToInt(
+       const CSSM_DATA &DER_Data)
+{
+       CSSM_INTPTR rtn = 0;
+       uint8 *bp = DER_Data.Data;
+       for(unsigned dex=0; dex<DER_Data.Length; dex++) {
+               rtn <<= 8;
+               rtn |= *bp++;
+       }
+       return rtn;
+}
+
+/* Convert a reference key to a raw key. */
+void AppleTPSession::refKeyToRaw(
+       CSSM_CSP_HANDLE cspHand,
+       const CSSM_KEY  *refKey,        
+       CSSM_KEY_PTR    rawKey)                 // RETURNED
+{
+       CSSM_CC_HANDLE          ccHand;
+       CSSM_RETURN                     crtn;
+       CSSM_ACCESS_CREDENTIALS creds;
+       
+       memset(rawKey, 0, sizeof(CSSM_KEY));
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
+                       CSSM_ALGID_NONE,
+                       CSSM_ALGMODE_NONE,
+                       &creds,                         // passPhrase
+                       NULL,                           // wrapping key
+                       NULL,                           // init vector
+                       CSSM_PADDING_NONE,      // Padding
+                       0,                                      // Params
+                       &ccHand);
+       if(crtn) {
+               tpCredDebug("AppleTPSession::refKeyToRaw: context err");
+               CssmError::throwMe(crtn);
+       }
+       
+       crtn = CSSM_WrapKey(ccHand,
+               &creds,
+               refKey,
+               NULL,                   // DescriptiveData
+               rawKey);
+       if(crtn != CSSM_OK) {
+               tpCredDebug("AppleTPSession::refKeyToRaw: wrapKey err");
+               CssmError::throwMe(crtn);
+       }
+       CSSM_DeleteContext(ccHand);
+}
+
+
+/*
+ * Cook up an unsigned cert.
+ * This is just a wrapper for CSSM_CL_CertCreateTemplate().
+ */
+void AppleTPSession::makeCertTemplate(
+       /* required */
+       CSSM_CL_HANDLE                  clHand,
+       CSSM_CSP_HANDLE                 cspHand,                // for converting ref to raw key
+       uint32                                  serialNumber,
+       const CSSM_X509_NAME    *issuerName,    
+       const CSSM_X509_NAME    *subjectName,
+       const CSSM_X509_TIME    *notBefore,     
+       const CSSM_X509_TIME    *notAfter,      
+       const CSSM_KEY                  *subjectPubKey,
+       const CSSM_OID                  &sigOid,                // e.g., CSSMOID_SHA1WithRSA
+       /* optional */
+       const CSSM_DATA                 *subjectUniqueId,
+       const CSSM_DATA                 *issuerUniqueId,
+       CSSM_X509_EXTENSION             *extensions,
+       unsigned                                numExtensions,
+       CSSM_DATA_PTR                   &rawCert)
+{
+       CSSM_FIELD              *certTemp;              
+       unsigned                fieldDex = 0;                   // index into certTemp
+       CSSM_DATA               serialDER = {0, NULL};  // serial number, DER format
+       CSSM_DATA               versionDER = {0, NULL};
+       unsigned                extNum;
+       CSSM_X509_ALGORITHM_IDENTIFIER algId;
+       const CSSM_KEY  *actPubKey;
+       CSSM_KEY                rawPubKey;
+       CSSM_BOOL               freeRawKey = CSSM_FALSE;
+       
+       rawCert = NULL;
+    
+    /* 
+     * Set Signature Algorithm OID and parameters
+     */
+       algId.algorithm = sigOid;
+    
+    /* NULL params - skip for ECDSA */
+    CSSM_ALGORITHMS algorithmType = 0;
+    cssmOidToAlg(&sigOid, &algorithmType);
+    switch(algorithmType) {
+        case CSSM_ALGID_SHA1WithECDSA:
+        case CSSM_ALGID_SHA224WithECDSA:
+        case CSSM_ALGID_SHA256WithECDSA:
+        case CSSM_ALGID_SHA384WithECDSA:
+        case CSSM_ALGID_SHA512WithECDSA:
+        case CSSM_ALGID_ECDSA_SPECIFIED:
+            algId.parameters.Data = NULL;
+            algId.parameters.Length = 0;
+            break;
+        default:
+            static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
+            CSSM_DATA encNullData;
+            encNullData.Data = (uint8 *)encNull;
+            encNullData.Length = 2;
+            
+            algId.parameters = encNullData;
+            break;
+    }
+
+       
+       /*
+        * Convert possible ref public key to raw format as required by CL.
+        */
+       switch(subjectPubKey->KeyHeader.BlobType) {
+               case CSSM_KEYBLOB_RAW:
+                       actPubKey = subjectPubKey;
+                       break;
+               case CSSM_KEYBLOB_REFERENCE:
+                       refKeyToRaw(cspHand, subjectPubKey, &rawPubKey);
+                       actPubKey = &rawPubKey;
+                       freeRawKey = CSSM_TRUE;
+                       break;
+               default:
+                       tpCredDebug("CSSM_CL_CertCreateTemplate: bad key blob type (%u)",
+                               (unsigned)subjectPubKey->KeyHeader.BlobType);
+                       CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
+       }
+                       
+
+       /*
+        * version, always 2 (X509v3)
+        * serialNumber thru subjectPubKey
+        */
+       unsigned numFields = 8 + numExtensions;
+       if(subjectUniqueId) {
+               numFields++;
+       }
+       if(issuerUniqueId) {
+               numFields++;
+       }
+
+       certTemp = (CSSM_FIELD *)malloc(sizeof(CSSM_FIELD) * numFields);
+
+        
+       /* version */
+       intToDER(2, versionDER, *this);
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1Version;
+       certTemp[fieldDex++].FieldValue = versionDER;
+       
+       /* serial number */
+       intToDER(serialNumber, serialDER, *this);
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1SerialNumber;
+       certTemp[fieldDex++].FieldValue = serialDER;
+
+       /* subject and issuer name  */
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)issuerName;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
+       
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)subjectName;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_NAME);
+
+       /* not before/after */
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotBefore;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)notBefore;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);
+
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1ValidityNotAfter;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)notAfter;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_TIME);
+
+       /* the subject key */
+       certTemp[fieldDex].FieldOid = CSSMOID_CSSMKeyStruct;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)actPubKey;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_KEY);
+
+       /* signature algorithm */
+       certTemp[fieldDex].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
+       certTemp[fieldDex].FieldValue.Data = (uint8 *)&algId;
+       certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
+       
+       /* subject/issuer unique IDs */
+       if(subjectUniqueId != 0) {
+               certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateSubjectUniqueId;
+               certTemp[fieldDex++].FieldValue = *subjectUniqueId;
+       }
+       if(issuerUniqueId != 0) {
+               certTemp[fieldDex].FieldOid = CSSMOID_X509V1CertificateIssuerUniqueId;
+               certTemp[fieldDex++].FieldValue = *issuerUniqueId;
+       }
+
+       for(extNum=0; extNum<numExtensions; extNum++) {
+               CSSM_X509_EXTENSION_PTR ext = &extensions[extNum];
+               if(ext->format == CSSM_X509_DATAFORMAT_PARSED) {
+                       certTemp[fieldDex].FieldOid = ext->extnId;
+               }
+               else {
+                       certTemp[fieldDex].FieldOid = CSSMOID_X509V3CertificateExtensionCStruct;
+               }
+               certTemp[fieldDex].FieldValue.Data = (uint8 *)ext;
+               certTemp[fieldDex++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
+       }
+       assert(fieldDex == numFields);
+       
+       /*
+        * OK, here we go
+        */
+       rawCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
+       rawCert->Data = NULL;
+       rawCert->Length = 0;
+       CSSM_RETURN crtn = CSSM_CL_CertCreateTemplate(clHand,
+               fieldDex,
+               certTemp,
+               rawCert);
+       if(crtn) {
+               tpCredDebug("CSSM_CL_CertCreateTemplate returned %ld", (long)crtn);
+               free(rawCert->Data);
+               free(rawCert);
+               rawCert = NULL;
+       }
+
+       /* free the stuff we mallocd to get here */
+       free(serialDER.Data);
+       free(versionDER.Data);
+       free(certTemp);
+       if(freeRawKey) {
+               tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
+       }
+       if(crtn) {
+               CssmError::throwMe(crtn);
+       }
+}
+
+/* given a cert and a ReferenceIdentifier, fill in ReferenceIdentifier and 
+ * add it and the cert to tpCredMap. */
+void AppleTPSession::addCertToMap(
+       const CSSM_DATA         *cert,
+       CSSM_DATA_PTR           refId)
+{
+       StLock<Mutex> _(tpCredMapLock);
+
+       TpCredHandle hand = reinterpret_cast<TpCredHandle>(cert);
+       intToDER(hand, *refId, *this);
+       tpCredMap[hand] = cert;
+}
+       
+/* given a ReferenceIdentifier, obtain associated cert and remove from the map */
+CSSM_DATA_PTR AppleTPSession::getCertFromMap(
+       const CSSM_DATA *refId)
+{
+       StLock<Mutex> _(tpCredMapLock);
+       CSSM_DATA_PTR rtn = NULL;
+       
+       if((refId == NULL) || (refId->Data == NULL)) {
+               return NULL;
+       }
+       TpCredHandle hand = DERToInt(*refId);
+       credMap::iterator it = tpCredMap.find(hand);
+       if(it == tpCredMap.end()) {
+               return NULL;
+       }
+       rtn = const_cast<CSSM_DATA *>(it->second);
+       tpCredMap.erase(hand);
+       return rtn;
+}
+
+/*
+ * SubmitCredRequest, CSR form.
+ */
+void AppleTPSession::SubmitCsrRequest(
+       const CSSM_TP_REQUEST_SET &RequestInput,
+       const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
+       sint32 &EstimatedTime,                                          // RETURNED
+       CssmData &ReferenceIdentifier)                          // RETURNED
+{
+       CSSM_DATA_PTR   csrPtr = NULL;
+       CSSM_CC_HANDLE  sigHand = 0;
+       CSSM_APPLE_CL_CSR_REQUEST csrReq;
+       
+       memset(&csrReq, 0, sizeof(csrReq));
+
+       /* for now we're using the same struct for input as the the normal
+        * X509 cert request. */
+       CSSM_APPLE_TP_CERT_REQUEST *certReq =
+               (CSSM_APPLE_TP_CERT_REQUEST *)RequestInput.Requests;
+       if((certReq->cspHand == 0) || 
+          (certReq->clHand == 0) ||
+          (certReq->certPublicKey == NULL) ||
+          (certReq->issuerPrivateKey == NULL) ||
+          (certReq->signatureOid.Data == NULL)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
+       }
+       
+       /* convert ref public key to raw per CL requirements */
+       const CSSM_KEY *subjectPubKey = certReq->certPublicKey;
+       const CSSM_KEY *actPubKey = NULL;
+       CSSM_BOOL freeRawKey = CSSM_FALSE;
+       CSSM_KEY rawPubKey;
+       
+       switch(subjectPubKey->KeyHeader.BlobType) {
+               case CSSM_KEYBLOB_RAW:
+                       actPubKey = subjectPubKey;
+                       break;
+               case CSSM_KEYBLOB_REFERENCE:
+                       refKeyToRaw(certReq->cspHand, subjectPubKey, &rawPubKey);
+                       actPubKey = &rawPubKey;
+                       freeRawKey = CSSM_TRUE;
+                       break;
+               default:
+                       tpCredDebug("SubmitCsrRequest: bad key blob type (%u)",
+                               (unsigned)subjectPubKey->KeyHeader.BlobType);
+                       CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
+       }
+
+       /* cook up a CL-passthrough-specific request */
+       csrReq.subjectNameX509   = buildX509Name(certReq->subjectNames, 
+                                                                                       certReq->numSubjectNames);
+       csrReq.signatureAlg      = certReq->signatureAlg;
+       csrReq.signatureOid      = certReq->signatureOid;
+       csrReq.cspHand                   = certReq->cspHand;
+       csrReq.subjectPublicKey  = actPubKey;
+       csrReq.subjectPrivateKey = certReq->issuerPrivateKey;
+       csrReq.challengeString   = certReq->challengeString;
+       
+       /* A crypto handle to pass to the CL */
+       CSSM_RETURN crtn;
+       crtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
+                       certReq->signatureAlg,
+                       (CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
+                       certReq->issuerPrivateKey,
+                       &sigHand);
+       if(crtn) {
+               tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)crtn);
+               goto abort;
+       }
+       
+       /* down to the CL to do the actual work */
+       crtn = CSSM_CL_PassThrough(certReq->clHand,
+               sigHand,
+               CSSM_APPLEX509CL_OBTAIN_CSR,
+               &csrReq,
+               (void **)&csrPtr);
+       if(crtn) {
+               tpCredDebug("CSSM_CL_PassThrough returned %ld", (long)crtn);
+               goto abort;
+       }
+
+       /* save it for retrieval by RetrieveCredResult */
+       addCertToMap(csrPtr, &ReferenceIdentifier);
+       EstimatedTime = 0;
+
+abort:
+       /* free local resources */
+       if(csrReq.subjectNameX509) {
+               freeX509Name(csrReq.subjectNameX509);
+       }
+       if(sigHand) {
+               CSSM_DeleteContext(sigHand);
+       }
+       if(freeRawKey) {
+               tpFreeCssmData(*this, &rawPubKey.KeyData, CSSM_FALSE);
+       }
+       if(crtn) {
+               CssmError::throwMe(crtn);
+       }
+}
+
+/*
+ * Submit cred (cert) request. Currently the only form of request we
+ * handle is the basis "sign this cert with key right now", with policy OI
+ * CSSMOID_APPLE_TP_LOCAL_CERT_GEN.
+ */
+void AppleTPSession::SubmitCredRequest(
+       const CSSM_TP_AUTHORITY_ID *PreferredAuthority,
+       CSSM_TP_AUTHORITY_REQUEST_TYPE RequestType,
+       const CSSM_TP_REQUEST_SET &RequestInput,
+       const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthContext,
+       sint32 &EstimatedTime,
+       CssmData &ReferenceIdentifier)
+{
+       /* free all of these on return if non-NULL */
+       CSSM_DATA_PTR certTemplate = NULL;
+       CSSM_X509_TIME_PTR notBeforeX509 = NULL;
+       CSSM_X509_TIME_PTR notAfterX509 = NULL;
+       CSSM_X509_NAME_PTR subjectX509 = NULL;
+       CSSM_X509_NAME_PTR issuerX509 = NULL;
+       CSSM_X509_EXTENSION_PTR extens509 = NULL;
+       CSSM_CC_HANDLE sigContext = 0;
+       
+       /* this gets saved on success */
+       CSSM_DATA_PTR signedCert = NULL;
+       
+       /* validate rather limited set of input args */
+       if(PreferredAuthority != NULL) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_AUTHORITY);
+       }
+       if(RequestType != CSSM_TP_AUTHORITY_REQUEST_CERTISSUE) {
+               CssmError::throwMe(CSSMERR_TP_UNSUPPORTED_SERVICE);
+       }
+       if(CallerAuthContext == NULL) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
+       }
+       if((RequestInput.NumberOfRequests != 1) ||
+          (RequestInput.Requests == NULL)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
+       }
+       
+       /* Apple-specific args */
+       const CSSM_TP_POLICYINFO *tpPolicy = &CallerAuthContext->Policy;
+       if((tpPolicy->NumberOfPolicyIds != 1) ||
+          (tpPolicy->PolicyIds == NULL)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_CALLERAUTH_CONTEXT_POINTER);
+       }
+       if(tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
+               &CSSMOID_APPLE_TP_CSR_GEN)) {
+               /* break out to CSR-specific code */
+               SubmitCsrRequest(RequestInput, CallerAuthContext, EstimatedTime, ReferenceIdentifier);
+               return;
+       }
+       else if(!tpCompareCssmData(&tpPolicy->PolicyIds->FieldOid,
+               &CSSMOID_APPLE_TP_LOCAL_CERT_GEN)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
+       }
+
+       CSSM_APPLE_TP_CERT_REQUEST *certReq =
+               (CSSM_APPLE_TP_CERT_REQUEST *)RequestInput.Requests;
+       if((certReq->cspHand == 0) || 
+          (certReq->clHand == 0) ||
+          (certReq->certPublicKey == NULL) ||
+          (certReq->issuerPrivateKey == NULL)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS);
+       }
+       if((certReq->numExtensions != 0) & (certReq->extensions == NULL)) {
+               CssmError::throwMe(CSSMERR_TP_INVALID_POINTER);
+       }
+       
+       CSSM_RETURN ourRtn = CSSM_OK;
+       
+       try {
+               /* convert caller's friendly names and times to CDSA style */
+               subjectX509 = buildX509Name(certReq->subjectNames, certReq->numSubjectNames);
+               if(certReq->issuerNames != NULL) {
+                       issuerX509 = buildX509Name(certReq->issuerNames, certReq->numIssuerNames);
+               }
+               else if(certReq->issuerNameX509) {
+                       /* caller obtained this from an existing signer's cert */
+                       issuerX509 = certReq->issuerNameX509;
+               }
+               else {
+                       /* self-signed */
+                       issuerX509 = subjectX509;
+               }
+               notBeforeX509 = buildX509Time(certReq->notBefore);
+               notAfterX509 = buildX509Time(certReq->notAfter);
+               
+               if(certReq->numExtensions != 0) { 
+                       /* convert extensions array from CE_DataAndType to CSSM_X509_EXTENSION */
+                       extens509 = (CSSM_X509_EXTENSION *)malloc(sizeof(CSSM_X509_EXTENSION) * 
+                                       certReq->numExtensions);
+                       memset(extens509, 0, sizeof(CSSM_X509_EXTENSION) * 
+                                       certReq->numExtensions);
+                       for(unsigned dex=0; dex<certReq->numExtensions; dex++) {
+                               CSSM_X509_EXTENSION *extn = &extens509[dex];
+                               CE_DataAndType *cdt = &certReq->extensions[dex];
+                               void *parsedValue;
+                               CSSM_OID extnId;
+                               
+                               switch(cdt->type) {
+                                       case DT_AuthorityKeyID: 
+                                               parsedValue = &cdt->extension.authorityKeyID;
+                                               extnId = CSSMOID_AuthorityKeyIdentifier;
+                                               break;
+                                       case DT_SubjectKeyID:           
+                                               parsedValue = &cdt->extension.subjectKeyID;
+                                               extnId = CSSMOID_SubjectKeyIdentifier;
+                                               break;
+                                       case DT_KeyUsage:                                
+                                               parsedValue = &cdt->extension.keyUsage;
+                                               extnId = CSSMOID_KeyUsage;
+                                               break;
+                                       case DT_SubjectAltName:                 
+                                               parsedValue = &cdt->extension.subjectAltName;
+                                               extnId = CSSMOID_SubjectAltName;
+                                               break;
+                                       case DT_IssuerAltName:                  
+                                               parsedValue = &cdt->extension.issuerAltName;
+                                               extnId = CSSMOID_IssuerAltName;
+                                               break;
+                                       case DT_ExtendedKeyUsage:               
+                                               parsedValue = &cdt->extension.extendedKeyUsage;
+                                               extnId = CSSMOID_ExtendedKeyUsage;
+                                               break;
+                                       case DT_BasicConstraints:               
+                                               parsedValue = &cdt->extension.basicConstraints;
+                                               extnId = CSSMOID_BasicConstraints;
+                                               break;
+                                       case DT_CertPolicies:                   
+                                               parsedValue = &cdt->extension.certPolicies;
+                                               extnId = CSSMOID_CertificatePolicies;
+                                               break;
+                                       case DT_NetscapeCertType:               
+                                               parsedValue = &cdt->extension.netscapeCertType;
+                                               extnId = CSSMOID_NetscapeCertType;
+                                               break;
+                                       case DT_CrlDistributionPoints:          
+                                               parsedValue = &cdt->extension.crlDistPoints;
+                                               extnId = CSSMOID_CrlDistributionPoints;
+                                               break;
+                                       case DT_AuthorityInfoAccess:            
+                                               parsedValue = &cdt->extension.authorityInfoAccess;
+                                               extnId = CSSMOID_AuthorityInfoAccess;
+                                               break;
+                                       case DT_Other:          
+                                       default:
+                                               tpCredDebug("SubmitCredRequest: DT_Other not supported");
+                                               CssmError::throwMe(CSSMERR_TP_UNKNOWN_TAG);
+                                               // NOT REACHED
+                               }
+                               extn->extnId                    = extnId;
+                               extn->critical                  = cdt->critical;
+                               extn->format                    = CSSM_X509_DATAFORMAT_PARSED;
+                               extn->value.parsedValue         = parsedValue;
+                               extn->BERvalue.Data = NULL;
+                               extn->BERvalue.Length = 0;
+                       }       /* for each extension */
+               }               /* converting extensions */
+                       
+               /* cook up the unsigned template */
+               makeCertTemplate(certReq->clHand,
+                       certReq->cspHand,
+                       certReq->serialNumber,
+                       issuerX509,
+                       subjectX509,
+                       notBeforeX509,
+                       notAfterX509,
+                       certReq->certPublicKey,
+                       certReq->signatureOid,
+                       NULL,                           // subjectUniqueID, not used here (yet)
+                       NULL,                           // issuerUniqueId
+                       extens509,
+                       certReq->numExtensions,
+                       certTemplate);
+                       
+               /* create signature context */          
+               ourRtn = CSSM_CSP_CreateSignatureContext(certReq->cspHand,
+                               certReq->signatureAlg,
+                               (CallerAuthContext ? CallerAuthContext->CallerCredentials : NULL),
+                               certReq->issuerPrivateKey,
+                               &sigContext);
+               if(ourRtn) {
+                       tpCredDebug("CSSM_CSP_CreateSignatureContext returned %ld", (long)ourRtn);
+                       CssmError::throwMe(ourRtn);
+               }
+               
+               signedCert = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
+               signedCert->Data = NULL;
+               signedCert->Length = 0;
+               ourRtn = CSSM_CL_CertSign(certReq->clHand,
+                       sigContext,
+                       certTemplate,           // CertToBeSigned
+                       NULL,                           // SignScope
+                       0,                                      // ScopeSize,
+                       signedCert);
+               if(ourRtn) {
+                       tpCredDebug("CSSM_CL_CertSign returned %ld", (long)ourRtn);
+                       CssmError::throwMe(ourRtn);
+               }
+               
+               /* save it for retrieval by RetrieveCredResult */
+               addCertToMap(signedCert, &ReferenceIdentifier);
+               EstimatedTime = 0;
+       }
+       catch (const CssmError &cerr) {
+               tpCredDebug("SubmitCredRequest: CSSM error %ld", (long)cerr.error);
+               ourRtn = cerr.error;
+       }
+       catch(...) {
+               tpCredDebug("SubmitCredRequest: unknown exception");
+               ourRtn = CSSMERR_TP_INTERNAL_ERROR;     // ??
+       }
+       
+       /* free reources */
+       tpFreeCssmData(*this, certTemplate, CSSM_TRUE);
+       freeX509Name(subjectX509);
+       if(certReq->issuerNames) {
+               freeX509Name(issuerX509);
+       }
+       /* else same as subject */
+       freeX509Time(notBeforeX509);
+       freeX509Time(notAfterX509);
+       if(extens509) {
+               free(extens509);
+       }
+       if(sigContext != 0) {
+               CSSM_DeleteContext(sigContext);
+       }
+       if(ourRtn) {
+               CssmError::throwMe(ourRtn);
+       }
+}
+
+void AppleTPSession::RetrieveCredResult(
+       const CssmData &ReferenceIdentifier,
+       const CSSM_TP_CALLERAUTH_CONTEXT *CallerAuthCredentials,
+       sint32 &EstimatedTime,
+       CSSM_BOOL &ConfirmationRequired,
+       CSSM_TP_RESULT_SET_PTR &RetrieveOutput)
+{
+       CSSM_DATA *cert = getCertFromMap(&ReferenceIdentifier);
+       
+       if(cert == NULL) {
+               tpCredDebug("RetrieveCredResult: refId not found");
+               CssmError::throwMe(CSSMERR_TP_INVALID_IDENTIFIER);
+       }
+       
+       /* CSSM_TP_RESULT_SET.Results points to a CSSM_ENCODED_CERT */
+       CSSM_ENCODED_CERT *encCert = (CSSM_ENCODED_CERT *)malloc(sizeof(CSSM_ENCODED_CERT));
+       encCert->CertType = CSSM_CERT_X_509v3;
+       encCert->CertEncoding = CSSM_CERT_ENCODING_DER;
+       
+       /* 
+        * caller must free all three:
+        *   CSSM_TP_RESULT_SET_PTR RetrieveOutput
+        *   RetrieveOutput->Results (CSSM_ENCODED_CERT *encCert)
+        *   encCert->CertBlob.Data (the actual cert)
+        * We free:
+        *       cert                                   -- mallocd in SubmitCredRequest
+        */
+       encCert->CertBlob = *cert;
+       RetrieveOutput = (CSSM_TP_RESULT_SET_PTR)malloc(
+               sizeof(CSSM_TP_RESULT_SET));
+       RetrieveOutput->Results = encCert;
+       RetrieveOutput->NumberOfResults = 1;
+       ConfirmationRequired = CSSM_FALSE;
+       free(cert);     
+       EstimatedTime = 0;
+}