dbck.c (48770B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 ** dbck.c 7 ** 8 ** utility for fixing corrupt cert databases 9 ** 10 */ 11 #include <stdio.h> 12 #include <string.h> 13 14 #include "secutil.h" 15 #include "cdbhdl.h" 16 #include "certdb.h" 17 #include "cert.h" 18 #include "nspr.h" 19 #include "prtypes.h" 20 #include "prtime.h" 21 #include "prlong.h" 22 #include "pcert.h" 23 #include "nss.h" 24 25 static char *progName; 26 27 /* placeholders for pointer error types */ 28 static void *WrongEntry; 29 static void *NoNickname; 30 static void *NoSMime; 31 32 typedef enum { 33 /* 0*/ NoSubjectForCert = 0, 34 /* 1*/ SubjectHasNoKeyForCert, 35 /* 2*/ NoNicknameOrSMimeForSubject, 36 /* 3*/ WrongNicknameForSubject, 37 /* 4*/ NoNicknameEntry, 38 /* 5*/ WrongSMimeForSubject, 39 /* 6*/ NoSMimeEntry, 40 /* 7*/ NoSubjectForNickname, 41 /* 8*/ NoSubjectForSMime, 42 /* 9*/ NicknameAndSMimeEntries, 43 NUM_ERROR_TYPES 44 } dbErrorType; 45 46 static char *dbErrorString[NUM_ERROR_TYPES] = { 47 /* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.", 48 /* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.", 49 /* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.", 50 /* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.", 51 /* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.", 52 /* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.", 53 /* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.", 54 /* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.", 55 /* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.", 56 }; 57 58 static char *errResult[NUM_ERROR_TYPES] = { 59 "Certificate entries that had no subject entry.", 60 "Subject entries with no corresponding Certificate entries.", 61 "Subject entries that had no nickname or S/MIME entries.", 62 "Redundant nicknames (subjects with the same nickname).", 63 "Subject entries that had no nickname entry.", 64 "Redundant email addresses (subjects with the same email address).", 65 "Subject entries that had no S/MIME entry.", 66 "Nickname entries that had no subject entry.", 67 "S/MIME entries that had no subject entry.", 68 "Subject entries with BOTH nickname and S/MIME entries." 69 }; 70 71 enum { 72 GOBOTH = 0, 73 GORIGHT, 74 GOLEFT 75 }; 76 77 typedef struct 78 { 79 PRBool verbose; 80 PRBool dograph; 81 PRFileDesc *out; 82 PRFileDesc *graphfile; 83 int dbErrors[NUM_ERROR_TYPES]; 84 } dbDebugInfo; 85 86 struct certDBEntryListNodeStr { 87 PRCList link; 88 certDBEntry entry; 89 void *appData; 90 }; 91 typedef struct certDBEntryListNodeStr certDBEntryListNode; 92 93 /* 94 * A list node for a cert db entry. The index is a unique identifier 95 * to use for creating generic maps of a db. This struct handles 96 * the cert, nickname, and smime db entry types, as all three have a 97 * single handle to a subject entry. 98 * This structure is pointed to by certDBEntryListNode->appData. 99 */ 100 typedef struct 101 { 102 PLArenaPool *arena; 103 int index; 104 certDBEntryListNode *pSubject; 105 } certDBEntryMap; 106 107 /* 108 * Subject entry is special case, it has bidirectional handles. One 109 * subject entry can point to several certs (using the same DN), and 110 * a nickname and/or smime entry. 111 * This structure is pointed to by certDBEntryListNode->appData. 112 */ 113 typedef struct 114 { 115 PLArenaPool *arena; 116 int index; 117 int numCerts; 118 certDBEntryListNode **pCerts; 119 certDBEntryListNode *pNickname; 120 certDBEntryListNode *pSMime; 121 } certDBSubjectEntryMap; 122 123 /* 124 * A map of a certdb. 125 */ 126 typedef struct 127 { 128 int numCerts; 129 int numSubjects; 130 int numNicknames; 131 int numSMime; 132 int numRevocation; 133 certDBEntryListNode certs; /* pointer to head of cert list */ 134 certDBEntryListNode subjects; /* pointer to head of subject list */ 135 certDBEntryListNode nicknames; /* pointer to head of nickname list */ 136 certDBEntryListNode smime; /* pointer to head of smime list */ 137 certDBEntryListNode revocation; /* pointer to head of revocation list */ 138 } certDBArray; 139 140 /* Cast list to the base element, a certDBEntryListNode. */ 141 #define LISTNODE_CAST(node) \ 142 ((certDBEntryListNode *)(node)) 143 144 static void 145 Usage(char *progName) 146 { 147 #define FPS fprintf(stderr, 148 FPS "Type %s -H for more detailed descriptions\n", progName); 149 FPS "Usage: %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", 150 progName); 151 #ifdef DORECOVER 152 FPS " %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", 153 progName); 154 #endif 155 exit(-1); 156 } 157 158 static void 159 LongUsage(char *progName) 160 { 161 FPS "%-15s Display this help message.\n", 162 "-H"); 163 FPS "%-15s Dump analysis. No changes will be made to the database.\n", 164 "-D"); 165 FPS "%-15s Cert database directory (default is ~/.netscape)\n", 166 " -d certdir"); 167 FPS "%-15s Put database graph in ./mailfile (default is stdout).\n", 168 " -m"); 169 FPS "%-15s Verbose mode. Dumps the entire contents of your cert8.db.\n", 170 " -v"); 171 FPS "%-15s File to dump verbose output into. (default is stdout)\n", 172 " -f dumpfile"); 173 #ifdef DORECOVER 174 FPS "%-15s Repair the database. The program will look for broken\n", 175 "-R"); 176 FPS "%-15s dependencies between subject entries and certificates,\n", 177 ""); 178 FPS "%-15s between nickname entries and subjects, and between SMIME\n", 179 ""); 180 FPS "%-15s profiles and subjects. Any duplicate entries will be\n", 181 ""); 182 FPS "%-15s removed, any missing entries will be created.\n", 183 ""); 184 FPS "%-15s File to store new database in (default is new_cert8.db)\n", 185 " -o newdbname"); 186 FPS "%-15s Cert database directory (default is ~/.netscape)\n", 187 " -d certdir"); 188 FPS "%-15s Prompt before removing any certificates.\n", 189 " -p"); 190 FPS "%-15s Keep all possible certificates. Only remove certificates\n", 191 " -a"); 192 FPS "%-15s which prevent creation of a consistent database. Thus any\n", 193 ""); 194 FPS "%-15s expired or redundant entries will be kept.\n", 195 ""); 196 FPS "%-15s Keep redundant nickname/email entries. It is possible\n", 197 " -r"); 198 FPS "%-15s only one such entry will be usable.\n", 199 ""); 200 FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n", 201 " -s"); 202 FPS "%-15s cert. An empty profile will be created.\n", 203 ""); 204 FPS "%-15s Keep expired certificates.\n", 205 " -x"); 206 FPS "%-15s Verbose mode - report all activity while recovering db.\n", 207 " -v"); 208 FPS "%-15s File to dump verbose output into.\n", 209 " -f dumpfile"); 210 FPS "\n"); 211 #endif 212 exit(-1); 213 #undef FPS 214 } 215 216 /******************************************************************* 217 * 218 * Functions for dbck. 219 * 220 ******************************************************************/ 221 222 void 223 printHexString(PRFileDesc *out, SECItem *hexval) 224 { 225 unsigned int i; 226 for (i = 0; i < hexval->len; i++) { 227 if (i != hexval->len - 1) { 228 PR_fprintf(out, "%02x:", hexval->data[i]); 229 } else { 230 PR_fprintf(out, "%02x", hexval->data[i]); 231 } 232 } 233 PR_fprintf(out, "\n"); 234 } 235 236 SECStatus 237 dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile) 238 { 239 int userCert = 0; 240 CERTCertTrust *trust = cert->trust; 241 userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) || 242 (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) || 243 (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER); 244 if (num >= 0) { 245 PR_fprintf(outfile, "Certificate: %3d\n", num); 246 } else { 247 PR_fprintf(outfile, "Certificate:\n"); 248 } 249 PR_fprintf(outfile, "----------------\n"); 250 if (userCert) 251 PR_fprintf(outfile, "(User Cert)\n"); 252 PR_fprintf(outfile, "## SUBJECT: %s\n", cert->subjectName); 253 PR_fprintf(outfile, "## ISSUER: %s\n", cert->issuerName); 254 PR_fprintf(outfile, "## SERIAL NUMBER: "); 255 printHexString(outfile, &cert->serialNumber); 256 { /* XXX should be separate function. */ 257 PRTime timeBefore, timeAfter; 258 PRExplodedTime beforePrintable, afterPrintable; 259 char *beforestr, *afterstr; 260 DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore); 261 DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter); 262 PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable); 263 PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable); 264 beforestr = PORT_Alloc(100); 265 afterstr = PORT_Alloc(100); 266 PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable); 267 PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable); 268 PR_fprintf(outfile, "## VALIDITY: %s to %s\n", beforestr, afterstr); 269 } 270 PR_fprintf(outfile, "\n"); 271 return SECSuccess; 272 } 273 274 SECStatus 275 dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile) 276 { 277 #if 0 278 NSSLOWCERTCertificate *cert; 279 /* should we check for existing duplicates? */ 280 cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, 281 entry->cert.nickname); 282 #else 283 CERTCertificate *cert; 284 cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL); 285 #endif 286 if (!cert) { 287 fprintf(stderr, "Failed to decode certificate.\n"); 288 return SECFailure; 289 } 290 cert->trust = (CERTCertTrust *)&entry->trust; 291 dumpCertificate(cert, num, outfile); 292 CERT_DestroyCertificate(cert); 293 return SECSuccess; 294 } 295 296 SECStatus 297 dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile) 298 { 299 char *subjectName = CERT_DerNameToAscii(&entry->derSubject); 300 301 PR_fprintf(outfile, "Subject: %3d\n", num); 302 PR_fprintf(outfile, "------------\n"); 303 PR_fprintf(outfile, "## %s\n", subjectName); 304 if (entry->nickname) 305 PR_fprintf(outfile, "## Subject nickname: %s\n", entry->nickname); 306 if (entry->emailAddrs) { 307 unsigned int n; 308 for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) { 309 char *emailAddr = entry->emailAddrs[n]; 310 if (emailAddr[0]) { 311 PR_fprintf(outfile, "## Subject email address: %s\n", 312 emailAddr); 313 } 314 } 315 } 316 PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts); 317 PR_fprintf(outfile, "\n"); 318 PORT_Free(subjectName); 319 return SECSuccess; 320 } 321 322 SECStatus 323 dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile) 324 { 325 PR_fprintf(outfile, "Nickname: %3d\n", num); 326 PR_fprintf(outfile, "-------------\n"); 327 PR_fprintf(outfile, "## \"%s\"\n\n", entry->nickname); 328 return SECSuccess; 329 } 330 331 SECStatus 332 dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile) 333 { 334 PR_fprintf(outfile, "S/MIME Profile: %3d\n", num); 335 PR_fprintf(outfile, "-------------------\n"); 336 PR_fprintf(outfile, "## \"%s\"\n", entry->emailAddr); 337 #ifdef OLDWAY 338 PR_fprintf(outfile, "## OPTIONS: "); 339 printHexString(outfile, &entry->smimeOptions); 340 PR_fprintf(outfile, "## TIMESTAMP: "); 341 printHexString(outfile, &entry->optionsDate); 342 #else 343 SECU_PrintAny(stdout, &entry->smimeOptions, "## OPTIONS ", 0); 344 fflush(stdout); 345 if (entry->optionsDate.len && entry->optionsDate.data) 346 PR_fprintf(outfile, "## TIMESTAMP: %.*s\n", 347 entry->optionsDate.len, entry->optionsDate.data); 348 #endif 349 PR_fprintf(outfile, "\n"); 350 return SECSuccess; 351 } 352 353 SECStatus 354 mapCertEntries(certDBArray *dbArray) 355 { 356 certDBEntryCert *certEntry; 357 certDBEntrySubject *subjectEntry; 358 certDBEntryListNode *certNode, *subjNode; 359 certDBSubjectEntryMap *smap; 360 certDBEntryMap *map; 361 PLArenaPool *tmparena; 362 SECItem derSubject; 363 SECItem certKey; 364 PRCList *cElem, *sElem; 365 366 /* Arena for decoded entries */ 367 tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 368 if (tmparena == NULL) { 369 PORT_SetError(SEC_ERROR_NO_MEMORY); 370 return SECFailure; 371 } 372 373 /* Iterate over cert entries and map them to subject entries. 374 * NOTE: mapSubjectEntries must be called first to alloc memory 375 * for array of subject->cert map. 376 */ 377 for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 378 cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) { 379 certNode = LISTNODE_CAST(cElem); 380 certEntry = (certDBEntryCert *)&certNode->entry; 381 map = (certDBEntryMap *)certNode->appData; 382 CERT_NameFromDERCert(&certEntry->derCert, &derSubject); 383 CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey); 384 /* Loop over found subjects for cert's DN. */ 385 for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); 386 sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { 387 subjNode = LISTNODE_CAST(sElem); 388 subjectEntry = (certDBEntrySubject *)&subjNode->entry; 389 if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) { 390 unsigned int i; 391 /* Found matching subject name, create link. */ 392 map->pSubject = subjNode; 393 /* Make sure subject entry has cert's key. */ 394 for (i = 0; i < subjectEntry->ncerts; i++) { 395 if (SECITEM_ItemsAreEqual(&certKey, 396 &subjectEntry->certKeys[i])) { 397 /* Found matching cert key. */ 398 smap = (certDBSubjectEntryMap *)subjNode->appData; 399 smap->pCerts[i] = certNode; 400 break; 401 } 402 } 403 } 404 } 405 } 406 PORT_FreeArena(tmparena, PR_FALSE); 407 return SECSuccess; 408 } 409 410 SECStatus 411 mapSubjectEntries(certDBArray *dbArray) 412 { 413 certDBEntrySubject *subjectEntry; 414 certDBEntryListNode *subjNode; 415 certDBSubjectEntryMap *subjMap; 416 PRCList *sElem; 417 418 for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); 419 sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { 420 /* Iterate over subject entries and map subjects to nickname 421 * and smime entries. The cert<->subject map will be handled 422 * by a subsequent call to mapCertEntries. 423 */ 424 subjNode = LISTNODE_CAST(sElem); 425 subjectEntry = (certDBEntrySubject *)&subjNode->entry; 426 subjMap = (certDBSubjectEntryMap *)subjNode->appData; 427 /* need to alloc memory here for array of matching certs. */ 428 subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 429 subjectEntry->ncerts * sizeof(int)); 430 subjMap->numCerts = subjectEntry->ncerts; 431 subjMap->pNickname = NoNickname; 432 subjMap->pSMime = NoSMime; 433 434 if (subjectEntry->nickname) { 435 /* Subject should have a nickname entry, so create a link. */ 436 PRCList *nElem; 437 for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link); 438 nElem != &dbArray->nicknames.link; 439 nElem = PR_NEXT_LINK(nElem)) { 440 certDBEntryListNode *nickNode; 441 certDBEntryNickname *nicknameEntry; 442 /* Look for subject's nickname in nickname entries. */ 443 nickNode = LISTNODE_CAST(nElem); 444 nicknameEntry = (certDBEntryNickname *)&nickNode->entry; 445 if (PL_strcmp(subjectEntry->nickname, 446 nicknameEntry->nickname) == 0) { 447 /* Found a nickname entry for subject's nickname. */ 448 if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject, 449 &nicknameEntry->subjectName)) { 450 certDBEntryMap *nickMap; 451 nickMap = (certDBEntryMap *)nickNode->appData; 452 /* Nickname and subject match. */ 453 subjMap->pNickname = nickNode; 454 nickMap->pSubject = subjNode; 455 } else if (subjMap->pNickname == NoNickname) { 456 /* Nickname entry found is for diff. subject. */ 457 subjMap->pNickname = WrongEntry; 458 } 459 } 460 } 461 } 462 if (subjectEntry->emailAddrs) { 463 unsigned int n; 464 for (n = 0; n < subjectEntry->nemailAddrs && 465 subjectEntry->emailAddrs[n]; 466 ++n) { 467 char *emailAddr = subjectEntry->emailAddrs[n]; 468 if (emailAddr[0]) { 469 PRCList *mElem; 470 /* Subject should have an smime entry, so create a link. */ 471 for (mElem = PR_LIST_HEAD(&dbArray->smime.link); 472 mElem != &dbArray->smime.link; 473 mElem = PR_NEXT_LINK(mElem)) { 474 certDBEntryListNode *smimeNode; 475 certDBEntrySMime *smimeEntry; 476 /* Look for subject's email in S/MIME entries. */ 477 smimeNode = LISTNODE_CAST(mElem); 478 smimeEntry = (certDBEntrySMime *)&smimeNode->entry; 479 if (PL_strcmp(emailAddr, 480 smimeEntry->emailAddr) == 0) { 481 /* Found a S/MIME entry for subject's email. */ 482 if (SECITEM_ItemsAreEqual( 483 &subjectEntry->derSubject, 484 &smimeEntry->subjectName)) { 485 certDBEntryMap *smimeMap; 486 /* S/MIME entry and subject match. */ 487 subjMap->pSMime = smimeNode; 488 smimeMap = (certDBEntryMap *)smimeNode->appData; 489 smimeMap->pSubject = subjNode; 490 } else if (subjMap->pSMime == NoSMime) { 491 /* S/MIME entry found is for diff. subject. */ 492 subjMap->pSMime = WrongEntry; 493 } 494 } 495 } /* end for */ 496 } /* endif (emailAddr[0]) */ 497 } /* end for */ 498 } /* endif (subjectEntry->emailAddrs) */ 499 } 500 return SECSuccess; 501 } 502 503 void 504 printnode(dbDebugInfo *info, const char *str, int num) 505 { 506 if (!info->dograph) 507 return; 508 if (num < 0) { 509 PR_fprintf(info->graphfile, str); 510 } else { 511 PR_fprintf(info->graphfile, str, num); 512 } 513 } 514 515 PRBool 516 map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent) 517 { 518 if (mapPtr == NULL) { 519 if (indent > 0) 520 printnode(info, " ", -1); 521 if (indent >= 0) 522 printnode(info, "******************* ", -1); 523 return PR_FALSE; 524 } else if (mapPtr == WrongEntry) { 525 if (indent > 0) 526 printnode(info, " ", -1); 527 if (indent >= 0) 528 printnode(info, "??????????????????? ", -1); 529 return PR_FALSE; 530 } else { 531 return PR_TRUE; 532 } 533 } 534 535 /* these call each other */ 536 void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 537 int direction); 538 void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 539 int direction); 540 void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 541 int direction, int optindex, int opttype); 542 void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 543 int direction); 544 545 /* Given an smime entry, print its unique identifier. If GOLEFT is 546 * specified, print the cert<-subject<-smime map, else just print 547 * the smime entry. 548 */ 549 void 550 print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction) 551 { 552 certDBSubjectEntryMap *subjMap; 553 certDBEntryListNode *subjNode; 554 if (direction == GOLEFT) { 555 /* Need to output subject and cert first, see print_subject_graph */ 556 subjNode = smimeMap->pSubject; 557 if (map_handle_is_ok(info, (void *)subjNode, 1)) { 558 subjMap = (certDBSubjectEntryMap *)subjNode->appData; 559 print_subject_graph(info, subjMap, GOLEFT, 560 smimeMap->index, certDBEntryTypeSMimeProfile); 561 } else { 562 printnode(info, "<---- S/MIME %5d ", smimeMap->index); 563 info->dbErrors[NoSubjectForSMime]++; 564 } 565 } else { 566 printnode(info, "S/MIME %5d ", smimeMap->index); 567 } 568 } 569 570 /* Given a nickname entry, print its unique identifier. If GOLEFT is 571 * specified, print the cert<-subject<-nickname map, else just print 572 * the nickname entry. 573 */ 574 void 575 print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction) 576 { 577 certDBSubjectEntryMap *subjMap; 578 certDBEntryListNode *subjNode; 579 if (direction == GOLEFT) { 580 /* Need to output subject and cert first, see print_subject_graph */ 581 subjNode = nickMap->pSubject; 582 if (map_handle_is_ok(info, (void *)subjNode, 1)) { 583 subjMap = (certDBSubjectEntryMap *)subjNode->appData; 584 print_subject_graph(info, subjMap, GOLEFT, 585 nickMap->index, certDBEntryTypeNickname); 586 } else { 587 printnode(info, "<---- Nickname %5d ", nickMap->index); 588 info->dbErrors[NoSubjectForNickname]++; 589 } 590 } else { 591 printnode(info, "Nickname %5d ", nickMap->index); 592 } 593 } 594 595 /* Given a subject entry, if going right print the graph of the nickname|smime 596 * that it maps to (by its unique identifier); and if going left 597 * print the list of certs that it points to. 598 */ 599 void 600 print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 601 int direction, int optindex, int opttype) 602 { 603 certDBEntryMap *map; 604 certDBEntryListNode *node; 605 int i; 606 /* The first line of output always contains the cert id, subject id, 607 * and nickname|smime id. Subsequent lines may contain additional 608 * cert id's for the subject if going left or both directions. 609 * Ex. of printing the graph for a subject entry: 610 * Cert 3 <- Subject 5 -> Nickname 32 611 * Cert 8 / 612 * Cert 9 / 613 * means subject 5 has 3 certs, 3, 8, and 9, and corresponds 614 * to nickname entry 32. 615 * To accomplish the above, it is required to dump the entire first 616 * line left-to-right, regardless of the input direction, and then 617 * finish up any remaining cert entries. Hence the code is uglier 618 * than one may expect. 619 */ 620 if (direction == GOLEFT || direction == GOBOTH) { 621 /* In this case, nothing should be output until the first cert is 622 * located and output (cert 3 in the above example). 623 */ 624 if (subjMap->numCerts == 0 || subjMap->pCerts == NULL) 625 /* XXX uh-oh */ 626 return; 627 /* get the first cert and dump it. */ 628 node = subjMap->pCerts[0]; 629 if (map_handle_is_ok(info, (void *)node, 0)) { 630 map = (certDBEntryMap *)node->appData; 631 /* going left here stops. */ 632 print_cert_graph(info, map, GOLEFT); 633 } else { 634 info->dbErrors[SubjectHasNoKeyForCert]++; 635 } 636 /* Now it is safe to output the subject id. */ 637 if (direction == GOLEFT) 638 printnode(info, "Subject %5d <---- ", subjMap->index); 639 else /* direction == GOBOTH */ 640 printnode(info, "Subject %5d ----> ", subjMap->index); 641 } 642 if (direction == GORIGHT || direction == GOBOTH) { 643 /* Okay, now output the nickname|smime for this subject. */ 644 if (direction != GOBOTH) /* handled above */ 645 printnode(info, "Subject %5d ----> ", subjMap->index); 646 if (subjMap->pNickname) { 647 node = subjMap->pNickname; 648 if (map_handle_is_ok(info, (void *)node, 0)) { 649 map = (certDBEntryMap *)node->appData; 650 /* going right here stops. */ 651 print_nickname_graph(info, map, GORIGHT); 652 } 653 } 654 if (subjMap->pSMime) { 655 node = subjMap->pSMime; 656 if (map_handle_is_ok(info, (void *)node, 0)) { 657 map = (certDBEntryMap *)node->appData; 658 /* going right here stops. */ 659 print_smime_graph(info, map, GORIGHT); 660 } 661 } 662 if (!subjMap->pNickname && !subjMap->pSMime) { 663 printnode(info, "******************* ", -1); 664 info->dbErrors[NoNicknameOrSMimeForSubject]++; 665 } 666 if (subjMap->pNickname && subjMap->pSMime) { 667 info->dbErrors[NicknameAndSMimeEntries]++; 668 } 669 } 670 if (direction != GORIGHT) { /* going right has only one cert */ 671 if (opttype == certDBEntryTypeNickname) 672 printnode(info, "Nickname %5d ", optindex); 673 else if (opttype == certDBEntryTypeSMimeProfile) 674 printnode(info, "S/MIME %5d ", optindex); 675 for (i = 1 /* 1st one already done */; i < subjMap->numCerts; i++) { 676 printnode(info, "\n", -1); /* start a new line */ 677 node = subjMap->pCerts[i]; 678 if (map_handle_is_ok(info, (void *)node, 0)) { 679 map = (certDBEntryMap *)node->appData; 680 /* going left here stops. */ 681 print_cert_graph(info, map, GOLEFT); 682 printnode(info, "/", -1); 683 } 684 } 685 } 686 } 687 688 /* Given a cert entry, print its unique identifer. If GORIGHT is specified, 689 * print the cert->subject->nickname|smime map, else just print 690 * the cert entry. 691 */ 692 void 693 print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction) 694 { 695 certDBSubjectEntryMap *subjMap; 696 certDBEntryListNode *subjNode; 697 if (direction == GOLEFT) { 698 printnode(info, "Cert %5d <---- ", certMap->index); 699 /* only want cert entry, terminate here. */ 700 return; 701 } 702 /* Keep going right then. */ 703 printnode(info, "Cert %5d ----> ", certMap->index); 704 subjNode = certMap->pSubject; 705 if (map_handle_is_ok(info, (void *)subjNode, 0)) { 706 subjMap = (certDBSubjectEntryMap *)subjNode->appData; 707 print_subject_graph(info, subjMap, GORIGHT, -1, -1); 708 } else { 709 info->dbErrors[NoSubjectForCert]++; 710 } 711 } 712 713 SECStatus 714 computeDBGraph(certDBArray *dbArray, dbDebugInfo *info) 715 { 716 PRCList *cElem, *sElem, *nElem, *mElem; 717 certDBEntryListNode *node; 718 certDBEntryMap *map; 719 certDBSubjectEntryMap *subjMap; 720 721 /* Graph is of this form: 722 * 723 * certs: 724 * cert ---> subject ---> (nickname|smime) 725 * 726 * subjects: 727 * cert <--- subject ---> (nickname|smime) 728 * 729 * nicknames and smime: 730 * cert <--- subject <--- (nickname|smime) 731 */ 732 733 /* Print cert graph. */ 734 for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 735 cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) { 736 /* Print graph of everything to right of cert entry. */ 737 node = LISTNODE_CAST(cElem); 738 map = (certDBEntryMap *)node->appData; 739 print_cert_graph(info, map, GORIGHT); 740 printnode(info, "\n", -1); 741 } 742 printnode(info, "\n", -1); 743 744 /* Print subject graph. */ 745 for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); 746 sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { 747 /* Print graph of everything to both sides of subject entry. */ 748 node = LISTNODE_CAST(sElem); 749 subjMap = (certDBSubjectEntryMap *)node->appData; 750 print_subject_graph(info, subjMap, GOBOTH, -1, -1); 751 printnode(info, "\n", -1); 752 } 753 printnode(info, "\n", -1); 754 755 /* Print nickname graph. */ 756 for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link); 757 nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) { 758 /* Print graph of everything to left of nickname entry. */ 759 node = LISTNODE_CAST(nElem); 760 map = (certDBEntryMap *)node->appData; 761 print_nickname_graph(info, map, GOLEFT); 762 printnode(info, "\n", -1); 763 } 764 printnode(info, "\n", -1); 765 766 /* Print smime graph. */ 767 for (mElem = PR_LIST_HEAD(&dbArray->smime.link); 768 mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) { 769 /* Print graph of everything to left of smime entry. */ 770 node = LISTNODE_CAST(mElem); 771 if (node == NULL) 772 break; 773 map = (certDBEntryMap *)node->appData; 774 print_smime_graph(info, map, GOLEFT); 775 printnode(info, "\n", -1); 776 } 777 printnode(info, "\n", -1); 778 779 return SECSuccess; 780 } 781 782 /* 783 * List the entries in the db, showing handles between entry types. 784 */ 785 void 786 verboseOutput(certDBArray *dbArray, dbDebugInfo *info) 787 { 788 int i, ref; 789 PRCList *elem; 790 certDBEntryListNode *node; 791 certDBEntryMap *map; 792 certDBSubjectEntryMap *smap; 793 certDBEntrySubject *subjectEntry; 794 795 /* List certs */ 796 for (elem = PR_LIST_HEAD(&dbArray->certs.link); 797 elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) { 798 node = LISTNODE_CAST(elem); 799 map = (certDBEntryMap *)node->appData; 800 dumpCertEntry((certDBEntryCert *)&node->entry, map->index, info->out); 801 /* walk the cert handle to it's subject entry */ 802 if (map_handle_is_ok(info, map->pSubject, -1)) { 803 smap = (certDBSubjectEntryMap *)map->pSubject->appData; 804 ref = smap->index; 805 PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); 806 } else { 807 PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); 808 } 809 } 810 /* List subjects */ 811 for (elem = PR_LIST_HEAD(&dbArray->subjects.link); 812 elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) { 813 int refs = 0; 814 node = LISTNODE_CAST(elem); 815 subjectEntry = (certDBEntrySubject *)&node->entry; 816 smap = (certDBSubjectEntryMap *)node->appData; 817 dumpSubjectEntry(subjectEntry, smap->index, info->out); 818 /* iterate over subject's certs */ 819 for (i = 0; i < smap->numCerts; i++) { 820 /* walk each subject handle to it's cert entries */ 821 if (map_handle_is_ok(info, smap->pCerts[i], -1)) { 822 ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index; 823 PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref); 824 } else { 825 PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i); 826 } 827 } 828 if (subjectEntry->nickname) { 829 ++refs; 830 /* walk each subject handle to it's nickname entry */ 831 if (map_handle_is_ok(info, smap->pNickname, -1)) { 832 ref = ((certDBEntryMap *)smap->pNickname->appData)->index; 833 PR_fprintf(info->out, "-->(nickname %d)\n", ref); 834 } else { 835 PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n"); 836 } 837 } 838 if (subjectEntry->nemailAddrs && 839 subjectEntry->emailAddrs && 840 subjectEntry->emailAddrs[0] && 841 subjectEntry->emailAddrs[0][0]) { 842 ++refs; 843 /* walk each subject handle to it's smime entry */ 844 if (map_handle_is_ok(info, smap->pSMime, -1)) { 845 ref = ((certDBEntryMap *)smap->pSMime->appData)->index; 846 PR_fprintf(info->out, "-->(s/mime %d)\n", ref); 847 } else { 848 PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n"); 849 } 850 } 851 if (!refs) { 852 PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n"); 853 } 854 PR_fprintf(info->out, "\n\n"); 855 } 856 for (elem = PR_LIST_HEAD(&dbArray->nicknames.link); 857 elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) { 858 node = LISTNODE_CAST(elem); 859 map = (certDBEntryMap *)node->appData; 860 dumpNicknameEntry((certDBEntryNickname *)&node->entry, map->index, 861 info->out); 862 if (map_handle_is_ok(info, map->pSubject, -1)) { 863 ref = ((certDBEntryMap *)map->pSubject->appData)->index; 864 PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); 865 } else { 866 PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); 867 } 868 } 869 for (elem = PR_LIST_HEAD(&dbArray->smime.link); 870 elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) { 871 node = LISTNODE_CAST(elem); 872 map = (certDBEntryMap *)node->appData; 873 dumpSMimeEntry((certDBEntrySMime *)&node->entry, map->index, info->out); 874 if (map_handle_is_ok(info, map->pSubject, -1)) { 875 ref = ((certDBEntryMap *)map->pSubject->appData)->index; 876 PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); 877 } else { 878 PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); 879 } 880 } 881 PR_fprintf(info->out, "\n\n"); 882 } 883 884 /* A callback function, intended to be called from nsslowcert_TraverseDBEntries 885 * Builds a PRCList of DB entries of the specified type. 886 */ 887 SECStatus 888 SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, 889 certDBEntryType entryType, void *pdata) 890 { 891 certDBEntry *entry; 892 certDBEntryListNode *node; 893 PRCList *list = (PRCList *)pdata; 894 895 if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) { 896 PORT_SetError(SEC_ERROR_INVALID_ARGS); 897 return SECFailure; 898 } 899 entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL); 900 if (!entry) { 901 return SECSuccess; /* skip it */ 902 } 903 node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode); 904 if (!node) { 905 /* DestroyDBEntry(entry); */ 906 PLArenaPool *arena = entry->common.arena; 907 PORT_Memset(&entry->common, 0, sizeof entry->common); 908 PORT_FreeArena(arena, PR_FALSE); 909 return SECFailure; 910 } 911 node->entry = *entry; /* crude but effective. */ 912 PR_INIT_CLIST(&node->link); 913 PR_INSERT_BEFORE(&node->link, list); 914 return SECSuccess; 915 } 916 917 int 918 fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, 919 certDBEntryListNode *list) 920 { 921 PRCList *elem; 922 certDBEntryListNode *node; 923 certDBEntryMap *mnode; 924 certDBSubjectEntryMap *smnode; 925 PLArenaPool *arena; 926 int count = 0; 927 928 /* Initialize a dummy entry in the list. The list head will be the 929 * next element, so this element is skipped by for loops. 930 */ 931 PR_INIT_CLIST((PRCList *)list); 932 /* Collect all of the cert db entries for this type into a list. */ 933 nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list); 934 935 for (elem = PR_LIST_HEAD(&list->link); 936 elem != &list->link; elem = PR_NEXT_LINK(elem)) { 937 /* Iterate over the entries and ... */ 938 node = (certDBEntryListNode *)elem; 939 if (type != certDBEntryTypeSubject) { 940 arena = PORT_NewArena(sizeof(*mnode)); 941 mnode = PORT_ArenaZNew(arena, certDBEntryMap); 942 mnode->arena = arena; 943 /* ... assign a unique index number to each node, and ... */ 944 mnode->index = count; 945 /* ... set the map pointer for the node. */ 946 node->appData = (void *)mnode; 947 } else { 948 /* allocate some room for the cert pointers also */ 949 arena = PORT_NewArena(sizeof(*smnode) + 20 * sizeof(void *)); 950 smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap); 951 smnode->arena = arena; 952 smnode->index = count; 953 node->appData = (void *)smnode; 954 } 955 count++; 956 } 957 return count; 958 } 959 960 void 961 freeDBEntryList(PRCList *list) 962 { 963 PRCList *next, *elem; 964 certDBEntryListNode *node; 965 certDBEntryMap *map; 966 967 for (elem = PR_LIST_HEAD(list); elem != list;) { 968 next = PR_NEXT_LINK(elem); 969 node = (certDBEntryListNode *)elem; 970 map = (certDBEntryMap *)node->appData; 971 PR_REMOVE_LINK(&node->link); 972 PORT_FreeArena(map->arena, PR_TRUE); 973 PORT_FreeArena(node->entry.common.arena, PR_TRUE); 974 elem = next; 975 } 976 } 977 978 void 979 DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, 980 PRFileDesc *mailfile) 981 { 982 int i, nCertsFound, nSubjFound, nErr; 983 int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation; 984 PRCList *elem; 985 char c; 986 dbDebugInfo info; 987 certDBArray dbArray; 988 989 PORT_Memset(&dbArray, 0, sizeof(dbArray)); 990 PORT_Memset(&info, 0, sizeof(info)); 991 info.verbose = (PRBool)(out != NULL); 992 info.dograph = info.verbose; 993 info.out = (out) ? out : PR_STDOUT; 994 info.graphfile = mailfile ? mailfile : PR_STDOUT; 995 996 /* Fill the array structure with cert/subject/nickname/smime entries. */ 997 dbArray.numCerts = fillDBEntryArray(handle, certDBEntryTypeCert, 998 &dbArray.certs); 999 dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject, 1000 &dbArray.subjects); 1001 dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 1002 &dbArray.nicknames); 1003 dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 1004 &dbArray.smime); 1005 dbArray.numRevocation = fillDBEntryArray(handle, certDBEntryTypeRevocation, 1006 &dbArray.revocation); 1007 1008 /* Compute the map between the database entries. */ 1009 mapSubjectEntries(&dbArray); 1010 mapCertEntries(&dbArray); 1011 computeDBGraph(&dbArray, &info); 1012 1013 /* Store the totals for later reference. */ 1014 nCerts = dbArray.numCerts; 1015 nSubjects = dbArray.numSubjects; 1016 nNicknames = dbArray.numNicknames; 1017 nSMime = dbArray.numSMime; 1018 nRevocation = dbArray.numRevocation; 1019 nSubjCerts = 0; 1020 for (elem = PR_LIST_HEAD(&dbArray.subjects.link); 1021 elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) { 1022 certDBSubjectEntryMap *smap; 1023 smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData; 1024 nSubjCerts += smap->numCerts; 1025 } 1026 1027 if (info.verbose) { 1028 /* Dump the database contents. */ 1029 verboseOutput(&dbArray, &info); 1030 } 1031 1032 freeDBEntryList(&dbArray.certs.link); 1033 freeDBEntryList(&dbArray.subjects.link); 1034 freeDBEntryList(&dbArray.nicknames.link); 1035 freeDBEntryList(&dbArray.smime.link); 1036 freeDBEntryList(&dbArray.revocation.link); 1037 1038 PR_fprintf(info.out, "\n"); 1039 PR_fprintf(info.out, "Database statistics:\n"); 1040 PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", 1041 nCerts); 1042 PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", 1043 nSubjects); 1044 PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", 1045 nSubjCerts); 1046 PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", 1047 nNicknames); 1048 PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", 1049 nSMime); 1050 PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", 1051 nRevocation); 1052 PR_fprintf(info.out, "\n"); 1053 1054 nErr = 0; 1055 for (i = 0; i < NUM_ERROR_TYPES; i++) { 1056 PR_fprintf(info.out, "E%d: Found %4d %s\n", 1057 i, info.dbErrors[i], errResult[i]); 1058 nErr += info.dbErrors[i]; 1059 } 1060 PR_fprintf(info.out, "--------------\n Found %4d errors in database.\n", 1061 nErr); 1062 1063 PR_fprintf(info.out, "\nCertificates:\n"); 1064 PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, 1065 SubjectHasNoKeyForCert); 1066 nCertsFound = nSubjCerts + 1067 info.dbErrors[NoSubjectForCert] + 1068 info.dbErrors[SubjectHasNoKeyForCert]; 1069 c = (nCertsFound == nCerts) ? '=' : '!'; 1070 PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, 1071 info.dbErrors[NoSubjectForCert], 1072 info.dbErrors[SubjectHasNoKeyForCert]); 1073 PR_fprintf(info.out, "\nSubjects:\n"); 1074 PR_fprintf(info.out, 1075 "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n", 1076 NoNicknameOrSMimeForSubject, 1077 WrongNicknameForSubject, 1078 NoNicknameEntry, 1079 WrongSMimeForSubject, 1080 NoSMimeEntry, 1081 NoSubjectForNickname, 1082 NoSubjectForSMime, 1083 NicknameAndSMimeEntries); 1084 nSubjFound = nNicknames + nSMime + 1085 info.dbErrors[NoNicknameOrSMimeForSubject] + 1086 info.dbErrors[WrongNicknameForSubject] + 1087 info.dbErrors[NoNicknameEntry] + 1088 info.dbErrors[WrongSMimeForSubject] + 1089 info.dbErrors[NoSMimeEntry] - 1090 info.dbErrors[NoSubjectForNickname] - 1091 info.dbErrors[NoSubjectForSMime] - 1092 info.dbErrors[NicknameAndSMimeEntries]; 1093 c = (nSubjFound == nSubjects) ? '=' : '!'; 1094 PR_fprintf(info.out, 1095 "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n", 1096 nSubjects, c, nNicknames, nSMime, 1097 info.dbErrors[NoNicknameOrSMimeForSubject], 1098 info.dbErrors[WrongNicknameForSubject], 1099 info.dbErrors[NoNicknameEntry], 1100 info.dbErrors[WrongSMimeForSubject], 1101 info.dbErrors[NoSMimeEntry], 1102 info.dbErrors[NoSubjectForNickname], 1103 info.dbErrors[NoSubjectForSMime], 1104 info.dbErrors[NicknameAndSMimeEntries]); 1105 PR_fprintf(info.out, "\n"); 1106 } 1107 1108 #ifdef DORECOVER 1109 #include "dbrecover.c" 1110 #endif /* DORECOVER */ 1111 1112 enum { 1113 cmd_Debug = 0, 1114 cmd_LongUsage, 1115 cmd_Recover 1116 }; 1117 1118 enum { 1119 opt_KeepAll = 0, 1120 opt_CertDir, 1121 opt_Dumpfile, 1122 opt_InputDB, 1123 opt_OutputDB, 1124 opt_Mailfile, 1125 opt_Prompt, 1126 opt_KeepRedundant, 1127 opt_KeepNoSMimeProfile, 1128 opt_Verbose, 1129 opt_KeepExpired 1130 }; 1131 1132 static secuCommandFlag dbck_commands[] = { 1133 { /* cmd_Debug, */ 'D', PR_FALSE, 0, PR_FALSE }, 1134 { /* cmd_LongUsage,*/ 'H', PR_FALSE, 0, PR_FALSE }, 1135 { /* cmd_Recover, */ 'R', PR_FALSE, 0, PR_FALSE } 1136 }; 1137 1138 static secuCommandFlag dbck_options[] = { 1139 { /* opt_KeepAll, */ 'a', PR_FALSE, 0, PR_FALSE }, 1140 { /* opt_CertDir, */ 'd', PR_TRUE, 0, PR_FALSE }, 1141 { /* opt_Dumpfile, */ 'f', PR_TRUE, 0, PR_FALSE }, 1142 { /* opt_InputDB, */ 'i', PR_TRUE, 0, PR_FALSE }, 1143 { /* opt_OutputDB, */ 'o', PR_TRUE, 0, PR_FALSE }, 1144 { /* opt_Mailfile, */ 'm', PR_FALSE, 0, PR_FALSE }, 1145 { /* opt_Prompt, */ 'p', PR_FALSE, 0, PR_FALSE }, 1146 { /* opt_KeepRedundant, */ 'r', PR_FALSE, 0, PR_FALSE }, 1147 { /* opt_KeepNoSMimeProfile,*/ 's', PR_FALSE, 0, PR_FALSE }, 1148 { /* opt_Verbose, */ 'v', PR_FALSE, 0, PR_FALSE }, 1149 { /* opt_KeepExpired, */ 'x', PR_FALSE, 0, PR_FALSE } 1150 }; 1151 1152 #define CERT_DB_FMT "%s/cert%s.db" 1153 1154 static char * 1155 dbck_certdb_name_cb(void *arg, int dbVersion) 1156 { 1157 const char *configdir = (const char *)arg; 1158 const char *dbver; 1159 char *smpname = NULL; 1160 char *dbname = NULL; 1161 1162 switch (dbVersion) { 1163 case 8: 1164 dbver = "8"; 1165 break; 1166 case 7: 1167 dbver = "7"; 1168 break; 1169 case 6: 1170 dbver = "6"; 1171 break; 1172 case 5: 1173 dbver = "5"; 1174 break; 1175 case 4: 1176 default: 1177 dbver = ""; 1178 break; 1179 } 1180 1181 /* make sure we return something allocated with PORT_ so we have properly 1182 * matched frees at the end */ 1183 smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver); 1184 if (smpname) { 1185 dbname = PORT_Strdup(smpname); 1186 PR_smprintf_free(smpname); 1187 } 1188 return dbname; 1189 } 1190 1191 int 1192 main(int argc, char **argv) 1193 { 1194 NSSLOWCERTCertDBHandle *certHandle; 1195 1196 PRFileDesc *mailfile = NULL; 1197 PRFileDesc *dumpfile = NULL; 1198 1199 char *pathname = 0; 1200 char *fullname = 0; 1201 char *newdbname = 0; 1202 1203 PRBool removeExpired, requireProfile, singleEntry; 1204 SECStatus rv; 1205 secuCommand dbck; 1206 1207 dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag); 1208 dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag); 1209 dbck.commands = dbck_commands; 1210 dbck.options = dbck_options; 1211 1212 progName = strrchr(argv[0], '/'); 1213 progName = progName ? progName + 1 : argv[0]; 1214 1215 rv = SECU_ParseCommandLine(argc, argv, progName, &dbck); 1216 1217 if (rv != SECSuccess) 1218 Usage(progName); 1219 1220 if (dbck.commands[cmd_LongUsage].activated) 1221 LongUsage(progName); 1222 1223 if (!dbck.commands[cmd_Debug].activated && 1224 !dbck.commands[cmd_Recover].activated) { 1225 PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n"); 1226 Usage(progName); 1227 } 1228 1229 removeExpired = !(dbck.options[opt_KeepAll].activated || 1230 dbck.options[opt_KeepExpired].activated); 1231 1232 requireProfile = !(dbck.options[opt_KeepAll].activated || 1233 dbck.options[opt_KeepNoSMimeProfile].activated); 1234 1235 singleEntry = !(dbck.options[opt_KeepAll].activated || 1236 dbck.options[opt_KeepRedundant].activated); 1237 1238 if (dbck.options[opt_OutputDB].activated) { 1239 newdbname = PL_strdup(dbck.options[opt_OutputDB].arg); 1240 } else { 1241 newdbname = PL_strdup("new_cert8.db"); 1242 } 1243 1244 /* Create a generic graph of the database. */ 1245 if (dbck.options[opt_Mailfile].activated) { 1246 mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660); 1247 if (!mailfile) { 1248 fprintf(stderr, "Unable to create mailfile.\n"); 1249 return -1; 1250 } 1251 } 1252 1253 /* Dump all debugging info while running. */ 1254 if (dbck.options[opt_Verbose].activated) { 1255 if (dbck.options[opt_Dumpfile].activated) { 1256 dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg, 1257 PR_RDWR | PR_CREATE_FILE, 00660); 1258 if (!dumpfile) { 1259 fprintf(stderr, "Unable to create dumpfile.\n"); 1260 return -1; 1261 } 1262 } else { 1263 dumpfile = PR_STDOUT; 1264 } 1265 } 1266 1267 /* Set the cert database directory. */ 1268 if (dbck.options[opt_CertDir].activated) { 1269 SECU_ConfigDirectory(dbck.options[opt_CertDir].arg); 1270 } 1271 1272 pathname = SECU_ConfigDirectory(NULL); 1273 1274 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1275 rv = NSS_NoDB_Init(pathname); 1276 if (rv != SECSuccess) { 1277 fprintf(stderr, "NSS_NoDB_Init failed\n"); 1278 return -1; 1279 } 1280 1281 certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle); 1282 if (!certHandle) { 1283 SECU_PrintError(progName, "unable to get database handle"); 1284 return -1; 1285 } 1286 certHandle->ref = 1; 1287 1288 #ifdef NOTYET 1289 /* Open the possibly corrupt database. */ 1290 if (dbck.options[opt_InputDB].activated) { 1291 PRFileInfo fileInfo; 1292 fullname = PR_smprintf("%s/%s", pathname, 1293 dbck.options[opt_InputDB].arg); 1294 if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) { 1295 fprintf(stderr, "Unable to read file \"%s\".\n", fullname); 1296 return -1; 1297 } 1298 rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE); 1299 } else 1300 #endif 1301 { 1302 /* Use the default. */ 1303 #ifdef NOTYET 1304 fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION); 1305 if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) { 1306 fprintf(stderr, "Unable to read file \"%s\".\n", fullname); 1307 return -1; 1308 } 1309 #endif 1310 rv = nsslowcert_OpenCertDB(certHandle, 1311 PR_TRUE, /* readOnly */ 1312 NULL, /* rdb appName */ 1313 "", /* rdb prefix */ 1314 dbck_certdb_name_cb, /* namecb */ 1315 pathname, /* configDir */ 1316 PR_FALSE); /* volatile */ 1317 } 1318 1319 if (rv) { 1320 SECU_PrintError(progName, "unable to open cert database"); 1321 return -1; 1322 } 1323 1324 if (dbck.commands[cmd_Debug].activated) { 1325 DBCK_DebugDB(certHandle, dumpfile, mailfile); 1326 return 0; 1327 } 1328 1329 #ifdef DORECOVER 1330 if (dbck.commands[cmd_Recover].activated) { 1331 DBCK_ReconstructDBFromCerts(certHandle, newdbname, 1332 dumpfile, removeExpired, 1333 requireProfile, singleEntry, 1334 dbck.options[opt_Prompt].activated); 1335 return 0; 1336 } 1337 #endif 1338 1339 if (mailfile) 1340 PR_Close(mailfile); 1341 if (dumpfile) 1342 PR_Close(dumpfile); 1343 if (certHandle) { 1344 nsslowcert_ClosePermCertDB(certHandle); 1345 PORT_Free(certHandle); 1346 } 1347 return -1; 1348 }