/* * Copyright (c) 2000-2001 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. */ /* * CertFields.cpp - convert between snacc-based Certificate components and CDSA-style * fields. A major component of DecodedCert. * * Created 9/1/2000 by Doug Mitchell. * Copyright (c) 2000 by Apple Computer. * * The code in this file is dreadfully gross. There is no practical way to do this * work (converting between C++ snacc types and C CSDA types) without the kind * of brute force code you see here. */ #include "DecodedCert.h" #include "cldebugging.h" #include "CertBuilder.h" #include "CLCertExtensions.h" #include "SnaccUtils.h" #include #include #include #include #include /* * Routines for common validity checking for certificateToSign fields. * * Call from setField*: verify field isn't already set, optionally validate * input length */ static void tbsSetCheck( void *fieldToSet, const CssmData &fieldValue, uint32 expLength, const char *op) { if(fieldToSet != NULL) { /* can't add another */ errorLog1("setField(%s): field already set\n", op); CssmError::throwMe(CSSMERR_CL_INVALID_NUMBER_OF_FIELDS); } if((expLength != 0) && (fieldValue.length() != expLength)) { errorLog3("setField(%s): bad length : exp %d got %d\n", op, (int)expLength, (int)fieldValue.length()); CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } } /* * Call from getField* for unique fields - detect missing field or index out of bounds. */ static bool tbsGetCheck( void *requiredField, uint32 reqIndex) { if((requiredField == NULL) || (reqIndex != 0)) { return false; } else { return true; } } /*** *** Version *** Format = DER-encoded int (max of four bytes in this case) ***/ static bool getField_Version ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->version, index)) { return false; } /* cook up big-endian char array representation */ int ivers = *cert.certificateToSign->version; uint32 uvers = static_cast(ivers); uint8 chars[sizeof(uint32)]; for(uint32 i=0; i>= 8; } fieldValue.copy(chars, sizeof(uint32)); numFields = 1; return true; } static void setField_Version ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->version, fieldValue, 0, "version"); /* get big-endian int from *fieldValue.Data */ if(fieldValue.length() > sizeof(unsigned)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } uint32 vers = 0; uint8 *cp = fieldValue; for(unsigned i=0; iversion = new Version((int)vers); cert.certificateToSign->version->Set((int)vers); } #if this_is_a_template /*** *** Version *** Format = DER-encoded int (always four bytes in this case) ***/ static bool getField_Version ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { tbsGetCheck(cert.certificateToSign->version, index); } static void setField_Version ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->version, fieldValue, sizeof(uint32), "version"); } static void freeField_Version ( CssmOwnedData &fieldValue) { } #endif /*** *** Serial Number *** Format = DER-encoded int, variable length ***/ static bool getField_SerialNumber ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(index > 0) { return false; } char *cp = cert.certificateToSign->serialNumber; uint32 len = cert.certificateToSign->serialNumber.Len(); fieldValue.copy(cp, len); numFields = 1; return true; } static void setField_SerialNumber ( DecodedCert &cert, const CssmData &fieldValue) { cert.certificateToSign->serialNumber.Set(fieldValue, fieldValue.Length); } /*** *** Issuer Name, Subject Name (C struct version) *** Format = CSSM_X509_NAME *** class Name from sm_x501if ***/ /* first, the common code */ static bool getField_RDN ( const Name &name, uint32 &numFields, // RETURNED (if successful, 0 or 1) CssmOwnedData &fieldValue) // RETURNED { RDNSequence *rdns = name.rDNSequence; int numRdns = rdns->Count(); if((rdns == NULL) || (numRdns == 0)) { /* not technically an error */ return false; } /* alloc top-level CSSM_X509_NAME and its RelativeDistinguishedName array */ CssmAllocator &alloc = fieldValue.allocator; fieldValue.malloc(sizeof(CSSM_X509_NAME)); CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue.data(); memset(x509Name, 0, sizeof(CSSM_X509_NAME)); x509Name->numberOfRDNs = numRdns; x509Name->RelativeDistinguishedName = (CSSM_X509_RDN_PTR)alloc.malloc(sizeof(CSSM_X509_RDN) * numRdns); CSSM_X509_RDN_PTR currRdn = x509Name->RelativeDistinguishedName; memset(currRdn, 0, sizeof(CSSM_X509_RDN) * numRdns); rdns->SetCurrElmt(0); for(int rdnDex=0; rdnDexCurr(); if(rdn == NULL) { /* not sure how this can happen... */ dprintf1("getField_RDN: NULL rdn at index %d\n", rdnDex); /* next snacc RDN but keep CDSA position unchanged */ rdns->GoNext(); // snacc format x509Name->numberOfRDNs--; // since we're skipping one continue; } int numAttrs = rdn->Count(); if(numAttrs == 0) { dprintf1("getField_RDN: zero numAttrs at index %d\n", rdnDex); rdns->GoNext(); x509Name->numberOfRDNs--; // since we're skipping one continue; } /* alloc CSSM_X509_TYPE_VALUE_PAIR array for this rdn */ currRdn->numberOfPairs = numAttrs; currRdn->AttributeTypeAndValue = (CSSM_X509_TYPE_VALUE_PAIR_PTR) alloc.malloc(sizeof(CSSM_X509_TYPE_VALUE_PAIR) * numAttrs); CSSM_X509_TYPE_VALUE_PAIR_PTR currAttr = currRdn->AttributeTypeAndValue; memset(currAttr, 0, sizeof(CSSM_X509_TYPE_VALUE_PAIR) * numAttrs); /* descend into array of attribute/values */ rdn->SetCurrElmt(0); for(int attrDex=0; attrDexCurr(); if(att == NULL) { /* not sure how this can happen... */ dprintf1("getField_RDN: NULL att at index %d\n", attrDex); rdn->GoNext(); currRdn->numberOfPairs--; continue; } /* * Convert snacc-style AttributeTypeAndDistinguishedValue to * CSSM-style CSSM_X509_TYPE_VALUE_PAIR * * Hopefully 'value' is one of the types defined in DirectoryString, * defined in sm_x520sa. Some certs use IA5String, which is not * technically legal and is not handled by DirectoryString, so * we have to handle that ourself. See e.g. the Thawte serverbasic * cert, which has an email address in IA5String format. */ CSM_Buffer *cbuf = att->value.value; AsnBuf buf; AsnLen len = cbuf->Length(); AsnTag tag; AsnLen elmtLen; ENV_TYPE env; char *valData; int valLength; DirectoryString *dirStr = NULL; buf.InstallData(cbuf->Access(), len); try { tag = BDecTag (buf, len, env); elmtLen = BDecLen (buf, len, env); } catch(...) { errorLog0("getField_RDN: malformed DirectoryString (1)\n"); /* FIXME - throw? Discard the whole cert? What? */ rdn->GoNext(); currRdn->numberOfPairs--; continue; } /* current buf ptr is at the string value's contents. */ if((tag == MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) || (tag == MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE))) { /* any other printable types not handled by DirectoryString here */ valData = buf.DataPtr(); valLength = buf.DataLen(); // workaround delete dirStr; dirStr = NULL; } else { /* from sm_x520sa.h */ AsnLen dec; dirStr = new DirectoryString; try { dirStr->BDecContent(buf, tag, elmtLen, dec, env); } catch(...) { errorLog0("getField_RDN: malformed DirectoryString (1)\n"); /* FIXME - throw? Discard the whole cert? What? */ rdn->GoNext(); currRdn->numberOfPairs--; continue; } AsnOcts *octs = NULL; switch(dirStr->choiceId) { case DirectoryString::printableStringCid: octs = dirStr->printableString; break; case DirectoryString::teletexStringCid: octs = dirStr->teletexString; break; case DirectoryString::universalStringCid: octs = dirStr->universalString; break; case DirectoryString::bmpStringCid: octs = dirStr->bmpString; break; case DirectoryString::utf8StringCid: octs = dirStr->utf8String; break; default: /* should never happen unless DirectoryString changes */ errorLog1("getField_RDN: Bad DirectoryString::choiceId (%d)\n", (int)dirStr->choiceId); CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } valData = *octs; valLength = octs->Len(); } /* normal DirectoryString */ /* OK, set up outgoing CSSM_X509_TYPE_VALUE_PAIR */ CssmOid &oid = CssmOid::overlay(currAttr->type); CL_snaccOidToCssm(att->type, oid, alloc); currAttr->valueType = tag >> 24; currAttr->value.Data = (uint8 *)alloc.malloc(valLength); currAttr->value.Length = valLength; memcpy(currAttr->value.Data, valData, valLength); rdn->GoNext(); // snacc format currAttr++; // CDSA format delete dirStr; } /* for eact attr in rdn */ rdns->GoNext(); // snacc format currRdn++; // CDSA format } /* for each rdn in rdns */ numFields = 1; return true; } /* common for issuer and subject */ static void freeField_RDN ( CssmOwnedData &fieldValue) { if(fieldValue.data() == NULL) { return; } if(fieldValue.length() != sizeof(CSSM_X509_NAME)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } CssmAllocator &alloc = fieldValue.allocator; CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue.data(); for(unsigned rdnDex=0; rdnDexnumberOfRDNs; rdnDex++) { CSSM_X509_RDN_PTR rdn = &x509Name->RelativeDistinguishedName[rdnDex]; for(unsigned atvDex=0; atvDexnumberOfPairs; atvDex++) { CSSM_X509_TYPE_VALUE_PAIR_PTR atv = &rdn->AttributeTypeAndValue[atvDex]; alloc.free(atv->type.Data); alloc.free(atv->value.Data); memset(atv, 0, sizeof(CSSM_X509_TYPE_VALUE_PAIR)); } alloc.free(rdn->AttributeTypeAndValue); memset(rdn, 0, sizeof(CSSM_X509_RDN)); } alloc.free(x509Name->RelativeDistinguishedName); memset(x509Name, 0, sizeof(CSSM_X509_NAME)); /* top-level x509Name pointer freed by freeCertFieldData() */ } /*** issuer ***/ static bool getField_Issuer ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { bool brtn; if(!tbsGetCheck(cert.certificateToSign->issuer, index)) { return false; } try { brtn = getField_RDN(*cert.certificateToSign->issuer, numFields, fieldValue); } catch (...) { freeField_RDN(fieldValue); throw; } return brtn; } static void setField_Issuer ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->issuer, fieldValue, sizeof(CSSM_X509_NAME), "IssuerName"); NameBuilder *issuer = new NameBuilder; cert.certificateToSign->issuer = issuer; const CSSM_X509_NAME *x509Name = (const CSSM_X509_NAME *)fieldValue.Data; issuer->addX509Name(x509Name); } /*** subject ***/ static bool getField_Subject ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->subject, index)) { return false; } bool brtn; try { brtn = getField_RDN(*cert.certificateToSign->subject, numFields, fieldValue); } catch (...) { freeField_RDN(fieldValue); throw; } return brtn; } static void setField_Subject ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->subject, fieldValue, sizeof(CSSM_X509_NAME), "SubjectName"); NameBuilder *subject = new NameBuilder; cert.certificateToSign->subject = subject; const CSSM_X509_NAME *x509Name = (const CSSM_X509_NAME *)fieldValue.Data; subject->addX509Name(x509Name); } /*** *** Issuer Name, Subject Name (normalized and encoded version) *** Format = CSSM_DATA containing the DER encoding of the normalized name *** class Name from sm_x501if ***/ /* first, the common code */ static bool getField_normRDN ( const Name &name, uint32 &numFields, // RETURNED (if successful, 0 or 1) CssmOwnedData &fieldValue) // RETURNED { /* * First step is to make a copy of the existing name. The easiest way to do * this is to encode and decode. */ CssmAllocator &alloc = fieldValue.allocator; CssmAutoData encodedName1(alloc); /* FIXME - should SC_encodeAsnObj() take a const AsnType & ? */ SC_encodeAsnObj(const_cast(name), encodedName1, MAX_RDN_SIZE); Name decodedName; SC_decodeAsnObj(encodedName1, decodedName); /* normalize */ CL_normalizeX509Name(decodedName, alloc); /* encode result */ SC_encodeAsnObj(decodedName, fieldValue, MAX_RDN_SIZE); numFields = 1; return true; } static bool getFieldSubjectNorm( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->subject, index)) { return false; } return getField_normRDN(*cert.certificateToSign->subject, numFields, fieldValue); } static bool getFieldIssuerNorm( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->issuer, index)) { return false; } return getField_normRDN(*cert.certificateToSign->issuer, numFields, fieldValue); } /*** *** TBS AlgId, Signature AlgId *** Format = CSSM_X509_ALGORITHM_IDENTIFIER *** *** common code: ***/ static void getField_AlgId ( const AlgorithmIdentifier *snaccAlgId, CssmOwnedData &fieldValue) // RETURNED { CssmAllocator &alloc = fieldValue.allocator; fieldValue.malloc(sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); CSSM_X509_ALGORITHM_IDENTIFIER *cssmAlgId = (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); CL_snaccAlgIdToCssm (*snaccAlgId, *cssmAlgId, alloc); } static void setField_AlgId ( AlgorithmIdentifier *snaccAlgId, const CssmData &fieldValue) { CSSM_X509_ALGORITHM_IDENTIFIER *cssmAlgId = (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); if(cssmAlgId->algorithm.Data == NULL) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } CL_cssmAlgIdToSnacc(*cssmAlgId, *snaccAlgId); } static void freeField_AlgId ( CssmOwnedData &fieldValue) { CSSM_X509_ALGORITHM_IDENTIFIER *cssmAlgId = (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); if(cssmAlgId == NULL) { return; } if(fieldValue.length() != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } CssmAllocator &alloc = fieldValue.allocator; alloc.free(cssmAlgId->algorithm.Data); alloc.free(cssmAlgId->parameters.Data); memset(cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); } /* TBS AlgId */ static bool getField_TbsAlgId ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { AlgorithmIdentifier *snaccAlgId = cert.certificateToSign->signature; if(!tbsGetCheck(snaccAlgId, index)) { return false; } getField_AlgId(snaccAlgId, fieldValue); numFields = 1; return true; } static void setField_TbsAlgId ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->signature, fieldValue, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER), "TBS_AlgId"); AlgorithmIdentifier *snaccAlgId = new AlgorithmIdentifier; cert.certificateToSign->signature = snaccAlgId; setField_AlgId(snaccAlgId, fieldValue); } /* Cert AlgId - read only */ static bool getField_CertAlgId ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { AlgorithmIdentifier *snaccAlgId = cert.algorithmIdentifier; if(!tbsGetCheck(snaccAlgId, index)) { return false; } getField_AlgId(snaccAlgId, fieldValue); numFields = 1; return true; } /*** *** Validity not before, not after *** Format: CSSM_X509_TIME ***/ /*** common code ***/ static void getField_Time ( const Time *snaccTime, CssmOwnedData &fieldValue) // RETURNED { CssmAllocator &alloc = fieldValue.allocator; fieldValue.malloc(sizeof(CSSM_X509_TIME)); CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)fieldValue.data(); memset(cssmTime, 0, sizeof(CSSM_X509_TIME)); char *timeStr = NULL; int timeStrLen = 0; switch(snaccTime->choiceId) { case Time::utcTimeCid: cssmTime->timeType = BER_TAG_UTC_TIME; timeStr = *snaccTime->utcTime; // an AsnOct timeStrLen = snaccTime->utcTime->Len(); break; case Time::generalizedTimeCid: timeStr = *snaccTime->generalizedTime; // an AsnOct timeStrLen = snaccTime->generalizedTime->Len(); cssmTime->timeType = BER_TAG_GENERALIZED_TIME; break; default: /* snacc error, should never happen */ cssmTime->timeType = BER_TAG_OCTET_STRING; timeStr = *snaccTime->generalizedTime; // an AsnOct timeStrLen = snaccTime->generalizedTime->Len(); break; } cssmTime->time.Data = reinterpret_cast(alloc.malloc(timeStrLen)); cssmTime->time.Length = timeStrLen; memcpy(cssmTime->time.Data, timeStr, timeStrLen); } static void setField_Time ( Time *snaccTime, const CssmData &fieldValue) { CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)fieldValue.data(); const char *tStr = reinterpret_cast(cssmTime->time.Data); size_t tLen = cssmTime->time.Length; switch(cssmTime->timeType) { case BER_TAG_GENERALIZED_TIME: snaccTime->choiceId = Time::generalizedTimeCid; snaccTime->generalizedTime = new GeneralizedTime(tStr, tLen); break; case BER_TAG_UTC_TIME: snaccTime->choiceId = Time::utcTimeCid; snaccTime->utcTime = new UTCTime(tStr, tLen); break; default: errorLog1("setField_Time: bad time tag (%d)\n", cssmTime->timeType); CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } } static void freeField_Time ( CssmOwnedData &fieldValue) { CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)fieldValue.data(); if(cssmTime == NULL) { return; } if(fieldValue.length() != sizeof(CSSM_X509_TIME)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } fieldValue.allocator.free(cssmTime->time.Data); memset(cssmTime, 0, sizeof(CSSM_X509_TIME)); } /*** not before ***/ static bool getField_NotBefore ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->validity, index)) { return false; } if(cert.certificateToSign->validity->notBefore == NULL) { return false; } getField_Time(cert.certificateToSign->validity->notBefore, fieldValue); numFields = 1; return true; } static void setField_NotBefore ( DecodedCert &cert, const CssmData &fieldValue) { /* anything could need mallocing except TBS */ if(cert.certificateToSign->validity == NULL) { cert.certificateToSign->validity = new Validity; } tbsSetCheck(cert.certificateToSign->validity->notBefore, fieldValue, sizeof(CSSM_X509_TIME), "NotBefore"); cert.certificateToSign->validity->notBefore = new Time; setField_Time(cert.certificateToSign->validity->notBefore, fieldValue); } /*** not after ***/ static bool getField_NotAfter ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->validity, index)) { return false; } if(cert.certificateToSign->validity->notAfter == NULL) { return false; } getField_Time(cert.certificateToSign->validity->notAfter, fieldValue); numFields = 1; return true; } static void setField_NotAfter ( DecodedCert &cert, const CssmData &fieldValue) { /* anything could need mallocing except TBS */ if(cert.certificateToSign->validity == NULL) { cert.certificateToSign->validity = new Validity; } tbsSetCheck(cert.certificateToSign->validity->notAfter, fieldValue, sizeof(CSSM_X509_TIME), "NotAfter"); cert.certificateToSign->validity->notAfter = new Time; setField_Time(cert.certificateToSign->validity->notAfter, fieldValue); } /*** *** Subject/issuer unique ID *** Format: Raw bytes. It's stored in the cert as an ASN bit string; the decoded *** bytes are present at this level (i.e., not tag and length in the bytes). *** NOTE: this is not quite accurate in that we only provide byte-aligned size, *** not bit-aligned. This field is rarely if ever used so I think it's O, but *** beware. ***/ static bool getField_SubjectUniqueId ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { UniqueIdentifier *id = cert.certificateToSign->subjectUniqueIdentifier; if(!tbsGetCheck(id, index)) { return false; } SC_asnBitsToCssmData(*id, fieldValue); numFields = 1; return true; } static void setField_SubjectUniqueId ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->subjectUniqueIdentifier, fieldValue, 0, "SubjectUniqueID"); cert.certificateToSign->subjectUniqueIdentifier = new UniqueIdentifier( reinterpret_cast(fieldValue.Data), fieldValue.Length * 8); } static bool getField_IssuerUniqueId ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { UniqueIdentifier *id = cert.certificateToSign->issuerUniqueIdentifier; if(!tbsGetCheck(id, index)) { return false; } SC_asnBitsToCssmData(*id, fieldValue); numFields = 1; return true; } static void setField_IssuerUniqueId ( DecodedCert &cert, const CssmData &fieldValue) { tbsSetCheck(cert.certificateToSign->issuerUniqueIdentifier, fieldValue, 0, "IssuerniqueID"); cert.certificateToSign->issuerUniqueIdentifier = new UniqueIdentifier( reinterpret_cast(fieldValue.Data), fieldValue.Length * 8); } /*** *** Public key info *** Format = CSSM_X509_SUBJECT_PUBLIC_KEY_INFO ***/ static bool getField_PublicKeyInfo ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->subjectPublicKeyInfo, index)) { return false; } SubjectPublicKeyInfo *snaccKeyInfo = cert.certificateToSign->subjectPublicKeyInfo; AlgorithmIdentifier *snaccAlgId = snaccKeyInfo->algorithm; if(snaccAlgId == NULL) { errorLog0("getField_PublicKeyInfo: cert has pubKeyInfo but no algorithm!\n"); return false; } CssmAllocator &alloc = fieldValue.allocator; fieldValue.malloc(sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO)); CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *cssmKeyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.data(); memset(cssmKeyInfo, 0, sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO)); CL_snaccAlgIdToCssm(*snaccAlgId, cssmKeyInfo->algorithm, alloc); /* * key info - the actual public key blob - is stored in the cert as a bit string; * snacc will give us the actual bits which are invariably yet another DER * encoding (e.g., PKCS1 for RSA public keys). */ size_t keyLen = (snaccKeyInfo->subjectPublicKey.BitLen() + 7) / 8; cssmKeyInfo->subjectPublicKey.Data = (uint8 *)alloc.malloc(keyLen); cssmKeyInfo->subjectPublicKey.Length = keyLen; memcpy(cssmKeyInfo->subjectPublicKey.Data, snaccKeyInfo->subjectPublicKey.BitOcts(), keyLen); numFields = 1; return true; } static void setField_PublicKeyInfo ( DecodedCert &cert, const CssmData &fieldValue) { /* This fails if setField_PublicKeyStruct has already been called */ tbsSetCheck(cert.certificateToSign->subjectPublicKeyInfo, fieldValue, sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO), "PubKeyInfo"); CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *cssmKeyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.Data; if((cssmKeyInfo->subjectPublicKey.Data == NULL) || (cssmKeyInfo->subjectPublicKey.Length == 0)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } SubjectPublicKeyInfo *snaccKeyInfo = new SubjectPublicKeyInfo; cert.certificateToSign->subjectPublicKeyInfo = snaccKeyInfo; snaccKeyInfo->algorithm = new AlgorithmIdentifier; /* common code to convert algorithm info (algID and parameters) */ const CSSM_X509_ALGORITHM_IDENTIFIER *cssmAlgId = &cssmKeyInfo->algorithm; CL_cssmAlgIdToSnacc(*cssmAlgId, *snaccKeyInfo->algorithm); /* actual public key blob - AsnBits */ snaccKeyInfo->subjectPublicKey.Set(reinterpret_cast (cssmKeyInfo->subjectPublicKey.Data), cssmKeyInfo->subjectPublicKey.Length * 8); } static void freeField_PublicKeyInfo ( CssmOwnedData &fieldValue) { CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *cssmKeyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)fieldValue.data(); if(cssmKeyInfo == NULL) { return; } CssmAllocator &alloc = fieldValue.allocator; CSSM_X509_ALGORITHM_IDENTIFIER *algId = &cssmKeyInfo->algorithm; alloc.free(algId->algorithm.Data); alloc.free(algId->parameters.Data); alloc.free(cssmKeyInfo->subjectPublicKey.Data); memset(cssmKeyInfo, 0, sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO));} /*** *** key info from CSSM_KEY *** Format = CSSM_KEY ***/ static bool getField_PublicKeyStruct ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(!tbsGetCheck(cert.certificateToSign->subjectPublicKeyInfo, index)) { return false; } CSSM_KEY_PTR cssmKey = cert.extractCSSMKey(fieldValue.allocator); fieldValue.set(reinterpret_cast(cssmKey), sizeof(CSSM_KEY)); numFields = 1; return true; } static void setField_PublicKeyStruct ( DecodedCert &cert, const CssmData &fieldValue) { /* This fails if setField_PublicKeyInfo has already been called */ tbsSetCheck(cert.certificateToSign->subjectPublicKeyInfo, fieldValue, sizeof(CSSM_KEY), "PubKey"); CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)fieldValue.data(); if((cssmKey->KeyData.Data == NULL) || (cssmKey->KeyData.Data == 0)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } SubjectPublicKeyInfo *snaccKeyInfo = new SubjectPublicKeyInfo; cert.certificateToSign->subjectPublicKeyInfo = snaccKeyInfo; snaccKeyInfo->algorithm = new AlgorithmIdentifier; CL_cssmAlgToSnaccOid(cssmKey->KeyHeader.AlgorithmId, snaccKeyInfo->algorithm->algorithm); /* NULL algorithm paramneters, always in this case */ CL_nullAlgParams(*snaccKeyInfo->algorithm); /* actual public key blob - AsnBits */ /*** *** Note: ideally we'd like to just convert an incoming ref key to a raw *** key here if necessary, but this occurs during CertCreateTemplate, *** when we don't have a CSP handle. This conversion is the caller's *** responsibility. ***/ if(cssmKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) { errorLog0("CL SetField: must specify RAW key blob\n"); CssmError::throwMe(CSSM_ERRCODE_INVALID_FIELD_POINTER); } snaccKeyInfo->subjectPublicKey.Set(reinterpret_cast (cssmKey->KeyData.Data), cssmKey->KeyData.Length * 8); } static void freeField_PublicKeyStruct ( CssmOwnedData &fieldValue) { CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)fieldValue.data(); CL_freeCSSMKey(cssmKey, fieldValue.allocator, false); } /*** *** Signature *** Format = raw bytes *** read-only ***/ static bool getField_Signature ( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if((index > 0) || // max of one sig (cert.signatureValue.BitLen() == 0)) { // no sig - must be TBS only return false; } SC_asnBitsToCssmData(cert.signatureValue, fieldValue); numFields = 1; return true; } /*** *** end of field-specific triplets ***/ /* setField for read-only OIDs (i.e., the ones in cert, not TBS) */ static void setField_ReadOnly ( DecodedCert &cert, const CssmData &fieldValue) { errorLog0("Attempt to set a read-only field\n"); CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); } /* * Table to map OID to {get,set,free}field */ typedef struct { const CSSM_OID *fieldId; getFieldFcn *getFcn; setFieldFcn *setFcn; freeFieldFcn *freeFcn; // OPTIONAL - NULL means just free the // top-level data } oidToFieldFuncs; static const oidToFieldFuncs fieldFuncTable[] = { { &CSSMOID_X509V1Version, &getField_Version, &setField_Version, NULL }, { &CSSMOID_X509V1SerialNumber, &getField_SerialNumber, &setField_SerialNumber, NULL }, { &CSSMOID_X509V1IssuerNameCStruct, &getField_Issuer, &setField_Issuer, &freeField_RDN }, { &CSSMOID_X509V1SubjectNameCStruct, &getField_Subject, &setField_Subject, &freeField_RDN }, { &CSSMOID_X509V1SignatureAlgorithmTBS, &getField_TbsAlgId, &setField_TbsAlgId, &freeField_AlgId }, { &CSSMOID_X509V1SignatureAlgorithm, &getField_CertAlgId, &setField_ReadOnly, &freeField_AlgId }, { &CSSMOID_X509V1ValidityNotBefore, &getField_NotBefore, &setField_NotBefore, &freeField_Time }, { &CSSMOID_X509V1ValidityNotAfter, &getField_NotAfter, &setField_NotAfter, &freeField_Time }, { &CSSMOID_X509V1CertificateIssuerUniqueId, &getField_IssuerUniqueId, &setField_IssuerUniqueId, NULL }, { &CSSMOID_X509V1CertificateSubjectUniqueId, &getField_SubjectUniqueId, &setField_SubjectUniqueId, NULL }, { &CSSMOID_X509V1SubjectPublicKeyCStruct, &getField_PublicKeyInfo, &setField_PublicKeyInfo, &freeField_PublicKeyInfo }, { &CSSMOID_CSSMKeyStruct, &getField_PublicKeyStruct, &setField_PublicKeyStruct, &freeField_PublicKeyStruct }, { &CSSMOID_X509V1Signature, &getField_Signature, &setField_ReadOnly, NULL }, { &CSSMOID_X509V1IssuerName, getFieldIssuerNorm, &setField_ReadOnly, NULL }, { &CSSMOID_X509V1SubjectName, getFieldSubjectNorm, &setField_ReadOnly, NULL }, /* * Extensions, implemented in CertExtensions.cpp * When adding new ones, also add to: * -- oidToSnaccObj() in CertExtensions.cpp * -- get/set/free functions in CertExtensions.{cpp,h} */ { &CSSMOID_KeyUsage, &getFieldKeyUsage, &setFieldKeyUsage, &freeFieldSimpleExtension }, { &CSSMOID_BasicConstraints, &getFieldBasicConstraints, &setFieldBasicConstraints, &freeFieldSimpleExtension }, { &CSSMOID_ExtendedKeyUsage, &getFieldExtKeyUsage, &setFieldExtKeyUsage, &freeFieldExtKeyUsage } , { &CSSMOID_SubjectKeyIdentifier, &getFieldSubjectKeyId, &setFieldSubjectKeyId, &freeFieldSubjectKeyId } , { &CSSMOID_AuthorityKeyIdentifier, &getFieldAuthorityKeyId, &setFieldAuthorityKeyId, &freeFieldAuthorityKeyId } , { &CSSMOID_SubjectAltName, &getFieldSubjAltName, &setFieldSubjAltName, &freeFieldSubjAltName } , { &CSSMOID_CertificatePolicies, &getFieldCertPolicies, &setFieldCertPolicies, &freeFieldCertPolicies } , { &CSSMOID_NetscapeCertType, &getFieldNetscapeCertType, &setFieldNetscapeCertType, &freeFieldSimpleExtension } , { &CSSMOID_X509V3CertificateExtensionCStruct, &getFieldUnknownExt, &setFieldUnknownExt, &freeFieldUnknownExt } }; #define NUM_KNOWN_FIELDS (sizeof(fieldFuncTable) / sizeof(oidToFieldFuncs)) #define NUM_STD_CERT_FIELDS 13 /* not including extensions */ /* map an OID to an oidToFieldFuncs */ static const oidToFieldFuncs *oidToFields( const CssmOid &fieldId) { const oidToFieldFuncs *funcPtr = fieldFuncTable; for(unsigned i=0; ifieldId)) { return funcPtr; } funcPtr++; } CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); } /*** *** Public functions ***/ /* * Obtain the index'th occurrence of field specified by fieldId in specified cert. * Format of the returned field depends on fieldId. * Returns total number of fieldId fields in the cert if index is 0. * FieldValue assumed to be empty on entry. * Returns true if specified field was found, else returns false. */ bool DecodedCert::getCertFieldData( const CssmOid &fieldId, // which field unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) const // RETURNED { CASSERT(certificateToSign != NULL); switch(mState) { case CS_Empty: case CS_Building: errorLog0("DecodedCert::getCertField: can't parse undecoded cert!\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); case CS_DecodedCert: case CS_DecodedTBS: break; } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); return fieldFuncs->getFcn(*this, index, numFields, fieldValue); } /* * Set the field specified by fieldId in the specified Cert. * Note no index - individual field routines either append (for extensions) * or if field already set ::throwMe(for all others) */ void DecodedCert::setCertField( const CssmOid &fieldId, // which field const CssmData &fieldValue) { CASSERT(certificateToSign != NULL); switch(mState) { case CS_Empty: // first time thru mState = CS_Building; break; case CS_Building: // subsequent passes break; case CS_DecodedCert: case CS_DecodedTBS: errorLog0("DecodedCert::setCertField: can't build on a decoded cert!\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); const CssmData &value = CssmData::overlay(fieldValue); fieldFuncs->setFcn(*this, value); } /* * Free the fieldId-specific data referred to by fieldValue->Data. */ void DecodedCert::freeCertFieldData( const CssmOid &fieldId, CssmOwnedData &fieldValue) { if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) { CssmError::throwMe(CSSM_ERRCODE_INVALID_FIELD_POINTER); } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); if(fieldFuncs->freeFcn != NULL) { /* optional - simple cases handled below */ fieldFuncs->freeFcn(fieldValue); } fieldValue.reset(); fieldValue.release(); } /* * Common means to get all fields from a decoded cert. Used in * CertGetAllTemplateFields and CertGetAllFields. */ void DecodedCert::getAllParsedCertFields( uint32 &NumberOfFields, // RETURNED CSSM_FIELD_PTR &CertFields) // RETURNED { /* this is the max - some might be missing */ uint32 maxFields = NUM_STD_CERT_FIELDS + mNumExtensions; CSSM_FIELD_PTR outFields = (CSSM_FIELD_PTR)alloc.malloc(maxFields * sizeof(CSSM_FIELD)); /* * We'll be copying oids and values for fields we find into * outFields; current number of valid fields found in numOutFields. */ memset(outFields, 0, maxFields * sizeof(CSSM_FIELD)); uint32 numOutFields = 0; CSSM_FIELD_PTR currOutField; uint32 currOidDex; const CSSM_OID *currOid; CssmAutoData aData(alloc); // for malloc/copy of outgoing data /* query for each OID we know about */ for(currOidDex=0; currOidDexfieldId; uint32 numFields; // for THIS oid /* * Return false if field not there, which is not an error here. * Actual exceptions are fatal. */ if(!fieldFuncs->getFcn(*this, 0, // index - looking for first one numFields, aData)) { continue; } /* got some data for this oid - copy it and oid to outgoing CertFields */ CASSERT(numOutFields < maxFields); currOutField = &outFields[numOutFields]; currOutField->FieldValue = aData.release(); aData.copy(*currOid); currOutField->FieldOid = aData.release(); numOutFields++; /* if more fields are available for this OID, snag them too */ for(uint32 fieldDex=1; fieldDexgetFcn(*this, fieldDex, numFields, // shouldn't change aData); if(!brtn) { errorLog0("getAllParsedCertFields: index screwup\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } CASSERT(numOutFields < maxFields); currOutField = &outFields[numOutFields]; currOutField->FieldValue = aData.release(); aData.copy(*currOid); currOutField->FieldOid = aData.release(); numOutFields++; } /* multiple fields for currOid */ } /* for each known OID */ NumberOfFields = numOutFields; CertFields = outFields; } void DecodedCert::describeFormat( CssmAllocator &alloc, uint32 &NumberOfFields, CSSM_OID_PTR &OidList) { /* malloc in app's space, do deep copy (including ->Data) */ CSSM_OID_PTR oidList = (CSSM_OID_PTR)alloc.malloc( NUM_KNOWN_FIELDS * sizeof(CSSM_OID)); memset(oidList, 0, NUM_KNOWN_FIELDS * sizeof(CSSM_OID)); for(unsigned i=0; i