--- /dev/null
+/*
+ * mdsLookup.cpp - demonstrate some MDS lookups
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <Security/mds.h>
+#include <Security/mds_schema.h>
+#include <Security/oidsalg.h> // for TP OIDs
+#include "common.h"
+#include <strings.h>
+
+/* the memory functions themselves are in utilLib/common.c. */
+static CSSM_MEMORY_FUNCS memFuncs = {
+ appMalloc,
+ appFree,
+ appRealloc,
+ appCalloc,
+ NULL
+ };
+
+static void usage(char **argv)
+{
+ printf("Usage: %s [options]\n", argv[0]);
+ printf("Options:\n");
+ printf(" k keep connected and go again\n");
+ exit(1);
+}
+
+#define NORM_KEY_LEN 10
+
+/* 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(' ');
+ }
+
+}
+
+/* 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.
+ */
+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(": ");
+ 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;
+ }
+}
+
+/*
+ * Core MDS lookup routine. Used in two situations. It's called by main() to perform
+ * a lookup in the CDSA Directory Database based one one key/value pair; this
+ * call fetches one attribute from the associated record - the GUID ("ModuleID"
+ * in MDS lingo). Then the function calls itself to do a lookup in the Object DB,
+ * based on that GUID, in order to fetch the path of the module associated with
+ * that GUID. The first call (from main()) corresponds to an application's
+ * typical use of MDS. The recursive call, which does a lookup in the Object
+ * DB, corresponds to CSSM's typical use of MDS, which is to map a GUID to a
+ * bundle path.
+ *
+ * The ModuleID and Path of all modules satisfying the initial search criteria
+ * are displayed on stdout.
+ *
+ * Caller specifies one search attribute, by name, value,Êand value format.
+ * Whether this is the first or second (recursive) call is indicated by the
+ * cdsaLookup argument. That determines both the DB to search and the attribute
+ * to fetch (ModuleID or Path).
+ */
+static void doLookup(
+ MDS_FUNCS *mdsFuncs,
+
+ /* Two DBs and a flag indicating which one to use */
+ MDS_DB_HANDLE objDlDb,
+ MDS_DB_HANDLE cdsaDlDb,
+ bool cdsaLookup, // true - use cdsaDlDb; false - objDlDb
+
+ /* Record type, a.k.a. Relation, e.g. MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE */
+ CSSM_DB_RECORDTYPE recordType,
+
+ /* key, value, valForm, and valOp are the thing we search on */
+ /* Note CSSM_DB_ATTRIBUTE_NAME_FORMAT - the format of the attribute name -
+ * is always CSSM_DB_ATTRIBUTE_NAME_AS_STRING for MDS. */
+ const char *key, // e.g. "AlgType"
+ const void *valPtr,
+ unsigned valLen,
+ CSSM_DB_ATTRIBUTE_FORMAT valForm, // CSSM_DB_ATTRIBUTE_FORMAT_STRING, etc.
+ CSSM_DB_OPERATOR valOp, // normally CSSM_DB_EQUAL
+
+ /* for display only */
+ const char *srchStr)
+{
+ CSSM_QUERY query;
+ CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
+ CSSM_HANDLE resultHand;
+ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
+ CSSM_SELECTION_PREDICATE predicate;
+ CSSM_DATA predData;
+ CSSM_DB_ATTRIBUTE_DATA outAttr;
+ CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo;
+ CSSM_RETURN crtn;
+ MDS_DB_HANDLE dlDb;
+ const char *attrName;
+
+ if(cdsaLookup) {
+ /* first call, fetching guid from the CDSA Directory DB */
+ dlDb = cdsaDlDb;
+ attrName = "ModuleID";
+ }
+ else {
+ /* recursive call, fetching path from Object DB */
+ dlDb = objDlDb;
+ attrName = "Path";
+ }
+
+ /* We want one attributes back, name and format specified by caller */
+ recordAttrs.DataRecordType = recordType;
+ recordAttrs.SemanticInformation = 0;
+ recordAttrs.NumberOfAttributes = 1;
+ recordAttrs.AttributeData = &outAttr;
+
+ memset(&outAttr, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA));
+ attrInfo = &outAttr.Info;
+ attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+ attrInfo->Label.AttributeName = (char *)attrName;
+ attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
+
+ /* one predicate - the caller's key and CSSM_DB_OPERATOR */
+ predicate.DbOperator = valOp;
+ predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+ predicate.Attribute.Info.Label.AttributeName = (char *)key;
+ predicate.Attribute.Info.AttributeFormat = valForm;
+ predData.Data = (uint8 *)valPtr;
+ predData.Length = valLen;
+ predicate.Attribute.Value = &predData;
+ predicate.Attribute.NumberOfValues = 1;
+
+ query.RecordType = recordType;
+ query.Conjunctive = CSSM_DB_NONE;
+ query.NumSelectionPredicates = 1;
+ query.SelectionPredicate = &predicate;
+ 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", srchStr);
+ return;
+ default:
+ printError("DataGetFirst", crtn);
+ return;
+ }
+ /* dump this record, one attribute */
+ if(srchStr) {
+ /* not done on recursive call */
+ printf("%s found:\n", srchStr);
+ }
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(dlDb, record);
+
+ if(srchStr != NULL) {
+ /*
+ * Now do a lookup in Object DB of this guid, looking for path.
+ * Apps normally don't do this; this is what CSSM does when given
+ * the GUID of a module.
+ */
+ if(outAttr.Value == NULL) {
+ printf("***Screwup: DataGetFirst worked, but no outAttr\n");
+ return;
+ }
+ doLookup(mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ false, // use objDlDb
+ MDS_OBJECT_RECORDTYPE,
+ "ModuleID", // key
+ outAttr.Value->Data, // valPtr, ModuleID, as string
+ outAttr.Value->Length, // valLen
+ CSSM_DB_ATTRIBUTE_FORMAT_STRING,
+ CSSM_DB_EQUAL,
+ NULL); // srchStr
+ }
+ freeAttrs(&recordAttrs);
+
+ /* now the rest of them */
+ for(;;) {
+ crtn = mdsFuncs->DataGetNext(dlDb,
+ resultHand,
+ &recordAttrs,
+ NULL,
+ &record);
+ switch(crtn) {
+ case CSSM_OK:
+ dumpRecord(&recordAttrs);
+ mdsFuncs->FreeUniqueRecord(cdsaDlDb, record);
+ if(srchStr != NULL) {
+ if(outAttr.Value == NULL) {
+ printf("***Screwup: DataGetNext worked, but no outAttr\n");
+ return;
+ }
+ doLookup(mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ false, // use objDlDb
+ MDS_OBJECT_RECORDTYPE,
+ "ModuleID", // key
+ outAttr.Value->Data, // valPtr, ModuleID, as string
+ outAttr.Value->Length, // valLen
+ CSSM_DB_ATTRIBUTE_FORMAT_STRING,
+ CSSM_DB_EQUAL,
+ NULL); // srchStr
+ }
+ freeAttrs(&recordAttrs);
+ 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)
+{
+ MDS_FUNCS mdsFuncs;
+ MDS_HANDLE mdsHand;
+ CSSM_RETURN crtn;
+ int arg;
+ CSSM_DB_HANDLE dbHand = 0;
+ MDS_DB_HANDLE objDlDb;
+ MDS_DB_HANDLE cdsaDlDb;
+ bool keepConnected = false;
+ uint32 val;
+
+ for(arg=2; arg<argc; arg++) {
+ switch(argv[arg][0]) {
+ case 'k':
+ keepConnected = true;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+ crtn = MDS_Initialize(NULL, // callerGuid
+ &memFuncs,
+ &mdsFuncs,
+ &mdsHand);
+ if(crtn) {
+ printError("MDS_Initialize", crtn);
+ exit(1);
+ }
+
+ do {
+ /*
+ * Open both MDS DBs - apps normally only have to open
+ * MDS_CDSA_DIRECTORY_NAME.
+ */
+ crtn = mdsFuncs.DbOpen(mdsHand,
+ MDS_OBJECT_DIRECTORY_NAME,
+ NULL, // DbLocation
+ CSSM_DB_ACCESS_READ,
+ NULL, // AccessCred - hopefully optional
+ NULL, // OpenParameters
+ &dbHand);
+ if(crtn) {
+ printError("DbOpen(MDS_OBJECT_DIRECTORY_NAME)", crtn);
+ exit(1);
+ }
+ objDlDb.DLHandle = mdsHand;
+ objDlDb.DBHandle = dbHand;
+
+ crtn = mdsFuncs.DbOpen(mdsHand,
+ MDS_CDSA_DIRECTORY_NAME,
+ NULL, // DbLocation
+ CSSM_DB_ACCESS_READ,
+ NULL, // AccessCred - hopefully optional
+ NULL, // OpenParameters
+ &dbHand);
+ if(crtn) {
+ printError("DbOpen(MDS_CDSA_DIRECTORY_NAME)", crtn);
+ exit(1);
+ }
+ cdsaDlDb.DLHandle = mdsHand;
+ cdsaDlDb.DBHandle = dbHand;
+
+ /*
+ * Do some typical lookups.
+ */
+
+ /* a CSP which can do SHA1 digest */
+ val = CSSM_ALGID_SHA1;
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
+ "AlgType",
+ &val,
+ sizeof(uint32),
+ CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
+ CSSM_DB_EQUAL,
+ "CSP for SHA1 digest");
+
+ /* a TP which can do iSign verification */
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_TP_OIDS_RECORDTYPE,
+ "OID",
+ CSSMOID_APPLE_ISIGN.Data,
+ CSSMOID_APPLE_ISIGN.Length,
+ CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
+ CSSM_DB_EQUAL,
+ "TP for CSSMOID_APPLE_ISIGN policy");
+
+ /* an X509-savvy CL */
+ /* Very weird data form - two fields in one 32-bit word */
+ val = (CSSM_CERT_X_509v3 << 16) | CSSM_CERT_ENCODING_DER;
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_CL_PRIMARY_RECORDTYPE,
+ "CertTypeFormat",
+ &val,
+ sizeof(uint32),
+ CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
+ CSSM_DB_EQUAL,
+ "X509 CL");
+
+ /* A DL which can do CSSM_DB_AND */
+ val = CSSM_DB_AND;
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_DL_PRIMARY_RECORDTYPE,
+ "ConjunctiveOps",
+ &val,
+ sizeof(uint32),
+ CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32,
+ /* This is a multi-uint32, meaning we want to search for a
+ * ConjunctiveOps which contains the specified value */
+ CSSM_DB_CONTAINS,
+ "DL with ConjunctiveOp CSSM_DB_AND");
+
+ /* a CSP which can do CSSM_ALGID_IDEA, should fail */
+ val = CSSM_ALGID_IDEA;
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
+ "AlgType",
+ &val,
+ sizeof(uint32),
+ CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
+ CSSM_DB_EQUAL,
+ "CSP for CSSM_ALGID_BLOWFISH, expect failure");
+
+ /* a TP which can obtain a .mac signing certificate */
+ doLookup(&mdsFuncs,
+ objDlDb,
+ cdsaDlDb,
+ true,
+ MDS_CDSADIR_TP_OIDS_RECORDTYPE,
+ "OID",
+ CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Data,
+ CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Length,
+ CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
+ CSSM_DB_EQUAL,
+ "TP for .mac signing certificate policy");
+
+ crtn = mdsFuncs.DbClose(objDlDb);
+ if(crtn) {
+ printError("DbClose(objDlDb)", crtn);
+ }
+ crtn = mdsFuncs.DbClose(cdsaDlDb);
+ if(crtn) {
+ printError("DbClose(cdsaDlDb)", crtn);
+ }
+ if(keepConnected) {
+ printf("\n");
+ fpurge(stdin);
+ printf("Enter CR to go again: ");
+ getchar();
+ }
+ } while(keepConnected);
+ crtn = MDS_Terminate(mdsHand);
+ if(crtn) {
+ printError("MDS_Terminate", crtn);
+ }
+ return 0;
+}