X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/cspxutils/dbTool/dbTool.cpp diff --git a/SecurityTests/cspxutils/dbTool/dbTool.cpp b/SecurityTests/cspxutils/dbTool/dbTool.cpp new file mode 100644 index 00000000..937d88ea --- /dev/null +++ b/SecurityTests/cspxutils/dbTool/dbTool.cpp @@ -0,0 +1,747 @@ +/* Copyright (c) 2002-2006 Apple Computer, Inc. + * + * dbTool.cpp - DL/DB tool. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "cspwrap.h" +#include "common.h" +#include "dbAttrs.h" +#include "dbCert.h" +#include "cspdlTesting.h" + + +static void usage(char **argv) +{ + printf("usage: %s dbFileName command [options]\n", argv[0]); + printf("Commands:\n"); + printf(" r Dump Schema Relations\n"); + printf(" k Dump all keys\n"); + printf(" c Dump certs\n"); + printf(" a Dump all records\n"); + printf(" d Delete records (interactively)\n"); + printf(" D Delete records (noninteractively, requires really arg)\n"); + printf(" i Import bad cert and its (good) private key\n"); + printf("Options:\n"); + printf(" v verbose\n"); + printf(" q quiet\n"); + printf(" R really! (for D command)\n"); + printf(" d dump data\n"); + printf(" c=certFile\n"); + printf(" k=keyFile\n"); + exit(1); +} + + +static unsigned indentVal = 0; +static void indentIncr() +{ + indentVal += 3; +} + +static void indentDecr() +{ + if(indentVal) { + indentVal -= 3; + } +} + +static void doIndent() +{ + unsigned i; + for(i=0; iAttributeNameFormat) { + case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: + { + char *attrName = attrInfo->Label.AttributeName; + printf("%s", attrName); + int len = strlen(attrName); + if(len > NORM_KEY_LEN) { + return; + } + int numSpaces = NORM_KEY_LEN - len; + for(int i=0; iLabel.AttributeID); + for(unsigned i=0; i<4; i++) { + putchar(*cp++); + } + printf(" "); + break; + } + default: + printf("Unknown attribute name format (%u)\n", + (unsigned)attrInfo->AttributeNameFormat); + break; + } +} + +/* + * Attempt to print a numeric value as a string, per a NameValuePair table. + * If the value is in fact a collection of legal values (per the nameValues + * array), the value will just be printed in hex. + */ +static void printValueAsString( + unsigned val, + const NameValuePair *nameValues) +{ + if(nameValues != NULL) { + while(nameValues->name != NULL) { + if(nameValues->value == val) { + printf("%s", nameValues->name); + return; + } + nameValues++; + } + } + /* Oh well */ + printf("0x%x", val); +} + +static void safePrint( + uint8 *cp, + uint32 len) +{ + for(unsigned i=0; iData; + for(unsigned i=0; iLength; i++) { + if(*cp == 0) { + if(i != (dp->Length - 1)) { + /* data contains NULL character before end */ + printable = false; + } + /* else end of string */ + break; + } + if(!isprint(*cp)) { + printable = false; + break; + } + cp++; + } + return printable; +} + +#define MAX_BLOB_TO_PRINT 12 +static void printBlob( + const CSSM_DATA *data) +{ + unsigned toPrint = data->Length; + if(toPrint > MAX_BLOB_TO_PRINT) { + toPrint = MAX_BLOB_TO_PRINT; + } + for(unsigned i=0; iData[i]; + printf("%02X ", dat); + } + if(toPrint < data->Length) { + printf("..."); + } +} + +static void printAttrData( + const CSSM_DB_ATTRIBUTE_INFO *attrInfo, + const CSSM_DATA *attrData, + const NameValuePair *nameValues) // optional +{ + void *data = attrData->Data; + + switch(attrInfo->AttributeFormat) { + + case CSSM_DB_ATTRIBUTE_FORMAT_STRING: + putchar('\''); + safePrint(attrData->Data, attrData->Length); + putchar('\''); + break; + case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: + case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: + { + unsigned val = *(unsigned *)data; + printValueAsString(val, nameValues); + break; + } + case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: + { + printf("BLOB length %u : ", (unsigned)attrData->Length); + /* see if it happens to be a printable string */ + if(isPrintable(attrData)) { + putchar('\''); + safePrint(attrData->Data, attrData->Length); + putchar('\''); + } + else { + printBlob(attrData); + } + break; + } + case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: + { + printf("multi_int["); + uint32 numInts = attrData->Length / sizeof(uint32); + uint32 *uip = (uint32 *)data; + for(unsigned i=0; i 0) { + printf(", "); + } + printValueAsString(*uip++, nameValues); + } + printf("]"); + break; + } + case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: + putchar('\''); + safePrint(attrData->Data, attrData->Length); + putchar('\''); + break; + + default: + printf("***UNKNOWN FORMAT (%u), Length %u", + (unsigned)attrInfo->AttributeFormat, (unsigned)attrData->Length); + break; + } +} + +/* free attribute(s) allocated by DL */ +static void freeAttrs( + CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs) +{ + unsigned i; + + for(i=0; iNumberOfAttributes; i++) { + CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i]; + if(attrData == NULL) { + /* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */ + printf("***freeAttrs screwup: NULL attrData\n"); + return; + } + unsigned j; + for(j=0; jNumberOfValues; j++) { + CSSM_DATA_PTR data = &attrData->Value[j]; + if(data == NULL) { + /* fault of MDS, who said there was a value here */ + printf("***freeAttrs screwup: NULL data\n"); + return; + } + CSSM_FREE(data->Data); + data->Data = NULL; + data->Length = 0; + } + CSSM_FREE(attrData->Value); + attrData->Value = NULL; + } +} + +static void dumpDataBlob( + const CSSM_DATA *datap) +{ + doIndent(); + printf("Record data length %lu ", datap->Length); + if(datap->Length != 0) { + printf(" : "); + printBlob(datap); + } + printf("\n"); +} + +static void dumpRecordAttrs( + const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs, + const NameValuePair **nameValues, // parallel to recordAttrs + const CSSM_DATA *recordData = NULL) // optional data +{ + unsigned valNum; + unsigned dex; + + for(dex=0; dexNumberOfAttributes; dex++) { + const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex]; + doIndent(); + printName(&attrData->Info); + printf(": "); + if(attrData->NumberOfValues == 0) { + printf("<>\n"); + continue; + } + for(valNum=0; valNumNumberOfValues; valNum++) { + printAttrData(&attrData->Info, &attrData->Value[valNum], nameValues[dex]); + if(valNum < (attrData->NumberOfValues - 1)) { + printf(", "); + } + } + printf("\n"); + } + if(recordData) { + dumpDataBlob(recordData); + } +} + +static void dumpRelation( + CSSM_DL_DB_HANDLE dlDbHand, + const RelationInfo *relInfo, + CSSM_BOOL dumpData) +{ + CSSM_QUERY query; + CSSM_DB_UNIQUE_RECORD_PTR record = NULL; + CSSM_RETURN crtn; + CSSM_HANDLE resultHand; + CSSM_DB_ATTRIBUTE_DATA *attrs; + CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; + unsigned attrDex; + unsigned recNum = 0; + uint32 numAttrs = relInfo->NumberOfAttributes; + CSSM_DATA data = {0, NULL}; + CSSM_DATA_PTR datap = NULL; + + if(dumpData) { + datap = &data; + } + + /* build an attr array from known schema */ + attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC( + sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs); + memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs); + for(attrDex=0; attrDexAttributeInfo[attrDex]; + } + recordAttrs.DataRecordType = relInfo->DataRecordType; + recordAttrs.NumberOfAttributes = numAttrs; + recordAttrs.AttributeData = attrs; + + /* just search by recordType, no predicates */ + query.RecordType = relInfo->DataRecordType; + 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? + + crtn = CSSM_DL_DataGetFirst(dlDbHand, + &query, + &resultHand, + &recordAttrs, + datap, + &record); + switch(crtn) { + case CSSM_OK: + break; // proceed + case CSSMERR_DL_ENDOFDATA: + printf("%s: no record found\n", relInfo->relationName); + CSSM_FREE(attrs); + return; + default: + printError("DataGetFirst", crtn); + CSSM_FREE(attrs); + return; + } + printf("%s:\n", relInfo->relationName); + printf(" record %d; numAttrs %d:\n", + recNum++, (int)recordAttrs.NumberOfAttributes); + indentIncr(); + + dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap); + CSSM_DL_FreeUniqueRecord(dlDbHand, record); + freeAttrs(&recordAttrs); + if(datap) { + CSSM_FREE(datap->Data); + } + + /* now the rest of them */ + /* hopefully we don't have to re-init the recordAttr array */ + for(;;) { + crtn = CSSM_DL_DataGetNext(dlDbHand, + resultHand, + &recordAttrs, + datap, + &record); + switch(crtn) { + case CSSM_OK: + printf(" record %d; numAttrs %d:\n", + recNum++, (int)recordAttrs.NumberOfAttributes); + dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap); + CSSM_DL_FreeUniqueRecord(dlDbHand, record); + freeAttrs(&recordAttrs); + if(datap) { + CSSM_FREE(datap->Data); + } + break; // and go again + case CSSMERR_DL_ENDOFDATA: + /* normal termination */ + break; + default: + printError("DataGetNext", crtn); + break; + } + if(crtn != CSSM_OK) { + break; + } + } + indentDecr(); + CSSM_FREE(attrs); +} + +/* + * Given a record type and a CSSM_DB_UNIQUE_RECORD, fetch and parse all the + * attributes we can. + */ +static void fetchParseRecord( + CSSM_DL_DB_HANDLE dlDbHand, + CSSM_DB_RECORD_ATTRIBUTE_DATA *inRecordAttrs, + CSSM_DB_UNIQUE_RECORD_PTR record, + const CSSM_DATA_PTR datap, + CSSM_BOOL dumpData) +{ + const RelationInfo *relInfo = NULL; + + /* infer RelationInfo from recordType */ + switch(inRecordAttrs->DataRecordType) { + case CSSM_DL_DB_RECORD_PUBLIC_KEY: + case CSSM_DL_DB_RECORD_PRIVATE_KEY: + case CSSM_DL_DB_RECORD_SYMMETRIC_KEY: + relInfo = &allKeysRelation; + break; + case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: + case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: + case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: + relInfo = &genericKcRelation; + break; + case CSSM_DL_DB_RECORD_CERT: + relInfo = &certRecordRelation; + break; + case CSSM_DL_DB_RECORD_X509_CERTIFICATE: + relInfo = &x509CertRecordRelation; + break; + case CSSM_DL_DB_RECORD_X509_CRL: + relInfo = &x509CrlRecordRelation; + break; + case CSSM_DL_DB_RECORD_USER_TRUST: + relInfo = &userTrustRelation; + break; + case CSSM_DL_DB_RECORD_UNLOCK_REFERRAL: + relInfo = &referralRecordRelation; + break; + case CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE: + relInfo = &extendedAttrRelation; + break; + case DBBlobRelationID: + relInfo = NULL; + doIndent(); + printf("--- No attributes ---\n"); + if(dumpData) { + dumpDataBlob(datap); + } + return; + default: + doIndent(); + printf("<>\n"); + if(dumpData) { + doIndent(); + printf("Record blob (length %ld): ", datap->Length); + printBlob(datap); + printf("\n"); + } + return; + } + + CSSM_DB_ATTRIBUTE_DATA *attrs = NULL; + CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; + unsigned attrDex; + uint32 numAttrs = relInfo->NumberOfAttributes; + CSSM_RETURN crtn; + CSSM_DATA recordData = {0, NULL}; + CSSM_DATA_PTR recordDataP = dumpData ? &recordData : NULL; + + /* build an attr array from known schema */ + attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC( + sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs); + memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs); + for(attrDex=0; attrDexAttributeInfo[attrDex]; + } + + /* from inRecordAttrs, not the relInfo, which could be a typeless template */ + recordAttrs.DataRecordType = relInfo->DataRecordType; + recordAttrs.NumberOfAttributes = numAttrs; + recordAttrs.AttributeData = attrs; + + crtn = CSSM_DL_DataGetFromUniqueRecordId(dlDbHand, + record, + &recordAttrs, + recordDataP); + if(crtn) { + printError("CSSM_DL_DataGetFromUniqueRecordId", crtn); + goto abort; + } + dumpRecordAttrs(&recordAttrs, relInfo->nameValues, recordDataP); + freeAttrs(&recordAttrs); + if(recordData.Data) { + CSSM_FREE(recordData.Data); + } +abort: + if(attrs) { + CSSM_FREE(attrs); + } + return; +} + +static void deleteRecord( + CSSM_DL_DB_HANDLE dlDbHand, + CSSM_DB_UNIQUE_RECORD_PTR record, + CSSM_BOOL interact) +{ + if(interact) { + fpurge(stdin); + printf("\nDelete this record [y/anything] ? "); + char resp = getchar(); + if(resp != 'y') { + return; + } + } + CSSM_RETURN crtn; + crtn = CSSM_DL_DataDelete(dlDbHand, record); + if(crtn) { + printError("CSSM_DL_DataDelete", crtn); + } + else if(interact) { + printf("...record deleted\n\n"); + } +} + +/* + * In this case we search for CSSM_DL_DB_RECORD_ANY. The current schema results + * in no single attribute which all interesting records have in common, so we + * can't grab any attributes at GetFirst/GetNext time. Instead we have + * to deal with the returned record per its record type. + */ +static void dumpAllRecords( + CSSM_DL_DB_HANDLE dlDbHand, + CSSM_BOOL deleteAll, + CSSM_BOOL interact, + CSSM_BOOL dumpData) +{ + CSSM_QUERY query; + CSSM_DB_UNIQUE_RECORD_PTR record = NULL; + CSSM_RETURN crtn; + CSSM_HANDLE resultHand; + CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; + CSSM_DATA data = {0, NULL}; + CSSM_DATA_PTR datap = NULL; + + if(dumpData) { + datap = &data; + } + + recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY; + recordAttrs.NumberOfAttributes = 0; + recordAttrs.AttributeData = NULL; + + /* just search by recordType, no predicates */ + query.RecordType = CSSM_DL_DB_RECORD_ANY; + 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? + + crtn = CSSM_DL_DataGetFirst(dlDbHand, + &query, + &resultHand, + &recordAttrs, + datap, + &record); + switch(crtn) { + case CSSM_OK: + break; // proceed + case CSSMERR_DL_ENDOFDATA: + printf("CSSM_DL_DB_RECORD_ANY: no record found\n"); + return; + default: + printError("DataGetFirst", crtn); + return; + } + + /* could be anything; check it out */ + if(interact) { + doIndent(); + printValueAsString(recordAttrs.DataRecordType, recordTypeNames); + printf("\n"); + indentIncr(); + fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData); + indentDecr(); + } + if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) { + /* NEVER delete a DBBlob */ + deleteRecord(dlDbHand, record, interact); + } + CSSM_DL_FreeUniqueRecord(dlDbHand, record); + + /* now the rest of them */ + /* hopefully we don't have to re-init the recordAttr array */ + for(;;) { + crtn = CSSM_DL_DataGetNext(dlDbHand, + resultHand, + &recordAttrs, + datap, + &record); + switch(crtn) { + case CSSM_OK: + if(interact) { + doIndent(); + printValueAsString(recordAttrs.DataRecordType, recordTypeNames); + printf("\n"); + indentIncr(); + fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData); + indentDecr(); + } + if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) { + /* NEVER delete a DBBlob */ + deleteRecord(dlDbHand, record, interact); + } + CSSM_DL_FreeUniqueRecord(dlDbHand, record); + break; // and go again + case CSSMERR_DL_ENDOFDATA: + /* normal termination */ + break; + default: + printError("DataGetNext", crtn); + break; + } + if(crtn != CSSM_OK) { + break; + } + } +} + +int main( + int argc, + char **argv) +{ + int arg; + char *argp; + char *dbFileName; + char cmd; + CSSM_DL_DB_HANDLE dlDbHand; + CSSM_BOOL verbose = CSSM_FALSE; + CSSM_BOOL quiet = CSSM_FALSE; + char *certFile = NULL; + char *keyFile = NULL; + CSSM_BOOL interact = CSSM_TRUE; + CSSM_BOOL dumpData = CSSM_FALSE; + + /* should be cmd line opts */ + CSSM_ALGORITHMS keyAlg = CSSM_ALGID_RSA; + CSSM_BOOL pemFormat = CSSM_FALSE; + CSSM_KEYBLOB_FORMAT keyFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE; + CSSM_RETURN crtn = CSSM_OK; + + if(argc < 3) { + usage(argv); + } + dbFileName = argv[1]; + cmd = argv[2][0]; + + for(arg=3; arg