/* * Copyright (c) 2002-2003 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. */ /* File: cuDbUtils.cpp Description: CDSA DB access utilities Author: dmitch */ #include "cuCdsaUtils.h" #include "cuTimeStr.h" #include "cuDbUtils.h" #include "cuPrintCert.h" #include #include #include #include /* private SecInferLabelFromX509Name() */ #include /* for cssmPerror() */ #include #include #include #include #include /* private API */ /* * Add a certificate to an open DLDB. */ CSSM_RETURN cuAddCertToDb( CSSM_DL_DB_HANDLE dlDbHand, const CSSM_DATA *cert, CSSM_CERT_TYPE certType, CSSM_CERT_ENCODING certEncoding, const char *printName, // C string const CSSM_DATA *publicKeyHash) { CSSM_DB_ATTRIBUTE_DATA attrs[6]; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0]; CSSM_DATA certTypeData; CSSM_DATA certEncData; CSSM_DATA printNameData; CSSM_RETURN crtn; CSSM_DB_UNIQUE_RECORD_PTR recordPtr; /* issuer and serial number required, fake 'em */ CSSM_DATA issuer = {6, (uint8 *)"issuer"}; CSSM_DATA serial = {6, (uint8 *)"serial"}; /* we spec six attributes, skipping alias */ certTypeData.Data = (uint8 *)&certType; certTypeData.Length = sizeof(CSSM_CERT_TYPE); certEncData.Data = (uint8 *)&certEncoding; certEncData.Length = sizeof(CSSM_CERT_ENCODING); printNameData.Data = (uint8 *)printName; printNameData.Length = strlen(printName) + 1; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "CertType"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &certTypeData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "CertEncoding"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &certEncData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "PrintName"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &printNameData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "PublicKeyHash"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = (CSSM_DATA_PTR)publicKeyHash; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "Issuer"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &issuer; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "SerialNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &serial; recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE; recordAttrs.SemanticInformation = 0; recordAttrs.NumberOfAttributes = 6; recordAttrs.AttributeData = attrs; crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CERTIFICATE, &recordAttrs, cert, &recordPtr); if(crtn) { cuPrintError("CSSM_DL_DataInsert", crtn); } else { CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr); } return crtn; } static CSSM_RETURN cuAddCrlSchema( CSSM_DL_DB_HANDLE dlDbHand); static void cuInferCrlLabel( const CSSM_X509_NAME *x509Name, CSSM_DATA *label) // not mallocd; contents are from the x509Name { /* use private API for common "infer label" logic */ const CSSM_DATA *printValue = SecInferLabelFromX509Name(x509Name); if(printValue == NULL) { /* punt! */ label->Data = (uint8 *)"X509 CRL"; label->Length = 8; } else { *label = *printValue; } } /* * Search extensions for specified OID, assumed to have underlying * value type of uint32; returns the value and true if found. */ static bool cuSearchNumericExtension( const CSSM_X509_EXTENSIONS *extens, const CSSM_OID *oid, uint32 *val) { for(uint32 dex=0; dexnumberOfExtensions; dex++) { const CSSM_X509_EXTENSION *exten = &extens->extensions[dex]; if(!cuCompareOid(&exten->extnId, oid)) { continue; } if(exten->format != CSSM_X509_DATAFORMAT_PAIR) { printf("***Malformed extension\n"); continue; } *val = *((uint32 *)exten->value.parsedValue); return true; } return false; } /* * Add a CRL to an existing DL/DB. */ #define MAX_CRL_ATTRS 9 CSSM_RETURN cuAddCrlToDb( CSSM_DL_DB_HANDLE dlDbHand, CSSM_CL_HANDLE clHand, const CSSM_DATA *crl, const CSSM_DATA *URI) // optional { CSSM_DB_ATTRIBUTE_DATA attrs[MAX_CRL_ATTRS]; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0]; CSSM_DATA crlTypeData; CSSM_DATA crlEncData; CSSM_DATA printNameData; CSSM_RETURN crtn; CSSM_DB_UNIQUE_RECORD_PTR recordPtr; CSSM_DATA_PTR issuer; // mallocd by CL CSSM_DATA_PTR crlValue; // ditto uint32 numFields; CSSM_HANDLE result; CSSM_CRL_ENCODING crlEnc = CSSM_CRL_ENCODING_DER; const CSSM_X509_SIGNED_CRL *signedCrl; const CSSM_X509_TBS_CERTLIST *tbsCrl; CSSM_CRL_TYPE crlType; CSSM_DATA thisUpdateData = {0, NULL}; CSSM_DATA nextUpdateData = {0, NULL}; char *thisUpdate, *nextUpdate; unsigned timeLen; uint32 crlNumber; uint32 deltaCrlNumber; CSSM_DATA crlNumberData; CSSM_DATA deltaCrlNumberData; bool crlNumberPresent = false; bool deltaCrlPresent = false; CSSM_DATA attrUri; /* get normalized issuer name as Issuer attr */ crtn = CSSM_CL_CrlGetFirstFieldValue(clHand, crl, &CSSMOID_X509V1IssuerName, &result, &numFields, &issuer); if(crtn) { cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn); return crtn; } CSSM_CL_CrlAbortQuery(clHand, result); /* get parsed CRL from the CL */ crtn = CSSM_CL_CrlGetFirstFieldValue(clHand, crl, &CSSMOID_X509V2CRLSignedCrlCStruct, &result, &numFields, &crlValue); if(crtn) { cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn); return crtn; } CSSM_CL_CrlAbortQuery(clHand, result); if(crlValue == NULL) { printf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n"); return CSSMERR_CL_INVALID_CRL_POINTER; } if((crlValue->Data == NULL) || (crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) { printf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n"); return CSSMERR_CL_INVALID_CRL_POINTER; } signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data; tbsCrl = &signedCrl->tbsCertList; /* CrlType inferred from version */ if(tbsCrl->version.Length == 0) { /* should never happen... */ crlType = CSSM_CRL_TYPE_X_509v1; } else { uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1]; switch(vers) { case 0: crlType = CSSM_CRL_TYPE_X_509v1; break; case 1: crlType = CSSM_CRL_TYPE_X_509v2; break; default: printf("***Unknown version in CRL (%u)\n", vers); crlType = CSSM_CRL_TYPE_X_509v1; break; } } crlTypeData.Data = (uint8 *)&crlType; crlTypeData.Length = sizeof(CSSM_CRL_TYPE); /* encoding more-or-less assumed here */ crlEncData.Data = (uint8 *)&crlEnc; crlEncData.Length = sizeof(CSSM_CRL_ENCODING); /* printName inferred from issuer */ cuInferCrlLabel(&tbsCrl->issuer, &printNameData); /* cook up CSSM_TIMESTRING versions of this/next update */ thisUpdate = cuX509TimeToCssmTimestring(&tbsCrl->thisUpdate, &timeLen); if(thisUpdate == NULL) { printf("***Badly formatted thisUpdate\n"); } else { thisUpdateData.Data = (uint8 *)thisUpdate; thisUpdateData.Length = timeLen; } if(tbsCrl->nextUpdate.time.Data != NULL) { nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen); if(nextUpdate == NULL) { printf("***Badly formatted nextUpdate\n"); } else { nextUpdateData.Data = (uint8 *)nextUpdate; nextUpdateData.Length = timeLen; } } else { /* * NextUpdate not present; fake it by using "virtual end of time" */ CSSM_X509_TIME tempTime = { 0, // timeType, not used { strlen(CSSM_APPLE_CRL_END_OF_TIME), (uint8 *)CSSM_APPLE_CRL_END_OF_TIME} }; nextUpdate = cuX509TimeToCssmTimestring(&tempTime, &timeLen); nextUpdateData.Data = (uint8 *)nextUpdate; nextUpdateData.Length = CSSM_TIME_STRLEN; } /* optional CrlNumber and DeltaCrlNumber */ if(cuSearchNumericExtension(&tbsCrl->extensions, &CSSMOID_CrlNumber, &crlNumber)) { crlNumberData.Data = (uint8 *)&crlNumber; crlNumberData.Length = sizeof(uint32); crlNumberPresent = true; } if(cuSearchNumericExtension(&tbsCrl->extensions, &CSSMOID_DeltaCrlIndicator, &deltaCrlNumber)) { deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber; deltaCrlNumberData.Length = sizeof(uint32); deltaCrlPresent = true; } attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "CrlType"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlTypeData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "CrlEncoding"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlEncData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "PrintName"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &printNameData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "Issuer"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = issuer; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "ThisUpdate"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &thisUpdateData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "NextUpdate"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &nextUpdateData; attr++; /* now the optional attributes */ if(crlNumberPresent) { attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "CrlNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlNumberData; attr++; } if(deltaCrlPresent) { attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "DeltaCrlNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &deltaCrlNumberData; attr++; } if(URI) { /* ensure URI string does not contain NULL */ attrUri = *URI; if((attrUri.Length != 0) && (attrUri.Data[attrUri.Length - 1] == 0)) { attrUri.Length--; } attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = "URI"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &attrUri; attr++; } recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL; recordAttrs.SemanticInformation = 0; recordAttrs.NumberOfAttributes = attr - attrs; recordAttrs.AttributeData = attrs; crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CRL, &recordAttrs, crl, &recordPtr); if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) { /* gross hack of inserting this "new" schema that Keychain didn't specify */ crtn = cuAddCrlSchema(dlDbHand); if(crtn == CSSM_OK) { /* Retry with a fully capable DLDB */ crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CRL, &recordAttrs, crl, &recordPtr); } } if(crtn) { cuPrintError("CSSM_DL_DataInsert", crtn); } else { CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr); } /* free all the stuff we allocated to get here */ CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer); CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue); free(thisUpdate); free(nextUpdate); return crtn; } /* * Update an existing DLDB to be CRL-capable. */ static CSSM_RETURN cuAddCrlSchema( CSSM_DL_DB_HANDLE dlDbHand) { return CSSM_DL_CreateRelation(dlDbHand, CSSM_DL_DB_RECORD_X509_CRL, "CSSM_DL_DB_RECORD_X509_CRL", Security::KeychainCore::Schema::X509CrlSchemaAttributeCount, Security::KeychainCore::Schema::X509CrlSchemaAttributeList, Security::KeychainCore::Schema::X509CrlSchemaIndexCount, Security::KeychainCore::Schema::X509CrlSchemaIndexList); } /* * Search DB for all records of type CRL or cert, calling appropriate * parse/print routine for each record. */ CSSM_RETURN cuDumpCrlsCerts( CSSM_DL_DB_HANDLE dlDbHand, CSSM_CL_HANDLE clHand, CSSM_BOOL isCert, unsigned &numItems, // returned CSSM_BOOL verbose) { CSSM_QUERY query; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_HANDLE resultHand; CSSM_RETURN crtn; CSSM_DATA certCrl; const char *itemStr; numItems = 0; itemStr = isCert ? "Certificate" : "CRL"; /* just search by recordType, no predicates, no attributes */ if(isCert) { query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE; } else { query.RecordType = CSSM_DL_DB_RECORD_X509_CRL; } query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 0; query.SelectionPredicate = NULL; query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? certCrl.Data = NULL; certCrl.Length = 0; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, NULL, // no attrs &certCrl, &record); switch(crtn) { case CSSM_OK: break; // proceed case CSSMERR_DL_ENDOFDATA: /* no data, otherwise OK */ return CSSM_OK; case CSSMERR_DL_INVALID_RECORDTYPE: /* invalid record type just means "this hasn't been set up * for certs yet". */ return crtn; default: cuPrintError("DataGetFirst", crtn); return crtn; } /* got one; print it */ printf("%s %u:\n", itemStr, numItems); if(isCert) { printCert(certCrl.Data, certCrl.Length, verbose); } else { printCrl(certCrl.Data, certCrl.Length, verbose); } CSSM_DL_FreeUniqueRecord(dlDbHand, record); APP_FREE(certCrl.Data); certCrl.Data = NULL; certCrl.Length = 0; numItems++; /* get the rest */ for(;;) { crtn = CSSM_DL_DataGetNext(dlDbHand, resultHand, NULL, &certCrl, &record); switch(crtn) { case CSSM_OK: printf("%s %u:\n", itemStr, numItems); if(isCert) { printCert(certCrl.Data, certCrl.Length, verbose); } else { printCrl(certCrl.Data, certCrl.Length, verbose); } CSSM_DL_FreeUniqueRecord(dlDbHand, record); APP_FREE(certCrl.Data); certCrl.Data = NULL; certCrl.Length = 0; numItems++; break; // and go again case CSSMERR_DL_ENDOFDATA: /* normal termination */ return CSSM_OK; default: cuPrintError("DataGetNext", crtn); return crtn; } } /* NOT REACHED */ }