--- /dev/null
+/*
+ * mdsdump.cpp - dump contents of system MDS databases
+ */
+
+ /**** FIXME this uses a private API not currently exported in any way from
+ **** Security project
+ ****/
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <Security/mds.h>
+#include <common.h>
+#include <Security/mds_schema.h>
+#include "MDSSchema.h"
+#include <strings.h>
+
+#define MAX_MDS_ATTRS 32
+
+static CSSM_MEMORY_FUNCS memFuncs = {
+ appMalloc,
+ appFree,
+ appRealloc,
+ appCalloc,
+ NULL
+ };
+
+static void showInfoTypes()
+{
+ printf(" o Object records\n");
+ printf(" C CSSM records\n");
+ printf(" p Plugin Common records\n");
+ printf(" c CSP records\n");
+ printf(" l CL records\n");
+ printf(" t TP records\n");
+ printf(" d DL records\n");
+ printf(" a All records from Object DB\n");
+ printf(" A All records from CDSA directory DB\n");
+}
+
+static void usage(char **argv)
+{
+ printf("Usage: %s info_type [options...]\n", argv[0]);
+ printf("info_type values:\n");
+ showInfoTypes();
+ printf(" h help\n");
+ printf("Options:\n");
+ printf(" i perform MDS_Install()\n");
+ printf(" v verbose\n");
+ printf(" k keep connected and go again\n");
+ exit(1);
+}
+
+#define NORM_KEY_LEN 20
+
+/* print a key name, padding out to NORM_KEY_LEN columns */
+static void printName(
+ const char *attrName)
+{
+ printf(" %s", attrName);
+ int len = strlen(attrName);
+ if(len > NORM_KEY_LEN) {
+ return;
+ }
+ int numSpaces = NORM_KEY_LEN - len;
+ for(int i=0; i<numSpaces; i++) {
+ putchar(' ');
+ }
+
+}
+
+#if 0
+/*
+ * Attempt to print a numeric value as a string, per a MDSNameValuePair table.
+ * Of course this can not deal with OR'd together values; the MDSNameValuePair
+ * mechanism does not indicate on a per-field basis which fields are OR-able.
+ * 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 Security::MDSNameValuePair *nameValues) // optional
+{
+ if(nameValues != NULL) {
+ while(nameValues->name != NULL) {
+ if(nameValues->value == val) {
+ printf("%s", nameValues->name);
+ return;
+ }
+ nameValues++;
+ }
+ }
+ /* Oh well */
+ printf("0x%x", val);
+}
+#endif
+
+/* print value string, surrounded by single quotes, then a newline */
+static void printValue(
+ const CSSM_DATA *attrValue)
+{
+ printf("'");
+ for(uint32 dex=0; dex<attrValue->Length; dex++) {
+ printf("%c", attrValue->Data[dex]);
+ }
+ printf("'\n");
+}
+
+/* Print one attribute value */
+static void dumpAttr(
+ CSSM_DB_ATTRIBUTE_FORMAT attrForm,
+ const CSSM_DATA *attrData)
+{
+ if((attrData == NULL) || (attrData->Data == NULL)) {
+ printf("<NULL DATA>\n");
+ return;
+ }
+ void *data = attrData->Data;
+ switch(attrForm) {
+ case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
+ printValue(attrData);
+ break;
+ case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: // not really supported in MDS
+ case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
+ {
+ unsigned val = *(unsigned *)data;
+ printf("0x%x\n", val);
+ break;
+ }
+ case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
+ {
+ printf("BLOB length %u : ", (unsigned)attrData->Length);
+ for(unsigned i=0; i<attrData->Length; i++) {
+ unsigned dat = attrData->Data[i];
+ printf("%02X ", dat);
+ }
+ printf("\n");
+ 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<numInts; i++) {
+ if(i > 0) {
+ printf(", ");
+ }
+ printf("0x%x", (unsigned)(*uip++));
+ }
+ printf("]\n");
+ break;
+ }
+ default:
+ printf("***UNKNOWN FORMAT (%u), Length %u\n",
+ (unsigned)attrForm, (unsigned)attrData->Length);
+ break;
+ }
+}
+
+/*
+ * Vanilla "dump one record" routine. Assumes format of all attribute labels
+ * as string. Uses a MDSNameValuePair ptr array in parallel to the attributes
+ * themselves to facilitate displaying numeric values as strings (e.g.
+ * "CSSM_ALGID_SHA1") where possible.
+ */
+static void dumpRecord(
+ const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
+{
+ unsigned dex;
+ for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
+ const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
+ if(attrData->Info.AttributeNameFormat != CSSM_DB_ATTRIBUTE_NAME_AS_STRING) {
+ printf("***BAD ATTR_NAME FORMAT (%u)\n",
+ (unsigned)attrData->Info.AttributeNameFormat);
+ continue;
+ }
+ const char *attrName = attrData->Info.Label.AttributeName;
+ printName(attrName);
+ printf(": ");
+ /* note currently in MDS NumberOfValues is always one or zero */
+ for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) {
+ dumpAttr(attrData->Info.AttributeFormat,
+ &attrData->Value[attrNum]);
+ }
+ if(attrData->NumberOfValues == 0) {
+ printf("<<no values present>>\n");
+ }
+ }
+}
+
+/* free attribute(s) allocated by MDS */
+static void freeAttrs(
+ CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR recordAttrs)
+{
+ unsigned i;
+
+ for(i=0; i<recordAttrs->NumberOfAttributes; 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; j<attrData->NumberOfValues; 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;
+ }
+ appFree(data->Data, NULL);
+ data->Data = NULL;
+ data->Length = 0;
+ }
+ appFree(attrData->Value, NULL);
+ attrData->Value = NULL;
+ }
+}
+
+/*
+ * Fetch and display all records of specified CSSM_DB_RECORDTYPE.
+ */
+static void fetchAllAttrs(
+ MDS_FUNCS *mdsFuncs,
+ MDS_DB_HANDLE dlDb,
+ CSSM_DB_RECORDTYPE recordType)
+{
+ CSSM_QUERY query;
+ CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
+ CSSM_RETURN crtn;
+ CSSM_HANDLE resultHand;
+ CSSM_DB_ATTRIBUTE_DATA attrs[MAX_MDS_ATTRS];
+ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
+ const RelationInfo *relInfo;
+
+ relInfo = MDSRecordTypeToRelation(recordType);
+ if(relInfo == NULL) {
+ printf("***\10UNKNOWN recordType %d\n", (int)recordType);
+ return;
+ }
+
+ /* build an attr array from schema so we get all known attrs */
+ memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * MAX_MDS_ATTRS);
+ unsigned attrDex;
+ for(attrDex=0; attrDex<relInfo->NumberOfAttributes; attrDex++) {
+ attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
+ }
+ recordAttrs.DataRecordType = recordType;
+ recordAttrs.NumberOfAttributes = relInfo->NumberOfAttributes;
+ recordAttrs.AttributeData = attrs;
+
+ /* just search by recordType, no predicates */
+ query.RecordType = recordType;
+ 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 = mdsFuncs->DataGetFirst(dlDb,
+ &query,
+ &resultHand,
+ &recordAttrs,
+ NULL, // No data
+ &record);
+ switch(crtn) {
+ case CSSM_OK:
+ break; // proceed
+ case CSSMERR_DL_ENDOFDATA:
+ printf("%s: no record found\n", relInfo->relationName);
+ return;
+ default:
+ printError("DataGetFirst", crtn);
+ return;
+ }
+ unsigned recNum = 0;
+ printf("%s:\n", relInfo->relationName);
+ printf(" record %d; numAttrs %d:\n",
+ recNum++, (int)recordAttrs.NumberOfAttributes);
+
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(dlDb, record);
+ freeAttrs(&recordAttrs);
+
+ /* now the rest of them */
+ /* hopefully we don't have to re-init the recordAttr array */
+ for(;;) {
+ crtn = mdsFuncs->DataGetNext(dlDb,
+ resultHand,
+ &recordAttrs,
+ NULL,
+ &record);
+ switch(crtn) {
+ case CSSM_OK:
+ printf(" record %d; numAttrs %d:\n",
+ recNum++, (int)recordAttrs.NumberOfAttributes);
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(dlDb, record);
+ freeAttrs(&recordAttrs);
+ break; // and go again
+ case CSSMERR_DL_ENDOFDATA:
+ /* normal termination */
+ break;
+ default:
+ printError("DataGetNext", crtn);
+ break;
+ }
+ if(crtn != CSSM_OK) {
+ break;
+ }
+ }
+}
+
+/*
+ * This is different - it's schema-independent. Fetch all records from specified
+ * DlDb which contain a ModuleID attribute.
+ */
+static void fetchAllRecords(
+ MDS_FUNCS *mdsFuncs,
+ MDS_DB_HANDLE dlDb)
+{
+ CSSM_QUERY query;
+ CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
+ CSSM_RETURN crtn;
+ CSSM_HANDLE resultHand;
+ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
+ CSSM_DB_ATTRIBUTE_DATA theAttr;
+ CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info;
+ CSSM_DATA attrValue = {0, NULL};
+
+ recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
+ recordAttrs.SemanticInformation = 0;
+ recordAttrs.NumberOfAttributes = 1;
+ recordAttrs.AttributeData = &theAttr;
+
+ attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+ attrInfo->Label.AttributeName = (char *)"ModuleID";
+ attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
+
+ theAttr.NumberOfValues = 1;
+ theAttr.Value = &attrValue;
+
+ /* 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 = mdsFuncs->DataGetFirst(dlDb,
+ &query,
+ &resultHand,
+ &recordAttrs,
+ NULL, // No data
+ &record);
+ switch(crtn) {
+ case CSSM_OK:
+ break; // proceed
+ case CSSMERR_DL_ENDOFDATA:
+ printf("no record found\n");
+ return;
+ default:
+ printError("DataGetFirst", crtn);
+ return;
+ }
+ unsigned recNum = 0;
+ printf("Records containing a ModuleID attribute:\n");
+ printf(" record %d:\n", recNum++);
+
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(dlDb, record);
+ freeAttrs(&recordAttrs);
+
+ /* now the rest of them */
+ /* hopefully we don't have to re-init the recordAttr array */
+ for(;;) {
+ crtn = mdsFuncs->DataGetNext(dlDb,
+ resultHand,
+ &recordAttrs,
+ NULL,
+ &record);
+ switch(crtn) {
+ case CSSM_OK:
+ printf(" record %d:\n", recNum++);
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(dlDb, record);
+ freeAttrs(&recordAttrs);
+ break; // and go again
+ case CSSMERR_DL_ENDOFDATA:
+ /* normal termination */
+ break;
+ default:
+ printError("DataGetNext", crtn);
+ break;
+ }
+ if(crtn != CSSM_OK) {
+ break;
+ }
+ }
+}
+
+static void doInstall(
+ MDS_HANDLE mdsHand)
+{
+ CFAbsoluteTime start, end;
+
+ start = CFAbsoluteTimeGetCurrent();
+ CSSM_RETURN crtn = MDS_Install(mdsHand);
+ end = CFAbsoluteTimeGetCurrent();
+ if(crtn) {
+ printError("MDS_Install", crtn);
+ }
+ else {
+ printf("MDS_Install took %gs\n", end - start);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ MDS_FUNCS mdsFuncs;
+ MDS_HANDLE mdsHand;
+ CSSM_RETURN crtn;
+ int arg;
+ char op;
+ char *dbName;
+ CSSM_DB_HANDLE dbHand = 0;
+ MDS_DB_HANDLE dlDb;
+ bool verbose = 0;
+ bool keepConnected = false;
+ bool install = false;
+
+ if(argc < 2) {
+ usage(argv);
+ }
+ op = argv[1][0];
+ if(op == 'h') {
+ usage(argv);
+ }
+ for(arg=2; arg<argc; arg++) {
+ switch(argv[arg][0]) {
+ case 'v':
+ verbose = true;
+ break;
+ case 'i':
+ install = true;
+ break;
+ case 'k':
+ keepConnected = true;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+ if(verbose) {
+ printf("..calling MDS_Initialize\n");
+ }
+ crtn = MDS_Initialize(NULL, // callerGuid
+ &memFuncs,
+ &mdsFuncs,
+ &mdsHand);
+ if(crtn) {
+ printError("MDS_Initialize", crtn);
+ exit(1);
+ }
+ if(install) {
+ doInstall(mdsHand);
+ }
+ do {
+ /* open one or the other DB */
+ switch(op) {
+ case 'o':
+ case 'a':
+ dbName = (char *)MDS_OBJECT_DIRECTORY_NAME;
+ break;
+ default:
+ dbName = (char *)MDS_CDSA_DIRECTORY_NAME;
+ break;
+ }
+ crtn = mdsFuncs.DbOpen(mdsHand,
+ dbName,
+ NULL, // DbLocation
+ CSSM_DB_ACCESS_READ,
+ NULL, // AccessCred - hopefully optional
+ NULL, // OpenParameters
+ &dbHand);
+ if(crtn) {
+ printError("DbOpen", crtn);
+ exit(1);
+ }
+ dlDb.DLHandle = mdsHand;
+ dlDb.DBHandle = dbHand;
+
+ /* go for it */
+ switch(op) {
+ case 'o':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_OBJECT_RECORDTYPE);
+ break;
+ case 'C':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSSM_RECORDTYPE);
+ break;
+ case 'p':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_COMMON_RECORDTYPE);
+ break;
+ case 'c':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE);
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE);
+ if(verbose) {
+ fetchAllAttrs(&mdsFuncs, dlDb,
+ MDS_CDSADIR_CSP_ENCAPSULATED_PRODUCT_RECORDTYPE);
+ fetchAllAttrs(&mdsFuncs, dlDb,
+ MDS_CDSADIR_CSP_SC_INFO_RECORDTYPE);
+ }
+ break;
+ case 'l':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CL_PRIMARY_RECORDTYPE);
+ break;
+ case 't':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_PRIMARY_RECORDTYPE);
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_OIDS_RECORDTYPE);
+ if(verbose) {
+ fetchAllAttrs(&mdsFuncs, dlDb,
+ MDS_CDSADIR_TP_ENCAPSULATED_PRODUCT_RECORDTYPE);
+ }
+ break;
+ case 'd':
+ fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_DL_PRIMARY_RECORDTYPE);
+ break;
+ case 'a':
+ case 'A':
+ fetchAllRecords(&mdsFuncs, dlDb);
+ break;
+ default:
+ usage(argv);
+ }
+
+ crtn = mdsFuncs.DbClose(dlDb);
+ if(crtn) {
+ printError("DbClose", crtn);
+ }
+ if(keepConnected) {
+ printf("\n");
+ showInfoTypes();
+ fpurge(stdin);
+ printf("Enter new info type: ");
+ op = getchar();
+ }
+ } while(keepConnected);
+ crtn = MDS_Terminate(mdsHand);
+ if(crtn) {
+ printError("MDS_Terminate", crtn);
+ }
+ return 0;
+}