1/* 2 * mdsdump.cpp - dump contents of system MDS databases 3 */ 4 5 /**** FIXME this uses a private API not currently exported in any way from 6 **** Security project 7 ****/ 8 9#include <CoreFoundation/CoreFoundation.h> 10#include <stdlib.h> 11#include <stdio.h> 12#include <Security/mds.h> 13#include <common.h> 14#include <Security/mds_schema.h> 15#include "MDSSchema.h" 16#include <strings.h> 17 18#define MAX_MDS_ATTRS 32 19 20static CSSM_MEMORY_FUNCS memFuncs = { 21 appMalloc, 22 appFree, 23 appRealloc, 24 appCalloc, 25 NULL 26 }; 27 28static void showInfoTypes() 29{ 30 printf(" o Object records\n"); 31 printf(" C CSSM records\n"); 32 printf(" p Plugin Common records\n"); 33 printf(" c CSP records\n"); 34 printf(" l CL records\n"); 35 printf(" t TP records\n"); 36 printf(" d DL records\n"); 37 printf(" a All records from Object DB\n"); 38 printf(" A All records from CDSA directory DB\n"); 39} 40 41static void usage(char **argv) 42{ 43 printf("Usage: %s info_type [options...]\n", argv[0]); 44 printf("info_type values:\n"); 45 showInfoTypes(); 46 printf(" h help\n"); 47 printf("Options:\n"); 48 printf(" i perform MDS_Install()\n"); 49 printf(" v verbose\n"); 50 printf(" k keep connected and go again\n"); 51 exit(1); 52} 53 54#define NORM_KEY_LEN 20 55 56/* print a key name, padding out to NORM_KEY_LEN columns */ 57static void printName( 58 const char *attrName) 59{ 60 printf(" %s", attrName); 61 int len = strlen(attrName); 62 if(len > NORM_KEY_LEN) { 63 return; 64 } 65 int numSpaces = NORM_KEY_LEN - len; 66 for(int i=0; i<numSpaces; i++) { 67 putchar(' '); 68 } 69 70} 71 72#if 0 73/* 74 * Attempt to print a numeric value as a string, per a MDSNameValuePair table. 75 * Of course this can not deal with OR'd together values; the MDSNameValuePair 76 * mechanism does not indicate on a per-field basis which fields are OR-able. 77 * If the value is in fact a collection of legal values (per the nameValues 78 * array), the value will just be printed in hex. 79 */ 80static void printValueAsString( 81 unsigned val, 82 const Security::MDSNameValuePair *nameValues) // optional 83{ 84 if(nameValues != NULL) { 85 while(nameValues->name != NULL) { 86 if(nameValues->value == val) { 87 printf("%s", nameValues->name); 88 return; 89 } 90 nameValues++; 91 } 92 } 93 /* Oh well */ 94 printf("0x%x", val); 95} 96#endif 97 98/* print value string, surrounded by single quotes, then a newline */ 99static void printValue( 100 const CSSM_DATA *attrValue) 101{ 102 printf("'"); 103 for(uint32 dex=0; dex<attrValue->Length; dex++) { 104 printf("%c", attrValue->Data[dex]); 105 } 106 printf("'\n"); 107} 108 109/* Print one attribute value */ 110static void dumpAttr( 111 CSSM_DB_ATTRIBUTE_FORMAT attrForm, 112 const CSSM_DATA *attrData) 113{ 114 if((attrData == NULL) || (attrData->Data == NULL)) { 115 printf("<NULL DATA>\n"); 116 return; 117 } 118 void *data = attrData->Data; 119 switch(attrForm) { 120 case CSSM_DB_ATTRIBUTE_FORMAT_STRING: 121 printValue(attrData); 122 break; 123 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: // not really supported in MDS 124 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: 125 { 126 unsigned val = *(unsigned *)data; 127 printf("0x%x\n", val); 128 break; 129 } 130 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: 131 { 132 printf("BLOB length %u : ", (unsigned)attrData->Length); 133 for(unsigned i=0; i<attrData->Length; i++) { 134 unsigned dat = attrData->Data[i]; 135 printf("%02X ", dat); 136 } 137 printf("\n"); 138 break; 139 } 140 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: 141 { 142 printf("multi_int["); 143 uint32 numInts = attrData->Length / sizeof(uint32); 144 uint32 *uip = (uint32 *)data; 145 for(unsigned i=0; i<numInts; i++) { 146 if(i > 0) { 147 printf(", "); 148 } 149 printf("0x%x", (unsigned)(*uip++)); 150 } 151 printf("]\n"); 152 break; 153 } 154 default: 155 printf("***UNKNOWN FORMAT (%u), Length %u\n", 156 (unsigned)attrForm, (unsigned)attrData->Length); 157 break; 158 } 159} 160 161/* 162 * Vanilla "dump one record" routine. Assumes format of all attribute labels 163 * as string. Uses a MDSNameValuePair ptr array in parallel to the attributes 164 * themselves to facilitate displaying numeric values as strings (e.g. 165 * "CSSM_ALGID_SHA1") where possible. 166 */ 167static void dumpRecord( 168 const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs) 169{ 170 unsigned dex; 171 for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) { 172 const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex]; 173 if(attrData->Info.AttributeNameFormat != CSSM_DB_ATTRIBUTE_NAME_AS_STRING) { 174 printf("***BAD ATTR_NAME FORMAT (%u)\n", 175 (unsigned)attrData->Info.AttributeNameFormat); 176 continue; 177 } 178 const char *attrName = attrData->Info.Label.AttributeName; 179 printName(attrName); 180 printf(": "); 181 /* note currently in MDS NumberOfValues is always one or zero */ 182 for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) { 183 dumpAttr(attrData->Info.AttributeFormat, 184 &attrData->Value[attrNum]); 185 } 186 if(attrData->NumberOfValues == 0) { 187 printf("<<no values present>>\n"); 188 } 189 } 190} 191 192/* free attribute(s) allocated by MDS */ 193static void freeAttrs( 194 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR recordAttrs) 195{ 196 unsigned i; 197 198 for(i=0; i<recordAttrs->NumberOfAttributes; i++) { 199 CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i]; 200 if(attrData == NULL) { 201 /* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */ 202 printf("***freeAttrs screwup: NULL attrData\n"); 203 return; 204 } 205 unsigned j; 206 for(j=0; j<attrData->NumberOfValues; j++) { 207 CSSM_DATA_PTR data = &attrData->Value[j]; 208 if(data == NULL) { 209 /* fault of MDS, who said there was a value here */ 210 printf("***freeAttrs screwup: NULL data\n"); 211 return; 212 } 213 appFree(data->Data, NULL); 214 data->Data = NULL; 215 data->Length = 0; 216 } 217 appFree(attrData->Value, NULL); 218 attrData->Value = NULL; 219 } 220} 221 222/* 223 * Fetch and display all records of specified CSSM_DB_RECORDTYPE. 224 */ 225static void fetchAllAttrs( 226 MDS_FUNCS *mdsFuncs, 227 MDS_DB_HANDLE dlDb, 228 CSSM_DB_RECORDTYPE recordType) 229{ 230 CSSM_QUERY query; 231 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 232 CSSM_RETURN crtn; 233 CSSM_HANDLE resultHand; 234 CSSM_DB_ATTRIBUTE_DATA attrs[MAX_MDS_ATTRS]; 235 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 236 const RelationInfo *relInfo; 237 238 relInfo = MDSRecordTypeToRelation(recordType); 239 if(relInfo == NULL) { 240 printf("***UNKNOWN recordType %d\n", (int)recordType); 241 return; 242 } 243 244 /* build an attr array from schema so we get all known attrs */ 245 memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * MAX_MDS_ATTRS); 246 unsigned attrDex; 247 for(attrDex=0; attrDex<relInfo->NumberOfAttributes; attrDex++) { 248 attrs[attrDex].Info = relInfo->AttributeInfo[attrDex]; 249 } 250 recordAttrs.DataRecordType = recordType; 251 recordAttrs.NumberOfAttributes = relInfo->NumberOfAttributes; 252 recordAttrs.AttributeData = attrs; 253 254 /* just search by recordType, no predicates */ 255 query.RecordType = recordType; 256 query.Conjunctive = CSSM_DB_NONE; 257 query.NumSelectionPredicates = 0; 258 query.SelectionPredicate = NULL; 259 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? 260 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? 261 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? 262 263 crtn = mdsFuncs->DataGetFirst(dlDb, 264 &query, 265 &resultHand, 266 &recordAttrs, 267 NULL, // No data 268 &record); 269 switch(crtn) { 270 case CSSM_OK: 271 break; // proceed 272 case CSSMERR_DL_ENDOFDATA: 273 printf("%s: no record found\n", relInfo->relationName); 274 return; 275 default: 276 printError("DataGetFirst", crtn); 277 return; 278 } 279 unsigned recNum = 0; 280 printf("%s:\n", relInfo->relationName); 281 printf(" record %d; numAttrs %d:\n", 282 recNum++, (int)recordAttrs.NumberOfAttributes); 283 284 dumpRecord(&recordAttrs); 285 mdsFuncs->FreeUniqueRecord(dlDb, record); 286 freeAttrs(&recordAttrs); 287 288 /* now the rest of them */ 289 /* hopefully we don't have to re-init the recordAttr array */ 290 for(;;) { 291 crtn = mdsFuncs->DataGetNext(dlDb, 292 resultHand, 293 &recordAttrs, 294 NULL, 295 &record); 296 switch(crtn) { 297 case CSSM_OK: 298 printf(" record %d; numAttrs %d:\n", 299 recNum++, (int)recordAttrs.NumberOfAttributes); 300 dumpRecord(&recordAttrs); 301 mdsFuncs->FreeUniqueRecord(dlDb, record); 302 freeAttrs(&recordAttrs); 303 break; // and go again 304 case CSSMERR_DL_ENDOFDATA: 305 /* normal termination */ 306 break; 307 default: 308 printError("DataGetNext", crtn); 309 break; 310 } 311 if(crtn != CSSM_OK) { 312 break; 313 } 314 } 315} 316 317/* 318 * This is different - it's schema-independent. Fetch all records from specified 319 * DlDb which contain a ModuleID attribute. 320 */ 321static void fetchAllRecords( 322 MDS_FUNCS *mdsFuncs, 323 MDS_DB_HANDLE dlDb) 324{ 325 CSSM_QUERY query; 326 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 327 CSSM_RETURN crtn; 328 CSSM_HANDLE resultHand; 329 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 330 CSSM_DB_ATTRIBUTE_DATA theAttr; 331 CSSM_DB_ATTRIBUTE_INFO_PTR attrInfo = &theAttr.Info; 332 CSSM_DATA attrValue = {0, NULL}; 333 334 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY; 335 recordAttrs.SemanticInformation = 0; 336 recordAttrs.NumberOfAttributes = 1; 337 recordAttrs.AttributeData = &theAttr; 338 339 attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 340 attrInfo->Label.AttributeName = (char *)"ModuleID"; 341 attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING; 342 343 theAttr.NumberOfValues = 1; 344 theAttr.Value = &attrValue; 345 346 /* just search by recordType, no predicates */ 347 query.RecordType = CSSM_DL_DB_RECORD_ANY; 348 query.Conjunctive = CSSM_DB_NONE; 349 query.NumSelectionPredicates = 0; 350 query.SelectionPredicate = NULL; 351 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? 352 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? 353 query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? 354 355 crtn = mdsFuncs->DataGetFirst(dlDb, 356 &query, 357 &resultHand, 358 &recordAttrs, 359 NULL, // No data 360 &record); 361 switch(crtn) { 362 case CSSM_OK: 363 break; // proceed 364 case CSSMERR_DL_ENDOFDATA: 365 printf("no record found\n"); 366 return; 367 default: 368 printError("DataGetFirst", crtn); 369 return; 370 } 371 unsigned recNum = 0; 372 printf("Records containing a ModuleID attribute:\n"); 373 printf(" record %d:\n", recNum++); 374 375 dumpRecord(&recordAttrs); 376 mdsFuncs->FreeUniqueRecord(dlDb, record); 377 freeAttrs(&recordAttrs); 378 379 /* now the rest of them */ 380 /* hopefully we don't have to re-init the recordAttr array */ 381 for(;;) { 382 crtn = mdsFuncs->DataGetNext(dlDb, 383 resultHand, 384 &recordAttrs, 385 NULL, 386 &record); 387 switch(crtn) { 388 case CSSM_OK: 389 printf(" record %d:\n", recNum++); 390 dumpRecord(&recordAttrs); 391 mdsFuncs->FreeUniqueRecord(dlDb, record); 392 freeAttrs(&recordAttrs); 393 break; // and go again 394 case CSSMERR_DL_ENDOFDATA: 395 /* normal termination */ 396 break; 397 default: 398 printError("DataGetNext", crtn); 399 break; 400 } 401 if(crtn != CSSM_OK) { 402 break; 403 } 404 } 405} 406 407static void doInstall( 408 MDS_HANDLE mdsHand) 409{ 410 CFAbsoluteTime start, end; 411 412 start = CFAbsoluteTimeGetCurrent(); 413 CSSM_RETURN crtn = MDS_Install(mdsHand); 414 end = CFAbsoluteTimeGetCurrent(); 415 if(crtn) { 416 printError("MDS_Install", crtn); 417 } 418 else { 419 printf("MDS_Install took %gs\n", end - start); 420 } 421} 422 423int main(int argc, char **argv) 424{ 425 MDS_FUNCS mdsFuncs; 426 MDS_HANDLE mdsHand; 427 CSSM_RETURN crtn; 428 int arg; 429 char op; 430 char *dbName; 431 CSSM_DB_HANDLE dbHand = 0; 432 MDS_DB_HANDLE dlDb; 433 bool verbose = 0; 434 bool keepConnected = false; 435 bool install = false; 436 437 if(argc < 2) { 438 usage(argv); 439 } 440 op = argv[1][0]; 441 if(op == 'h') { 442 usage(argv); 443 } 444 for(arg=2; arg<argc; arg++) { 445 switch(argv[arg][0]) { 446 case 'v': 447 verbose = true; 448 break; 449 case 'i': 450 install = true; 451 break; 452 case 'k': 453 keepConnected = true; 454 break; 455 default: 456 usage(argv); 457 } 458 } 459 if(verbose) { 460 printf("..calling MDS_Initialize\n"); 461 } 462 crtn = MDS_Initialize(NULL, // callerGuid 463 &memFuncs, 464 &mdsFuncs, 465 &mdsHand); 466 if(crtn) { 467 printError("MDS_Initialize", crtn); 468 exit(1); 469 } 470 if(install) { 471 doInstall(mdsHand); 472 } 473 do { 474 /* open one or the other DB */ 475 switch(op) { 476 case 'o': 477 case 'a': 478 dbName = (char *)MDS_OBJECT_DIRECTORY_NAME; 479 break; 480 default: 481 dbName = (char *)MDS_CDSA_DIRECTORY_NAME; 482 break; 483 } 484 crtn = mdsFuncs.DbOpen(mdsHand, 485 dbName, 486 NULL, // DbLocation 487 CSSM_DB_ACCESS_READ, 488 NULL, // AccessCred - hopefully optional 489 NULL, // OpenParameters 490 &dbHand); 491 if(crtn) { 492 printError("DbOpen", crtn); 493 exit(1); 494 } 495 dlDb.DLHandle = mdsHand; 496 dlDb.DBHandle = dbHand; 497 498 /* go for it */ 499 switch(op) { 500 case 'o': 501 fetchAllAttrs(&mdsFuncs, dlDb, MDS_OBJECT_RECORDTYPE); 502 break; 503 case 'C': 504 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSSM_RECORDTYPE); 505 break; 506 case 'p': 507 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_COMMON_RECORDTYPE); 508 break; 509 case 'c': 510 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE); 511 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE); 512 if(verbose) { 513 fetchAllAttrs(&mdsFuncs, dlDb, 514 MDS_CDSADIR_CSP_ENCAPSULATED_PRODUCT_RECORDTYPE); 515 fetchAllAttrs(&mdsFuncs, dlDb, 516 MDS_CDSADIR_CSP_SC_INFO_RECORDTYPE); 517 } 518 break; 519 case 'l': 520 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CL_PRIMARY_RECORDTYPE); 521 break; 522 case 't': 523 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_PRIMARY_RECORDTYPE); 524 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_OIDS_RECORDTYPE); 525 if(verbose) { 526 fetchAllAttrs(&mdsFuncs, dlDb, 527 MDS_CDSADIR_TP_ENCAPSULATED_PRODUCT_RECORDTYPE); 528 } 529 break; 530 case 'd': 531 fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_DL_PRIMARY_RECORDTYPE); 532 break; 533 case 'a': 534 case 'A': 535 fetchAllRecords(&mdsFuncs, dlDb); 536 break; 537 default: 538 usage(argv); 539 } 540 541 crtn = mdsFuncs.DbClose(dlDb); 542 if(crtn) { 543 printError("DbClose", crtn); 544 } 545 if(keepConnected) { 546 printf("\n"); 547 showInfoTypes(); 548 fpurge(stdin); 549 printf("Enter new info type: "); 550 op = getchar(); 551 } 552 } while(keepConnected); 553 crtn = MDS_Terminate(mdsHand); 554 if(crtn) { 555 printError("MDS_Terminate", crtn); 556 } 557 return 0; 558} 559