1/* 2 * mdsLookup.cpp - demonstrate some MDS lookups 3 */ 4 5#include <stdlib.h> 6#include <stdio.h> 7#include <Security/mds.h> 8#include <Security/mds_schema.h> 9#include <Security/oidsalg.h> // for TP OIDs 10#include "common.h" 11#include <strings.h> 12 13/* the memory functions themselves are in utilLib/common.c. */ 14static CSSM_MEMORY_FUNCS memFuncs = { 15 appMalloc, 16 appFree, 17 appRealloc, 18 appCalloc, 19 NULL 20 }; 21 22static void usage(char **argv) 23{ 24 printf("Usage: %s [options]\n", argv[0]); 25 printf("Options:\n"); 26 printf(" k keep connected and go again\n"); 27 exit(1); 28} 29 30#define NORM_KEY_LEN 10 31 32/* print a key name, padding out to NORM_KEY_LEN columns */ 33static void printName( 34 const char *attrName) 35{ 36 printf(" %s", attrName); 37 int len = strlen(attrName); 38 if(len > NORM_KEY_LEN) { 39 return; 40 } 41 int numSpaces = NORM_KEY_LEN - len; 42 for(int i=0; i<numSpaces; i++) { 43 putchar(' '); 44 } 45 46} 47 48/* print value string, surrounded by single quotes, then a newline */ 49static void printValue( 50 const CSSM_DATA *attrValue) 51{ 52 printf("'"); 53 for(uint32 dex=0; dex<attrValue->Length; dex++) { 54 printf("%c", attrValue->Data[dex]); 55 } 56 printf("'\n"); 57} 58 59/* Print one attribute value */ 60static void dumpAttr( 61 CSSM_DB_ATTRIBUTE_FORMAT attrForm, 62 const CSSM_DATA *attrData) 63{ 64 if((attrData == NULL) || (attrData->Data == NULL)) { 65 printf("<NULL DATA>\n"); 66 return; 67 } 68 void *data = attrData->Data; 69 switch(attrForm) { 70 case CSSM_DB_ATTRIBUTE_FORMAT_STRING: 71 printValue(attrData); 72 break; 73 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: // not really supported in MDS 74 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: 75 { 76 unsigned val = *(unsigned *)data; 77 printf("0x%x\n", val); 78 break; 79 } 80 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: 81 { 82 printf("BLOB length %u : ", (unsigned)attrData->Length); 83 for(unsigned i=0; i<attrData->Length; i++) { 84 unsigned dat = attrData->Data[i]; 85 printf("%02X ", dat); 86 } 87 printf("\n"); 88 break; 89 } 90 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: 91 { 92 printf("multi_int["); 93 uint32 numInts = attrData->Length / sizeof(uint32); 94 uint32 *uip = (uint32 *)data; 95 for(unsigned i=0; i<numInts; i++) { 96 if(i > 0) { 97 printf(", "); 98 } 99 printf("0x%x", (unsigned)*uip++); 100 } 101 printf("]\n"); 102 break; 103 } 104 default: 105 printf("***UNKNOWN FORMAT (%u), Length %u\n", 106 (unsigned)attrForm, (unsigned)attrData->Length); 107 break; 108 } 109} 110 111/* 112 * Vanilla "dump one record" routine. Assumes format of all attribute labels 113 * as string. 114 */ 115static void dumpRecord( 116 const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs) 117{ 118 unsigned dex; 119 for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) { 120 const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex]; 121 if(attrData->Info.AttributeNameFormat != 122 CSSM_DB_ATTRIBUTE_NAME_AS_STRING) { 123 printf("***BAD ATTR_NAME FORMAT (%u)\n", 124 (unsigned)attrData->Info.AttributeNameFormat); 125 continue; 126 } 127 const char *attrName = attrData->Info.Label.AttributeName; 128 printName(attrName); 129 printf(": "); 130 for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) { 131 dumpAttr(attrData->Info.AttributeFormat, 132 &attrData->Value[attrNum]); 133 } 134 if(attrData->NumberOfValues == 0) { 135 printf("<<no values present>>\n"); 136 } 137 } 138} 139 140/* free attribute(s) allocated by MDS */ 141static void freeAttrs( 142 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR recordAttrs) 143{ 144 unsigned i; 145 146 for(i=0; i<recordAttrs->NumberOfAttributes; i++) { 147 CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i]; 148 if(attrData == NULL) { 149 /* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */ 150 printf("***freeAttrs screwup: NULL attrData\n"); 151 return; 152 } 153 unsigned j; 154 for(j=0; j<attrData->NumberOfValues; j++) { 155 CSSM_DATA_PTR data = &attrData->Value[j]; 156 if(data == NULL) { 157 /* fault of MDS, who said there was a value here */ 158 printf("***freeAttrs screwup: NULL data\n"); 159 return; 160 } 161 appFree(data->Data, NULL); 162 data->Data = NULL; 163 data->Length = 0; 164 } 165 appFree(attrData->Value, NULL); 166 attrData->Value = NULL; 167 } 168} 169 170/* 171 * Core MDS lookup routine. Used in two situations. It's called by main() to perform 172 * a lookup in the CDSA Directory Database based one one key/value pair; this 173 * call fetches one attribute from the associated record - the GUID ("ModuleID" 174 * in MDS lingo). Then the function calls itself to do a lookup in the Object DB, 175 * based on that GUID, in order to fetch the path of the module associated with 176 * that GUID. The first call (from main()) corresponds to an application's 177 * typical use of MDS. The recursive call, which does a lookup in the Object 178 * DB, corresponds to CSSM's typical use of MDS, which is to map a GUID to a 179 * bundle path. 180 * 181 * The ModuleID and Path of all modules satisfying the initial search criteria 182 * are displayed on stdout. 183 * 184 * Caller specifies one search attribute, by name, value,�and value format. 185 * Whether this is the first or second (recursive) call is indicated by the 186 * cdsaLookup argument. That determines both the DB to search and the attribute 187 * to fetch (ModuleID or Path). 188 */ 189static void doLookup( 190 MDS_FUNCS *mdsFuncs, 191 192 /* Two DBs and a flag indicating which one to use */ 193 MDS_DB_HANDLE objDlDb, 194 MDS_DB_HANDLE cdsaDlDb, 195 bool cdsaLookup, // true - use cdsaDlDb; false - objDlDb 196 197 /* Record type, a.k.a. Relation, e.g. MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE */ 198 CSSM_DB_RECORDTYPE recordType, 199 200 /* key, value, valForm, and valOp are the thing we search on */ 201 /* Note CSSM_DB_ATTRIBUTE_NAME_FORMAT - the format of the attribute name - 202 * is always CSSM_DB_ATTRIBUTE_NAME_AS_STRING for MDS. */ 203 const char *key, // e.g. "AlgType" 204 const void *valPtr, 205 unsigned valLen, 206 CSSM_DB_ATTRIBUTE_FORMAT valForm, // CSSM_DB_ATTRIBUTE_FORMAT_STRING, etc. 207 CSSM_DB_OPERATOR valOp, // normally CSSM_DB_EQUAL 208 209 /* for display only */ 210 const char *srchStr) 211{ 212 CSSM_QUERY query; 213 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 214 CSSM_HANDLE resultHand; 215 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 216 CSSM_SELECTION_PREDICATE predicate; 217 CSSM_DATA predData; 218 CSSM_DB_ATTRIBUTE_DATA outAttr; 219 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo; 220 CSSM_RETURN crtn; 221 MDS_DB_HANDLE dlDb; 222 const char *attrName; 223 224 if(cdsaLookup) { 225 /* first call, fetching guid from the CDSA Directory DB */ 226 dlDb = cdsaDlDb; 227 attrName = "ModuleID"; 228 } 229 else { 230 /* recursive call, fetching path from Object DB */ 231 dlDb = objDlDb; 232 attrName = "Path"; 233 } 234 235 /* We want one attributes back, name and format specified by caller */ 236 recordAttrs.DataRecordType = recordType; 237 recordAttrs.SemanticInformation = 0; 238 recordAttrs.NumberOfAttributes = 1; 239 recordAttrs.AttributeData = &outAttr; 240 241 memset(&outAttr, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA)); 242 attrInfo = &outAttr.Info; 243 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 244 attrInfo->Label.AttributeName = (char *)attrName; 245 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; 246 247 /* one predicate - the caller's key and CSSM_DB_OPERATOR */ 248 predicate.DbOperator = valOp; 249 predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 250 predicate.Attribute.Info.Label.AttributeName = (char *)key; 251 predicate.Attribute.Info.AttributeFormat = valForm; 252 predData.Data = (uint8 *)valPtr; 253 predData.Length = valLen; 254 predicate.Attribute.Value = &predData; 255 predicate.Attribute.NumberOfValues = 1; 256 257 query.RecordType = recordType; 258 query.Conjunctive = CSSM_DB_NONE; 259 query.NumSelectionPredicates = 1; 260 query.SelectionPredicate = &predicate; 261 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? 262 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? 263 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? 264 265 crtn = mdsFuncs->DataGetFirst(dlDb, 266 &query, 267 &resultHand, 268 &recordAttrs, 269 NULL, // No data 270 &record); 271 switch(crtn) { 272 case CSSM_OK: 273 break; // proceed 274 case CSSMERR_DL_ENDOFDATA: 275 printf("%s: no record found\n", srchStr); 276 return; 277 default: 278 printError("DataGetFirst", crtn); 279 return; 280 } 281 /* dump this record, one attribute */ 282 if(srchStr) { 283 /* not done on recursive call */ 284 printf("%s found:\n", srchStr); 285 } 286 dumpRecord(&recordAttrs); 287 mdsFuncs->FreeUniqueRecord(dlDb, record); 288 289 if(srchStr != NULL) { 290 /* 291 * Now do a lookup in Object DB of this guid, looking for path. 292 * Apps normally don't do this; this is what CSSM does when given 293 * the GUID of a module. 294 */ 295 if(outAttr.Value == NULL) { 296 printf("***Screwup: DataGetFirst worked, but no outAttr\n"); 297 return; 298 } 299 doLookup(mdsFuncs, 300 objDlDb, 301 cdsaDlDb, 302 false, // use objDlDb 303 MDS_OBJECT_RECORDTYPE, 304 "ModuleID", // key 305 outAttr.Value->Data, // valPtr, ModuleID, as string 306 outAttr.Value->Length, // valLen 307 CSSM_DB_ATTRIBUTE_FORMAT_STRING, 308 CSSM_DB_EQUAL, 309 NULL); // srchStr 310 } 311 freeAttrs(&recordAttrs); 312 313 /* now the rest of them */ 314 for(;;) { 315 crtn = mdsFuncs->DataGetNext(dlDb, 316 resultHand, 317 &recordAttrs, 318 NULL, 319 &record); 320 switch(crtn) { 321 case CSSM_OK: 322 dumpRecord(&recordAttrs); 323 mdsFuncs->FreeUniqueRecord(cdsaDlDb, record); 324 if(srchStr != NULL) { 325 if(outAttr.Value == NULL) { 326 printf("***Screwup: DataGetNext worked, but no outAttr\n"); 327 return; 328 } 329 doLookup(mdsFuncs, 330 objDlDb, 331 cdsaDlDb, 332 false, // use objDlDb 333 MDS_OBJECT_RECORDTYPE, 334 "ModuleID", // key 335 outAttr.Value->Data, // valPtr, ModuleID, as string 336 outAttr.Value->Length, // valLen 337 CSSM_DB_ATTRIBUTE_FORMAT_STRING, 338 CSSM_DB_EQUAL, 339 NULL); // srchStr 340 } 341 freeAttrs(&recordAttrs); 342 break; // and go again 343 case CSSMERR_DL_ENDOFDATA: 344 /* normal termination */ 345 break; 346 default: 347 printError("DataGetNext", crtn); 348 break; 349 } 350 if(crtn != CSSM_OK) { 351 break; 352 } 353 } 354} 355 356int main(int argc, char **argv) 357{ 358 MDS_FUNCS mdsFuncs; 359 MDS_HANDLE mdsHand; 360 CSSM_RETURN crtn; 361 int arg; 362 CSSM_DB_HANDLE dbHand = 0; 363 MDS_DB_HANDLE objDlDb; 364 MDS_DB_HANDLE cdsaDlDb; 365 bool keepConnected = false; 366 uint32 val; 367 368 for(arg=2; arg<argc; arg++) { 369 switch(argv[arg][0]) { 370 case 'k': 371 keepConnected = true; 372 break; 373 default: 374 usage(argv); 375 } 376 } 377 crtn = MDS_Initialize(NULL, // callerGuid 378 &memFuncs, 379 &mdsFuncs, 380 &mdsHand); 381 if(crtn) { 382 printError("MDS_Initialize", crtn); 383 exit(1); 384 } 385 386 do { 387 /* 388 * Open both MDS DBs - apps normally only have to open 389 * MDS_CDSA_DIRECTORY_NAME. 390 */ 391 crtn = mdsFuncs.DbOpen(mdsHand, 392 MDS_OBJECT_DIRECTORY_NAME, 393 NULL, // DbLocation 394 CSSM_DB_ACCESS_READ, 395 NULL, // AccessCred - hopefully optional 396 NULL, // OpenParameters 397 &dbHand); 398 if(crtn) { 399 printError("DbOpen(MDS_OBJECT_DIRECTORY_NAME)", crtn); 400 exit(1); 401 } 402 objDlDb.DLHandle = mdsHand; 403 objDlDb.DBHandle = dbHand; 404 405 crtn = mdsFuncs.DbOpen(mdsHand, 406 MDS_CDSA_DIRECTORY_NAME, 407 NULL, // DbLocation 408 CSSM_DB_ACCESS_READ, 409 NULL, // AccessCred - hopefully optional 410 NULL, // OpenParameters 411 &dbHand); 412 if(crtn) { 413 printError("DbOpen(MDS_CDSA_DIRECTORY_NAME)", crtn); 414 exit(1); 415 } 416 cdsaDlDb.DLHandle = mdsHand; 417 cdsaDlDb.DBHandle = dbHand; 418 419 /* 420 * Do some typical lookups. 421 */ 422 423 /* a CSP which can do SHA1 digest */ 424 val = CSSM_ALGID_SHA1; 425 doLookup(&mdsFuncs, 426 objDlDb, 427 cdsaDlDb, 428 true, 429 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE, 430 "AlgType", 431 &val, 432 sizeof(uint32), 433 CSSM_DB_ATTRIBUTE_FORMAT_UINT32, 434 CSSM_DB_EQUAL, 435 "CSP for SHA1 digest"); 436 437 /* a TP which can do iSign verification */ 438 doLookup(&mdsFuncs, 439 objDlDb, 440 cdsaDlDb, 441 true, 442 MDS_CDSADIR_TP_OIDS_RECORDTYPE, 443 "OID", 444 CSSMOID_APPLE_ISIGN.Data, 445 CSSMOID_APPLE_ISIGN.Length, 446 CSSM_DB_ATTRIBUTE_FORMAT_BLOB, 447 CSSM_DB_EQUAL, 448 "TP for CSSMOID_APPLE_ISIGN policy"); 449 450 /* an X509-savvy CL */ 451 /* Very weird data form - two fields in one 32-bit word */ 452 val = (CSSM_CERT_X_509v3 << 16) | CSSM_CERT_ENCODING_DER; 453 doLookup(&mdsFuncs, 454 objDlDb, 455 cdsaDlDb, 456 true, 457 MDS_CDSADIR_CL_PRIMARY_RECORDTYPE, 458 "CertTypeFormat", 459 &val, 460 sizeof(uint32), 461 CSSM_DB_ATTRIBUTE_FORMAT_UINT32, 462 CSSM_DB_EQUAL, 463 "X509 CL"); 464 465 /* A DL which can do CSSM_DB_AND */ 466 val = CSSM_DB_AND; 467 doLookup(&mdsFuncs, 468 objDlDb, 469 cdsaDlDb, 470 true, 471 MDS_CDSADIR_DL_PRIMARY_RECORDTYPE, 472 "ConjunctiveOps", 473 &val, 474 sizeof(uint32), 475 CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32, 476 /* This is a multi-uint32, meaning we want to search for a 477 * ConjunctiveOps which contains the specified value */ 478 CSSM_DB_CONTAINS, 479 "DL with ConjunctiveOp CSSM_DB_AND"); 480 481 /* a CSP which can do CSSM_ALGID_IDEA, should fail */ 482 val = CSSM_ALGID_IDEA; 483 doLookup(&mdsFuncs, 484 objDlDb, 485 cdsaDlDb, 486 true, 487 MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE, 488 "AlgType", 489 &val, 490 sizeof(uint32), 491 CSSM_DB_ATTRIBUTE_FORMAT_UINT32, 492 CSSM_DB_EQUAL, 493 "CSP for CSSM_ALGID_BLOWFISH, expect failure"); 494 495 /* a TP which can obtain a .mac signing certificate */ 496 doLookup(&mdsFuncs, 497 objDlDb, 498 cdsaDlDb, 499 true, 500 MDS_CDSADIR_TP_OIDS_RECORDTYPE, 501 "OID", 502 CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Data, 503 CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Length, 504 CSSM_DB_ATTRIBUTE_FORMAT_BLOB, 505 CSSM_DB_EQUAL, 506 "TP for .mac signing certificate policy"); 507 508 crtn = mdsFuncs.DbClose(objDlDb); 509 if(crtn) { 510 printError("DbClose(objDlDb)", crtn); 511 } 512 crtn = mdsFuncs.DbClose(cdsaDlDb); 513 if(crtn) { 514 printError("DbClose(cdsaDlDb)", crtn); 515 } 516 if(keepConnected) { 517 printf("\n"); 518 fpurge(stdin); 519 printf("Enter CR to go again: "); 520 getchar(); 521 } 522 } while(keepConnected); 523 crtn = MDS_Terminate(mdsHand); 524 if(crtn) { 525 printError("MDS_Terminate", crtn); 526 } 527 return 0; 528} 529