/* * Copyright (c) 2002 Apple Computer, 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 * * Created 1/24/2002 by Doug Mitchell. */ #include "AppleTPSession.h" #include "certGroupUtils.h" #include "tpdebugging.h" #include "tpTime.h" #include #include #include #include #include #include #include #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; nameDexRelativeDistinguishedName[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->valueType = BER_TAG_PRINTABLE_STRING; atvp->value.Length = strlen(nameOid->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; nameDexnumberOfRDNs; nameDex++) { rdn = &top->RelativeDistinguishedName[nameDex]; if(rdn->AttributeTypeAndValue) { for(unsigned aDex=0; aDexnumberOfPairs; 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 _(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( uint32 theInt, CSSM_DATA &DER_Data, CssmAllocator &alloc) { if(theInt < 0x100) { DER_Data.Length = 1; DER_Data.Data = (uint8 *)alloc.malloc(1); DER_Data.Data[0] = (unsigned char)(theInt); } else if(theInt < 0x10000) { DER_Data.Length = 2; DER_Data.Data = (uint8 *)alloc.malloc(2); DER_Data.Data[0] = (unsigned char)(theInt >> 8); DER_Data.Data[1] = (unsigned char)(theInt); } else if(theInt < 0x1000000) { DER_Data.Length = 3; DER_Data.Data = (uint8 *)alloc.malloc(3); DER_Data.Data[0] = (unsigned char)(theInt >> 16); DER_Data.Data[1] = (unsigned char)(theInt >> 8); DER_Data.Data[2] = (unsigned char)(theInt); } else { DER_Data.Length = 4; DER_Data.Data = (uint8 *)alloc.malloc(4); DER_Data.Data[0] = (unsigned char)(theInt >> 24); DER_Data.Data[1] = (unsigned char)(theInt >> 16); DER_Data.Data[2] = (unsigned char)(theInt >> 8); DER_Data.Data[3] = (unsigned char)(theInt); } } /* The reverse of the above. */ static uint32 DERToInt( const CSSM_DATA &DER_Data) { uint32 rtn = 0; uint8 *bp = DER_Data.Data; for(unsigned dex=0; dexKeyHeader.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; extNumformat == 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 %s", cssmErrorString(crtn).c_str()); 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 _(tpCredMapLock); TpCredHandle hand = reinterpret_cast(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 _(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(it->second); tpCredMap.erase(hand); return rtn; } /* * SubmitCredRequest, CSR form. */ void AppleTPSession::SubmitCsrRequest( const CSSM_TP_REQUEST_SET &RequestInput, 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, NULL, // AccessCred certReq->issuerPrivateKey, &sigHand); if(crtn) { tpCredDebug("CSSM_CSP_CreateSignatureContext returned %s", cssmErrorString(crtn).c_str()); 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_CSP_CreateSignatureContext returned %s", cssmErrorString(crtn).c_str()); 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, 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; dexnumExtensions; 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_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_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 %s", cssmErrorString(ourRtn).c_str()); 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 %s", cssmErrorString(ourRtn).c_str()); CssmError::throwMe(ourRtn); } /* save it for retrieval by RetrieveCredResult */ addCertToMap(signedCert, &ReferenceIdentifier); EstimatedTime = 0; } catch (const CssmError &cerr) { tpCredDebug("SubmitCredRequest: CSSM error %s", cssmErrorString(cerr).c_str()); ourRtn = cerr.cssmError(); } 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) { const 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) */ encCert->CertBlob = *cert; RetrieveOutput = (CSSM_TP_RESULT_SET_PTR)malloc( sizeof(CSSM_TP_RESULT_SET)); RetrieveOutput->Results = encCert; RetrieveOutput->NumberOfResults = 1; ConfirmationRequired = CSSM_FALSE; EstimatedTime = 0; }