-/*
- * Copyright (c) 2002-2003,2011-2012,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.
- */
-
-/*
- File: cuDbUtils.cpp
-
- Description: CDSA DB access utilities
-
- Author: dmitch
-*/
-
-#include "cuCdsaUtils.h"
-#include "cuTimeStr.h"
-#include "cuDbUtils.h"
-#include "cuPrintCert.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <Security/SecCertificate.h>
-#include <Security/SecCertificatePriv.h> /* private SecInferLabelFromX509Name() */
-#include <Security/cssmapple.h> /* for cssmPerror() */
-#include <Security/oidscert.h>
-#include <Security/oidscrl.h>
-#include <Security/oidsattr.h>
-#include <strings.h>
-#include <security_cdsa_utilities/Schema.h> /* private API */
-
-#ifndef NDEBUG
-#define dprintf(args...) printf(args)
-#else
-#define dprintf(args...)
-#endif
-
-/*
- * 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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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; dex<extens->numberOfExtensions; dex++) {
- const CSSM_X509_EXTENSION *exten = &extens->extensions[dex];
- if(!cuCompareOid(&exten->extnId, oid)) {
- continue;
- }
- if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
- dprintf("***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 = NULL; // mallocd by CL
- CSSM_DATA_PTR crlValue = NULL; // 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 = NULL;
- char *nextUpdate = NULL;
- 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);
- goto errOut;
- }
- CSSM_CL_CrlAbortQuery(clHand, result);
- if(crlValue == NULL) {
- dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
- crtn = CSSMERR_CL_INVALID_CRL_POINTER;
- goto errOut;
- }
- if((crlValue->Data == NULL) ||
- (crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) {
- dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
- crtn = CSSMERR_CL_INVALID_CRL_POINTER;
- goto errOut;
- }
- 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:
- dprintf("***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) {
- dprintf("***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) {
- dprintf("***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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (char*) "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 = (uint32)(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 == CSSM_OK) {
- CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
- }
-
-errOut:
- /* free all the stuff we allocated to get here */
- if(issuer) {
- CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer);
- }
- if(crlValue) {
- CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue);
- }
- if(thisUpdate) {
- free(thisUpdate);
- }
- if(nextUpdate) {
- 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 */
- dprintf("%s %u:\n", itemStr, numItems);
- if(isCert) {
- printCert(certCrl.Data, (unsigned)certCrl.Length, verbose);
- }
- else {
- printCrl(certCrl.Data, (unsigned)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:
- dprintf("%s %u:\n", itemStr, numItems);
- if(isCert) {
- printCert(certCrl.Data, (unsigned)certCrl.Length, verbose);
- }
- else {
- printCrl(certCrl.Data, (unsigned)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 */
-}
-