X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_apple_x509_cl/lib/CLFieldsCommon.cpp diff --git a/libsecurity_apple_x509_cl/lib/CLFieldsCommon.cpp b/libsecurity_apple_x509_cl/lib/CLFieldsCommon.cpp new file mode 100644 index 00000000..f507a236 --- /dev/null +++ b/libsecurity_apple_x509_cl/lib/CLFieldsCommon.cpp @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2002-2010 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. + */ + + +/* + * CLFieldsCommon.h - get/set/free routines common to certs and CRLs + */ + +#include "CLFieldsCommon.h" +#include "clNameUtils.h" +#include "clNssUtils.h" +#include "AppleX509CLSession.h" +#include +#include +#include +#include +#include + +/* + * Table to map an OID to the info needed to decode the + * associated extension + */ +typedef struct { + const CSSM_OID &oid; + unsigned nssObjLen; + const SecAsn1Template *templ; +} NssExtenInfo; + +static const NssExtenInfo nssExtenInfo[] = { + { CSSMOID_KeyUsage, + sizeof(CSSM_DATA), + kSecAsn1KeyUsageTemplate }, + { CSSMOID_BasicConstraints, + sizeof(NSS_BasicConstraints), + kSecAsn1BasicConstraintsTemplate }, + { CSSMOID_ExtendedKeyUsage, + sizeof(NSS_ExtKeyUsage), + kSecAsn1ExtKeyUsageTemplate }, + { CSSMOID_SubjectKeyIdentifier, + sizeof(CSSM_DATA), + kSecAsn1SubjectKeyIdTemplate }, + { CSSMOID_AuthorityKeyIdentifier, + sizeof(NSS_AuthorityKeyId), + kSecAsn1AuthorityKeyIdTemplate }, + { CSSMOID_SubjectAltName, + sizeof(NSS_GeneralNames), + kSecAsn1GeneralNamesTemplate }, + { CSSMOID_IssuerAltName, + sizeof(NSS_GeneralNames), + kSecAsn1GeneralNamesTemplate }, + { CSSMOID_CertificatePolicies, + sizeof(NSS_CertPolicies), + kSecAsn1CertPoliciesTemplate }, + { CSSMOID_NetscapeCertType, + sizeof(CSSM_DATA), + kSecAsn1NetscapeCertTypeTemplate }, + { CSSMOID_CrlDistributionPoints, + sizeof(NSS_CRLDistributionPoints), + kSecAsn1CRLDistributionPointsTemplate }, + { CSSMOID_CertIssuer, + sizeof(NSS_GeneralNames), + kSecAsn1GeneralNamesTemplate }, + { CSSMOID_AuthorityInfoAccess, + sizeof(NSS_AuthorityInfoAccess), + kSecAsn1AuthorityInfoAccessTemplate }, + { CSSMOID_SubjectInfoAccess, + sizeof(NSS_AuthorityInfoAccess), + kSecAsn1AuthorityInfoAccessTemplate }, + /* CRL extensions */ + { CSSMOID_CrlNumber, + sizeof(CSSM_DATA), + kSecAsn1IntegerTemplate }, + { CSSMOID_IssuingDistributionPoint, + sizeof(NSS_IssuingDistributionPoint), + kSecAsn1IssuingDistributionPointTemplate }, + { CSSMOID_HoldInstructionCode, + sizeof(CSSM_OID), + kSecAsn1ObjectIDTemplate }, + { CSSMOID_CrlReason, + sizeof(CSSM_DATA), + kSecAsn1EnumeratedTemplate }, + { CSSMOID_DeltaCrlIndicator, + sizeof(CSSM_DATA), + kSecAsn1IntegerTemplate }, + { CSSMOID_InvalidityDate, + sizeof(CSSM_DATA), + kSecAsn1GeneralizedTimeTemplate }, + { CSSMOID_QC_Statements, + sizeof(NSS_QC_Statements), + kSecAsn1QC_StatementsTemplate }, + { CSSMOID_NameConstraints, + sizeof(NSS_NameConstraints), + kSecAsn1NameConstraintsTemplate }, + { CSSMOID_PolicyMappings, + sizeof(NSS_PolicyMappings), + kSecAsn1PolicyMappingsTemplate }, + { CSSMOID_PolicyConstraints, + sizeof(NSS_PolicyConstraints), + kSecAsn1PolicyConstraintsTemplate }, + { CSSMOID_InhibitAnyPolicy, + sizeof(CSSM_DATA), + kSecAsn1IntegerTemplate }, +}; + +#define NUM_NSS_EXTEN_INFOS (sizeof(nssExtenInfo) / sizeof(nssExtenInfo[0])) + +/* + * Returns true if we find the OID. + */ +bool clOidToNssInfo( + const CSSM_OID &oid, + unsigned &nssObjLen, // RETURNED + const SecAsn1Template *&templ) // RETURNED +{ + for(unsigned dex=0; dex(fieldValue.data()); + if(berEncoded) { + if(cssmExt->BERvalue.Data == NULL) { + CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); + } + } + else { + if(cssmExt->value.parsedValue == NULL) { + CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); + } + } + return cssmExt; +} + +/* + * Common free code for all extensions. Extension-specific code must + * free anything beyond cdsaExt->Value.parsedValue, then we free everything + * else (except the extension struct itself, which is freed by + * DecodedCert::freeCertFieldData()). + * The value union may contain a parsed value, or a CSSM_X509EXT_TAGandVALUE; + * wed ont' care, we just free it. + */ +void freeFieldExtenCommon( + CSSM_X509_EXTENSION_PTR exten, + Allocator &alloc) +{ + alloc.free(exten->extnId.Data); + alloc.free(exten->BERvalue.Data); // may be NULL + alloc.free(exten->value.parsedValue); // may be NULL +} + +/* + * One common free for extensions whose parsed value doesn't go any deeper + * than cssmExt->value.parsedValue. + */ +void freeFieldSimpleExtension ( + CssmOwnedData &fieldValue) +{ + CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, + false); + freeFieldExtenCommon(cssmExt, fieldValue.allocator); +} + + +/*** + *** Common code for get/set subject/issuer name (C struct version) + *** Format = CSSM_X509_NAME + *** class Name from sm_x501if + ***/ +bool getField_RDN_NSS ( + const NSS_Name &nssName, + CssmOwnedData &fieldValue) // RETURNED +{ + /* alloc top-level CSSM_X509_NAME */ + Allocator &alloc = fieldValue.allocator; + fieldValue.malloc(sizeof(CSSM_X509_NAME)); + CSSM_X509_NAME_PTR cssmName = (CSSM_X509_NAME_PTR)fieldValue.data(); + + CL_nssNameToCssm(nssName, *cssmName, alloc); + return true; +} + +void freeField_RDN ( + CssmOwnedData &fieldValue) +{ + if(fieldValue.data() == NULL) { + return; + } + if(fieldValue.length() != sizeof(CSSM_X509_NAME)) { + CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); + } + Allocator &alloc = fieldValue.allocator; + CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue.data(); + CL_freeX509Name(x509Name, alloc); + + /* top-level x509Name pointer freed by freeCertFieldData() */ +} + +/*** + *** Common code for Issuer Name, Subject Name (normalized and encoded + *** version) + *** Format = CSSM_DATA containing the DER encoding of the normalized name + ***/ +bool getField_normRDN_NSS ( + const CSSM_DATA &derName, + uint32 &numFields, // RETURNED (if successful, 0 or 1) + CssmOwnedData &fieldValue) // RETURNED +{ + if(derName.Data == NULL) { + /* This can happen during CertGetAllTemplateFields() because + * the normalized fields are only set up during cert/CRL decode */ + return false; + } + + /* + * First make a temp decoded copy which we'll be manipulating. + */ + SecNssCoder coder; + NSS_Name decodedName; + + memset(&decodedName, 0, sizeof(decodedName)); + PRErrorCode prtn = coder.decodeItem(derName, kSecAsn1NameTemplate, &decodedName); + if(prtn) { + /* + * Actually should never happen since this same bag of bits successfully + * decoded when the cert as a whole was decoded... + */ + clErrorLog("getField_normRDN decode error\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + + } + + /* normalize */ + CL_normalizeX509NameNSS(decodedName, coder); + + /* encode result */ + prtn = SecNssEncodeItemOdata(&decodedName, kSecAsn1NameTemplate, fieldValue); + if(prtn) { + clErrorLog("getField_normRDN encode error\n"); + CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); + } + numFields = 1; + return true; +} + +/*** + *** Common code for Time fields - Validity not before, Not After, + *** This Update, Next Update + *** Format: CSSM_X509_TIME + ***/ +bool getField_TimeNSS ( + const NSS_Time &nssTime, + unsigned index, // which occurrence (0 = first) + uint32 &numFields, // RETURNED + CssmOwnedData &fieldValue) // RETURNED +{ + if(!tbsGetCheck(nssTime.item.Data, index)) { + return false; + } + Allocator &alloc = fieldValue.allocator; + fieldValue.malloc(sizeof(CSSM_X509_TIME)); + CSSM_X509_TIME *cssmTime = + (CSSM_X509_TIME *)fieldValue.data(); + if(CL_nssTimeToCssm(nssTime, *cssmTime, alloc)) { + numFields = 1; + return true; + } + else { + return false; + } +} + +void setField_TimeNSS ( + const CssmData &fieldValue, + NSS_Time &nssTime, + SecNssCoder &coder) +{ + CSSM_X509_TIME *cssmTime = + (CSSM_X509_TIME *)fieldValue.data(); + CL_cssmTimeToNss(*cssmTime, nssTime, coder); +} + +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); + } + CL_freeCssmTime(cssmTime, fieldValue.allocator); +} + +/*** + *** TBS AlgId, Signature AlgId + *** Format = CSSM_X509_ALGORITHM_IDENTIFIER + ***/ +void getField_AlgIdNSS ( + const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId, + CssmOwnedData &fieldValue) // RETURNED +{ + Allocator &alloc = fieldValue.allocator; + fieldValue.malloc(sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); + CSSM_X509_ALGORITHM_IDENTIFIER *destAlgId = + (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); + CL_copyAlgId(srcAlgId, *destAlgId, alloc); +} + +void setField_AlgIdNSS ( + const CssmData &fieldValue, + CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId, + SecNssCoder &coder) +{ + CSSM_X509_ALGORITHM_IDENTIFIER *srcAlgId = + (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); + /* allocator for this coder */ + ArenaAllocator areanAlloc(coder); + CL_copyAlgId(*srcAlgId, dstAlgId, areanAlloc); +} + +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); + } + Allocator &alloc = fieldValue.allocator; + alloc.free(cssmAlgId->algorithm.Data); + alloc.free(cssmAlgId->parameters.Data); + memset(cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); +} + +/* + * Routines for common validity checking for certificateToSign fields. + * + * Call from setField*: verify field isn't already set, optionally validate + * input length + */ +void tbsSetCheck( + void *fieldToSet, + const CssmData &fieldValue, + uint32 expLength, + const char *op) +{ + if(fieldToSet != NULL) { + /* can't add another */ + clErrorLog("setField(%s): field already set", op); + CssmError::throwMe(CSSMERR_CL_INVALID_NUMBER_OF_FIELDS); + } + if((expLength != 0) && (fieldValue.length() != expLength)) { + clErrorLog("setField(%s): bad length : exp %d got %d", + 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. + */ +bool tbsGetCheck( + const void *requiredField, + uint32 reqIndex) +{ + if((requiredField == NULL) || (reqIndex != 0)) { + return false; + } + else { + return true; + } +} + +/*** + *** unknown extensions + *** CDSA format: raw bytes in a CSSM_DATA. This data is the BER-encoding of + *** some extension struct we don't know about. + *** NSS format CSSM_DATA + *** OID CSSMOID_X509V3CertificateExtensionCStruct + ***/ + +void setFieldUnknownExt( + DecodedItem &cert, + const CssmData &fieldValue) +{ + CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); + SecNssCoder &coder = cert.coder(); + CSSM_DATA *rawExtn = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA)); + coder.allocCopyItem(cssmExt->BERvalue, *rawExtn); + cert.addExtension(NULL, cssmExt->extnId, cssmExt->critical, + true, NULL /* no template */, rawExtn); +} + +bool getFieldUnknownExt( + DecodedItem &cert, + unsigned index, // which occurrence (0 = first) + uint32 &numFields, // RETURNED + CssmOwnedData &fieldValue) +{ + uint8 noOidDataLikeThis[2] = {1, 2}; // a dummy argument + CSSM_OID noOidLikeThis = {2, noOidDataLikeThis}; + const DecodedExten *decodedExt = + cert.DecodedItem::findDecodedExt(noOidLikeThis, + true, index, numFields); + if(decodedExt == NULL) { + return false; + } + getFieldExtenCommon(NULL, *decodedExt, fieldValue); + return true; +} + +void freeFieldUnknownExt ( + CssmOwnedData &fieldValue) +{ + CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); + Allocator &alloc = fieldValue.allocator; + freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue +} + +/* setField for read-only OIDs (i.e., the ones in cert/CRL, not TBS) */ +void setField_ReadOnly ( + DecodedItem &item, + const CssmData &fieldValue) +{ + clErrorLog("Attempt to set a read-only field"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); +} + +bool getField_Unimplemented ( + DecodedItem &item, + unsigned index, // which occurrence (0 = first) + uint32 &numFields, // RETURNED + CssmOwnedData &fieldValue) // RETURNED +{ + clErrorLog("Attempt to get an unimplemented field"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); +} + + +