1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is the Netscape security libraries.
16
* The Initial Developer of the Original Code is
17
* Netscape Communications Corporation.
18
* Portions created by the Initial Developer are Copyright (C) 1994-2000
19
* the Initial Developer. All Rights Reserved.
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
35
* ***** END LICENSE BLOCK ***** */
40
** utility for fixing corrupt cert databases
57
static char *progName;
59
/* placeholders for pointer error types */
60
static void *WrongEntry;
61
static void *NoNickname;
65
/* 0*/ NoSubjectForCert = 0,
66
/* 1*/ SubjectHasNoKeyForCert,
67
/* 2*/ NoNicknameOrSMimeForSubject,
68
/* 3*/ WrongNicknameForSubject,
69
/* 4*/ NoNicknameEntry,
70
/* 5*/ WrongSMimeForSubject,
72
/* 7*/ NoSubjectForNickname,
73
/* 8*/ NoSubjectForSMime,
74
/* 9*/ NicknameAndSMimeEntries,
78
static char *dbErrorString[NUM_ERROR_TYPES] = {
79
/* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
80
/* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
81
/* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
82
/* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
83
/* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
84
/* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
85
/* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
86
/* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
87
/* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
90
static char *errResult[NUM_ERROR_TYPES] = {
91
"Certificate entries that had no subject entry.",
92
"Subject entries with no corresponding Certificate entries.",
93
"Subject entries that had no nickname or S/MIME entries.",
94
"Redundant nicknames (subjects with the same nickname).",
95
"Subject entries that had no nickname entry.",
96
"Redundant email addresses (subjects with the same email address).",
97
"Subject entries that had no S/MIME entry.",
98
"Nickname entries that had no subject entry.",
99
"S/MIME entries that had no subject entry.",
100
"Subject entries with BOTH nickname and S/MIME entries."
115
PRFileDesc *graphfile;
116
int dbErrors[NUM_ERROR_TYPES];
119
struct certDBEntryListNodeStr {
124
typedef struct certDBEntryListNodeStr certDBEntryListNode;
127
* A list node for a cert db entry. The index is a unique identifier
128
* to use for creating generic maps of a db. This struct handles
129
* the cert, nickname, and smime db entry types, as all three have a
130
* single handle to a subject entry.
131
* This structure is pointed to by certDBEntryListNode->appData.
137
certDBEntryListNode *pSubject;
141
* Subject entry is special case, it has bidirectional handles. One
142
* subject entry can point to several certs (using the same DN), and
143
* a nickname and/or smime entry.
144
* This structure is pointed to by certDBEntryListNode->appData.
151
certDBEntryListNode **pCerts;
152
certDBEntryListNode *pNickname;
153
certDBEntryListNode *pSMime;
154
} certDBSubjectEntryMap;
166
certDBEntryListNode certs; /* pointer to head of cert list */
167
certDBEntryListNode subjects; /* pointer to head of subject list */
168
certDBEntryListNode nicknames; /* pointer to head of nickname list */
169
certDBEntryListNode smime; /* pointer to head of smime list */
170
certDBEntryListNode revocation; /* pointer to head of revocation list */
173
/* Cast list to the base element, a certDBEntryListNode. */
174
#define LISTNODE_CAST(node) \
175
((certDBEntryListNode *)(node))
178
Usage(char *progName)
180
#define FPS fprintf(stderr,
181
FPS "Type %s -H for more detailed descriptions\n", progName);
182
FPS "Usage: %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n",
185
FPS " %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n",
192
LongUsage(char *progName)
194
FPS "%-15s Display this help message.\n",
196
FPS "%-15s Dump analysis. No changes will be made to the database.\n",
198
FPS "%-15s Cert database directory (default is ~/.netscape)\n",
200
FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
202
FPS "%-15s Verbose mode. Dumps the entire contents of your cert8.db.\n",
204
FPS "%-15s File to dump verbose output into. (default is stdout)\n",
207
FPS "%-15s Repair the database. The program will look for broken\n",
209
FPS "%-15s dependencies between subject entries and certificates,\n",
211
FPS "%-15s between nickname entries and subjects, and between SMIME\n",
213
FPS "%-15s profiles and subjects. Any duplicate entries will be\n",
215
FPS "%-15s removed, any missing entries will be created.\n",
217
FPS "%-15s File to store new database in (default is new_cert8.db)\n",
219
FPS "%-15s Cert database directory (default is ~/.netscape)\n",
221
FPS "%-15s Prompt before removing any certificates.\n",
223
FPS "%-15s Keep all possible certificates. Only remove certificates\n",
225
FPS "%-15s which prevent creation of a consistent database. Thus any\n",
227
FPS "%-15s expired or redundant entries will be kept.\n",
229
FPS "%-15s Keep redundant nickname/email entries. It is possible\n",
231
FPS "%-15s only one such entry will be usable.\n",
233
FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
235
FPS "%-15s cert. An empty profile will be created.\n",
237
FPS "%-15s Keep expired certificates.\n",
239
FPS "%-15s Verbose mode - report all activity while recovering db.\n",
241
FPS "%-15s File to dump verbose output into.\n",
249
/*******************************************************************
251
* Functions for dbck.
253
******************************************************************/
256
printHexString(PRFileDesc *out, SECItem *hexval)
259
for (i = 0; i < hexval->len; i++) {
260
if (i != hexval->len - 1) {
261
PR_fprintf(out, "%02x:", hexval->data[i]);
263
PR_fprintf(out, "%02x", hexval->data[i]);
266
PR_fprintf(out, "\n");
271
dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
274
CERTCertTrust *trust = cert->trust;
275
userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
276
(SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
277
(SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
279
PR_fprintf(outfile, "Certificate: %3d\n", num);
281
PR_fprintf(outfile, "Certificate:\n");
283
PR_fprintf(outfile, "----------------\n");
285
PR_fprintf(outfile, "(User Cert)\n");
286
PR_fprintf(outfile, "## SUBJECT: %s\n", cert->subjectName);
287
PR_fprintf(outfile, "## ISSUER: %s\n", cert->issuerName);
288
PR_fprintf(outfile, "## SERIAL NUMBER: ");
289
printHexString(outfile, &cert->serialNumber);
290
{ /* XXX should be separate function. */
291
int64 timeBefore, timeAfter;
292
PRExplodedTime beforePrintable, afterPrintable;
293
char *beforestr, *afterstr;
294
DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
295
DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
296
PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
297
PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
298
beforestr = PORT_Alloc(100);
299
afterstr = PORT_Alloc(100);
300
PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
301
PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
302
PR_fprintf(outfile, "## VALIDITY: %s to %s\n", beforestr, afterstr);
304
PR_fprintf(outfile, "\n");
309
dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
312
NSSLOWCERTCertificate *cert;
313
/* should we check for existing duplicates? */
314
cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert,
315
entry->cert.nickname);
317
CERTCertificate *cert;
318
cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
321
fprintf(stderr, "Failed to decode certificate.\n");
324
cert->trust = (CERTCertTrust *)&entry->trust;
325
dumpCertificate(cert, num, outfile);
326
CERT_DestroyCertificate(cert);
331
dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
333
char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
335
PR_fprintf(outfile, "Subject: %3d\n", num);
336
PR_fprintf(outfile, "------------\n");
337
PR_fprintf(outfile, "## %s\n", subjectName);
339
PR_fprintf(outfile, "## Subject nickname: %s\n", entry->nickname);
340
if (entry->emailAddrs) {
342
for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
343
char * emailAddr = entry->emailAddrs[n];
345
PR_fprintf(outfile, "## Subject email address: %s\n",
350
PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
351
PR_fprintf(outfile, "\n");
352
PORT_Free(subjectName);
357
dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
359
PR_fprintf(outfile, "Nickname: %3d\n", num);
360
PR_fprintf(outfile, "-------------\n");
361
PR_fprintf(outfile, "## \"%s\"\n\n", entry->nickname);
366
dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
368
PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
369
PR_fprintf(outfile, "-------------------\n");
370
PR_fprintf(outfile, "## \"%s\"\n", entry->emailAddr);
372
PR_fprintf(outfile, "## OPTIONS: ");
373
printHexString(outfile, &entry->smimeOptions);
374
PR_fprintf(outfile, "## TIMESTAMP: ");
375
printHexString(outfile, &entry->optionsDate);
377
SECU_PrintAny(stdout, &entry->smimeOptions, "## OPTIONS ", 0);
379
if (entry->optionsDate.len && entry->optionsDate.data)
380
PR_fprintf(outfile, "## TIMESTAMP: %.*s\n",
381
entry->optionsDate.len, entry->optionsDate.data);
383
PR_fprintf(outfile, "\n");
388
mapCertEntries(certDBArray *dbArray)
390
certDBEntryCert *certEntry;
391
certDBEntrySubject *subjectEntry;
392
certDBEntryListNode *certNode, *subjNode;
393
certDBSubjectEntryMap *smap;
395
PRArenaPool *tmparena;
398
PRCList *cElem, *sElem;
400
/* Arena for decoded entries */
401
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
402
if (tmparena == NULL) {
403
PORT_SetError(SEC_ERROR_NO_MEMORY);
407
/* Iterate over cert entries and map them to subject entries.
408
* NOTE: mapSubjectEntries must be called first to alloc memory
409
* for array of subject->cert map.
411
for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
412
cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
413
certNode = LISTNODE_CAST(cElem);
414
certEntry = (certDBEntryCert *)&certNode->entry;
415
map = (certDBEntryMap *)certNode->appData;
416
CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
417
CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
418
/* Loop over found subjects for cert's DN. */
419
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
420
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
421
subjNode = LISTNODE_CAST(sElem);
422
subjectEntry = (certDBEntrySubject *)&subjNode->entry;
423
if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
425
/* Found matching subject name, create link. */
426
map->pSubject = subjNode;
427
/* Make sure subject entry has cert's key. */
428
for (i=0; i<subjectEntry->ncerts; i++) {
429
if (SECITEM_ItemsAreEqual(&certKey,
430
&subjectEntry->certKeys[i])) {
431
/* Found matching cert key. */
432
smap = (certDBSubjectEntryMap *)subjNode->appData;
433
smap->pCerts[i] = certNode;
440
PORT_FreeArena(tmparena, PR_FALSE);
445
mapSubjectEntries(certDBArray *dbArray)
447
certDBEntrySubject *subjectEntry;
448
certDBEntryListNode *subjNode;
449
certDBSubjectEntryMap *subjMap;
452
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
453
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
454
/* Iterate over subject entries and map subjects to nickname
455
* and smime entries. The cert<->subject map will be handled
456
* by a subsequent call to mapCertEntries.
458
subjNode = LISTNODE_CAST(sElem);
459
subjectEntry = (certDBEntrySubject *)&subjNode->entry;
460
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
461
/* need to alloc memory here for array of matching certs. */
462
subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena,
463
subjectEntry->ncerts*sizeof(int));
464
subjMap->numCerts = subjectEntry->ncerts;
465
subjMap->pNickname = NoNickname;
466
subjMap->pSMime = NoSMime;
468
if (subjectEntry->nickname) {
469
/* Subject should have a nickname entry, so create a link. */
471
for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
472
nElem != &dbArray->nicknames.link;
473
nElem = PR_NEXT_LINK(nElem)) {
474
certDBEntryListNode *nickNode;
475
certDBEntryNickname *nicknameEntry;
476
/* Look for subject's nickname in nickname entries. */
477
nickNode = LISTNODE_CAST(nElem);
478
nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
479
if (PL_strcmp(subjectEntry->nickname,
480
nicknameEntry->nickname) == 0) {
481
/* Found a nickname entry for subject's nickname. */
482
if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
483
&nicknameEntry->subjectName)) {
484
certDBEntryMap *nickMap;
485
nickMap = (certDBEntryMap *)nickNode->appData;
486
/* Nickname and subject match. */
487
subjMap->pNickname = nickNode;
488
nickMap->pSubject = subjNode;
489
} else if (subjMap->pNickname == NoNickname) {
490
/* Nickname entry found is for diff. subject. */
491
subjMap->pNickname = WrongEntry;
496
if (subjectEntry->emailAddrs) {
498
for (n = 0; n < subjectEntry->nemailAddrs &&
499
subjectEntry->emailAddrs[n]; ++n) {
500
char * emailAddr = subjectEntry->emailAddrs[n];
503
/* Subject should have an smime entry, so create a link. */
504
for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
505
mElem != &dbArray->smime.link;
506
mElem = PR_NEXT_LINK(mElem)) {
507
certDBEntryListNode *smimeNode;
508
certDBEntrySMime *smimeEntry;
509
/* Look for subject's email in S/MIME entries. */
510
smimeNode = LISTNODE_CAST(mElem);
511
smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
512
if (PL_strcmp(emailAddr,
513
smimeEntry->emailAddr) == 0) {
514
/* Found a S/MIME entry for subject's email. */
515
if (SECITEM_ItemsAreEqual(
516
&subjectEntry->derSubject,
517
&smimeEntry->subjectName)) {
518
certDBEntryMap *smimeMap;
519
/* S/MIME entry and subject match. */
520
subjMap->pSMime = smimeNode;
521
smimeMap = (certDBEntryMap *)smimeNode->appData;
522
smimeMap->pSubject = subjNode;
523
} else if (subjMap->pSMime == NoSMime) {
524
/* S/MIME entry found is for diff. subject. */
525
subjMap->pSMime = WrongEntry;
529
} /* endif (emailAddr[0]) */
531
} /* endif (subjectEntry->emailAddrs) */
537
printnode(dbDebugInfo *info, const char *str, int num)
542
PR_fprintf(info->graphfile, str);
544
PR_fprintf(info->graphfile, str, num);
549
map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
551
if (mapPtr == NULL) {
553
printnode(info, " ", -1);
555
printnode(info, "******************* ", -1);
557
} else if (mapPtr == WrongEntry) {
559
printnode(info, " ", -1);
561
printnode(info, "??????????????????? ", -1);
568
/* these call each other */
569
void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap,
571
void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap,
573
void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap,
574
int direction, int optindex, int opttype);
575
void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap,
578
/* Given an smime entry, print its unique identifier. If GOLEFT is
579
* specified, print the cert<-subject<-smime map, else just print
583
print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
585
certDBSubjectEntryMap *subjMap;
586
certDBEntryListNode *subjNode;
587
if (direction == GOLEFT) {
588
/* Need to output subject and cert first, see print_subject_graph */
589
subjNode = smimeMap->pSubject;
590
if (map_handle_is_ok(info, (void *)subjNode, 1)) {
591
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
592
print_subject_graph(info, subjMap, GOLEFT,
593
smimeMap->index, certDBEntryTypeSMimeProfile);
595
printnode(info, "<---- S/MIME %5d ", smimeMap->index);
596
info->dbErrors[NoSubjectForSMime]++;
599
printnode(info, "S/MIME %5d ", smimeMap->index);
603
/* Given a nickname entry, print its unique identifier. If GOLEFT is
604
* specified, print the cert<-subject<-nickname map, else just print
605
* the nickname entry.
608
print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
610
certDBSubjectEntryMap *subjMap;
611
certDBEntryListNode *subjNode;
612
if (direction == GOLEFT) {
613
/* Need to output subject and cert first, see print_subject_graph */
614
subjNode = nickMap->pSubject;
615
if (map_handle_is_ok(info, (void *)subjNode, 1)) {
616
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
617
print_subject_graph(info, subjMap, GOLEFT,
618
nickMap->index, certDBEntryTypeNickname);
620
printnode(info, "<---- Nickname %5d ", nickMap->index);
621
info->dbErrors[NoSubjectForNickname]++;
624
printnode(info, "Nickname %5d ", nickMap->index);
628
/* Given a subject entry, if going right print the graph of the nickname|smime
629
* that it maps to (by its unique identifier); and if going left
630
* print the list of certs that it points to.
633
print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap,
634
int direction, int optindex, int opttype)
637
certDBEntryListNode *node;
639
/* The first line of output always contains the cert id, subject id,
640
* and nickname|smime id. Subsequent lines may contain additional
641
* cert id's for the subject if going left or both directions.
642
* Ex. of printing the graph for a subject entry:
643
* Cert 3 <- Subject 5 -> Nickname 32
646
* means subject 5 has 3 certs, 3, 8, and 9, and corresponds
647
* to nickname entry 32.
648
* To accomplish the above, it is required to dump the entire first
649
* line left-to-right, regardless of the input direction, and then
650
* finish up any remaining cert entries. Hence the code is uglier
651
* than one may expect.
653
if (direction == GOLEFT || direction == GOBOTH) {
654
/* In this case, nothing should be output until the first cert is
655
* located and output (cert 3 in the above example).
657
if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
660
/* get the first cert and dump it. */
661
node = subjMap->pCerts[0];
662
if (map_handle_is_ok(info, (void *)node, 0)) {
663
map = (certDBEntryMap *)node->appData;
664
/* going left here stops. */
665
print_cert_graph(info, map, GOLEFT);
667
info->dbErrors[SubjectHasNoKeyForCert]++;
669
/* Now it is safe to output the subject id. */
670
if (direction == GOLEFT)
671
printnode(info, "Subject %5d <---- ", subjMap->index);
672
else /* direction == GOBOTH */
673
printnode(info, "Subject %5d ----> ", subjMap->index);
675
if (direction == GORIGHT || direction == GOBOTH) {
676
/* Okay, now output the nickname|smime for this subject. */
677
if (direction != GOBOTH) /* handled above */
678
printnode(info, "Subject %5d ----> ", subjMap->index);
679
if (subjMap->pNickname) {
680
node = subjMap->pNickname;
681
if (map_handle_is_ok(info, (void *)node, 0)) {
682
map = (certDBEntryMap *)node->appData;
683
/* going right here stops. */
684
print_nickname_graph(info, map, GORIGHT);
687
if (subjMap->pSMime) {
688
node = subjMap->pSMime;
689
if (map_handle_is_ok(info, (void *)node, 0)) {
690
map = (certDBEntryMap *)node->appData;
691
/* going right here stops. */
692
print_smime_graph(info, map, GORIGHT);
695
if (!subjMap->pNickname && !subjMap->pSMime) {
696
printnode(info, "******************* ", -1);
697
info->dbErrors[NoNicknameOrSMimeForSubject]++;
699
if (subjMap->pNickname && subjMap->pSMime) {
700
info->dbErrors[NicknameAndSMimeEntries]++;
703
if (direction != GORIGHT) { /* going right has only one cert */
704
if (opttype == certDBEntryTypeNickname)
705
printnode(info, "Nickname %5d ", optindex);
706
else if (opttype == certDBEntryTypeSMimeProfile)
707
printnode(info, "S/MIME %5d ", optindex);
708
for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
709
printnode(info, "\n", -1); /* start a new line */
710
node = subjMap->pCerts[i];
711
if (map_handle_is_ok(info, (void *)node, 0)) {
712
map = (certDBEntryMap *)node->appData;
713
/* going left here stops. */
714
print_cert_graph(info, map, GOLEFT);
715
printnode(info, "/", -1);
721
/* Given a cert entry, print its unique identifer. If GORIGHT is specified,
722
* print the cert->subject->nickname|smime map, else just print
726
print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
728
certDBSubjectEntryMap *subjMap;
729
certDBEntryListNode *subjNode;
730
if (direction == GOLEFT) {
731
printnode(info, "Cert %5d <---- ", certMap->index);
732
/* only want cert entry, terminate here. */
735
/* Keep going right then. */
736
printnode(info, "Cert %5d ----> ", certMap->index);
737
subjNode = certMap->pSubject;
738
if (map_handle_is_ok(info, (void *)subjNode, 0)) {
739
subjMap = (certDBSubjectEntryMap *)subjNode->appData;
740
print_subject_graph(info, subjMap, GORIGHT, -1, -1);
742
info->dbErrors[NoSubjectForCert]++;
747
computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
749
PRCList *cElem, *sElem, *nElem, *mElem;
750
certDBEntryListNode *node;
752
certDBSubjectEntryMap *subjMap;
754
/* Graph is of this form:
757
* cert ---> subject ---> (nickname|smime)
760
* cert <--- subject ---> (nickname|smime)
762
* nicknames and smime:
763
* cert <--- subject <--- (nickname|smime)
766
/* Print cert graph. */
767
for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
768
cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
769
/* Print graph of everything to right of cert entry. */
770
node = LISTNODE_CAST(cElem);
771
map = (certDBEntryMap *)node->appData;
772
print_cert_graph(info, map, GORIGHT);
773
printnode(info, "\n", -1);
775
printnode(info, "\n", -1);
777
/* Print subject graph. */
778
for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
779
sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
780
/* Print graph of everything to both sides of subject entry. */
781
node = LISTNODE_CAST(sElem);
782
subjMap = (certDBSubjectEntryMap *)node->appData;
783
print_subject_graph(info, subjMap, GOBOTH, -1, -1);
784
printnode(info, "\n", -1);
786
printnode(info, "\n", -1);
788
/* Print nickname graph. */
789
for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
790
nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
791
/* Print graph of everything to left of nickname entry. */
792
node = LISTNODE_CAST(nElem);
793
map = (certDBEntryMap *)node->appData;
794
print_nickname_graph(info, map, GOLEFT);
795
printnode(info, "\n", -1);
797
printnode(info, "\n", -1);
799
/* Print smime graph. */
800
for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
801
mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
802
/* Print graph of everything to left of smime entry. */
803
node = LISTNODE_CAST(mElem);
804
if (node == NULL) break;
805
map = (certDBEntryMap *)node->appData;
806
print_smime_graph(info, map, GOLEFT);
807
printnode(info, "\n", -1);
809
printnode(info, "\n", -1);
815
* List the entries in the db, showing handles between entry types.
818
verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
822
certDBEntryListNode *node;
824
certDBSubjectEntryMap *smap;
825
certDBEntrySubject *subjectEntry;
828
for (elem = PR_LIST_HEAD(&dbArray->certs.link);
829
elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
830
node = LISTNODE_CAST(elem);
831
map = (certDBEntryMap *)node->appData;
832
dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
833
/* walk the cert handle to it's subject entry */
834
if (map_handle_is_ok(info, map->pSubject, -1)) {
835
smap = (certDBSubjectEntryMap *)map->pSubject->appData;
837
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
839
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
843
for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
844
elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
846
node = LISTNODE_CAST(elem);
847
subjectEntry = (certDBEntrySubject *)&node->entry;
848
smap = (certDBSubjectEntryMap *)node->appData;
849
dumpSubjectEntry(subjectEntry, smap->index, info->out);
850
/* iterate over subject's certs */
851
for (i=0; i<smap->numCerts; i++) {
852
/* walk each subject handle to it's cert entries */
853
if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
854
ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
855
PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
857
PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
860
if (subjectEntry->nickname) {
862
/* walk each subject handle to it's nickname entry */
863
if (map_handle_is_ok(info, smap->pNickname, -1)) {
864
ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
865
PR_fprintf(info->out, "-->(nickname %d)\n", ref);
867
PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
870
if (subjectEntry->nemailAddrs &&
871
subjectEntry->emailAddrs &&
872
subjectEntry->emailAddrs[0] &&
873
subjectEntry->emailAddrs[0][0]) {
875
/* walk each subject handle to it's smime entry */
876
if (map_handle_is_ok(info, smap->pSMime, -1)) {
877
ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
878
PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
880
PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
884
PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
886
PR_fprintf(info->out, "\n\n");
888
for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
889
elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
890
node = LISTNODE_CAST(elem);
891
map = (certDBEntryMap *)node->appData;
892
dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index,
894
if (map_handle_is_ok(info, map->pSubject, -1)) {
895
ref = ((certDBEntryMap *)map->pSubject->appData)->index;
896
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
898
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
901
for (elem = PR_LIST_HEAD(&dbArray->smime.link);
902
elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
903
node = LISTNODE_CAST(elem);
904
map = (certDBEntryMap *)node->appData;
905
dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
906
if (map_handle_is_ok(info, map->pSubject, -1)) {
907
ref = ((certDBEntryMap *)map->pSubject->appData)->index;
908
PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
910
PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
913
PR_fprintf(info->out, "\n\n");
917
/* A callback function, intended to be called from nsslowcert_TraverseDBEntries
918
* Builds a PRCList of DB entries of the specified type.
921
SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey,
922
certDBEntryType entryType, void *pdata)
925
certDBEntryListNode * node;
926
PRCList * list = (PRCList *)pdata;
928
if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
929
PORT_SetError(SEC_ERROR_INVALID_ARGS);
932
entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
934
return SECSuccess; /* skip it */
936
node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
938
/* DestroyDBEntry(entry); */
939
PLArenaPool *arena = entry->common.arena;
940
PORT_Memset(&entry->common, 0, sizeof entry->common);
941
PORT_FreeArena(arena, PR_FALSE);
944
node->entry = *entry; /* crude but effective. */
945
PR_INIT_CLIST(&node->link);
946
PR_INSERT_BEFORE(&node->link, list);
952
fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type,
953
certDBEntryListNode *list)
956
certDBEntryListNode *node;
957
certDBEntryMap *mnode;
958
certDBSubjectEntryMap *smnode;
962
/* Initialize a dummy entry in the list. The list head will be the
963
* next element, so this element is skipped by for loops.
965
PR_INIT_CLIST((PRCList *)list);
966
/* Collect all of the cert db entries for this type into a list. */
967
nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
969
for (elem = PR_LIST_HEAD(&list->link);
970
elem != &list->link; elem = PR_NEXT_LINK(elem)) {
971
/* Iterate over the entries and ... */
972
node = (certDBEntryListNode *)elem;
973
if (type != certDBEntryTypeSubject) {
974
arena = PORT_NewArena(sizeof(*mnode));
975
mnode = PORT_ArenaZNew(arena, certDBEntryMap);
976
mnode->arena = arena;
977
/* ... assign a unique index number to each node, and ... */
978
mnode->index = count;
979
/* ... set the map pointer for the node. */
980
node->appData = (void *)mnode;
982
/* allocate some room for the cert pointers also */
983
arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
984
smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
985
smnode->arena = arena;
986
smnode->index = count;
987
node->appData = (void *)smnode;
995
freeDBEntryList(PRCList *list)
997
PRCList *next, *elem;
998
certDBEntryListNode *node;
1001
for (elem = PR_LIST_HEAD(list); elem != list;) {
1002
next = PR_NEXT_LINK(elem);
1003
node = (certDBEntryListNode *)elem;
1004
map = (certDBEntryMap *)node->appData;
1005
PR_REMOVE_LINK(&node->link);
1006
PORT_FreeArena(map->arena, PR_TRUE);
1007
PORT_FreeArena(node->entry.common.arena, PR_TRUE);
1013
DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out,
1014
PRFileDesc *mailfile)
1016
int i, nCertsFound, nSubjFound, nErr;
1017
int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
1021
certDBArray dbArray;
1023
PORT_Memset(&dbArray, 0, sizeof(dbArray));
1024
PORT_Memset(&info, 0, sizeof(info));
1025
info.verbose = (PRBool)(out != NULL);
1026
info.dograph = info.verbose;
1027
info.out = (out) ? out : PR_STDOUT;
1028
info.graphfile = mailfile ? mailfile : PR_STDOUT;
1030
/* Fill the array structure with cert/subject/nickname/smime entries. */
1031
dbArray.numCerts = fillDBEntryArray(handle, certDBEntryTypeCert,
1033
dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject,
1035
dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname,
1036
&dbArray.nicknames);
1037
dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile,
1039
dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation,
1040
&dbArray.revocation);
1042
/* Compute the map between the database entries. */
1043
mapSubjectEntries(&dbArray);
1044
mapCertEntries(&dbArray);
1045
computeDBGraph(&dbArray, &info);
1047
/* Store the totals for later reference. */
1048
nCerts = dbArray.numCerts;
1049
nSubjects = dbArray.numSubjects;
1050
nNicknames = dbArray.numNicknames;
1051
nSMime = dbArray.numSMime;
1052
nRevocation= dbArray.numRevocation;
1054
for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
1055
elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
1056
certDBSubjectEntryMap *smap;
1057
smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
1058
nSubjCerts += smap->numCerts;
1062
/* Dump the database contents. */
1063
verboseOutput(&dbArray, &info);
1066
freeDBEntryList(&dbArray.certs.link);
1067
freeDBEntryList(&dbArray.subjects.link);
1068
freeDBEntryList(&dbArray.nicknames.link);
1069
freeDBEntryList(&dbArray.smime.link);
1070
freeDBEntryList(&dbArray.revocation.link);
1072
PR_fprintf(info.out, "\n");
1073
PR_fprintf(info.out, "Database statistics:\n");
1074
PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n",
1076
PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n",
1078
PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n",
1080
PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n",
1082
PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n",
1084
PR_fprintf(info.out, "N5: Found %4d CRL entries.\n",
1086
PR_fprintf(info.out, "\n");
1089
for (i=0; i < NUM_ERROR_TYPES; i++) {
1090
PR_fprintf(info.out, "E%d: Found %4d %s\n",
1091
i, info.dbErrors[i], errResult[i]);
1092
nErr += info.dbErrors[i];
1094
PR_fprintf(info.out, "--------------\n Found %4d errors in database.\n",
1097
PR_fprintf(info.out, "\nCertificates:\n");
1098
PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert,
1099
SubjectHasNoKeyForCert);
1100
nCertsFound = nSubjCerts +
1101
info.dbErrors[NoSubjectForCert] +
1102
info.dbErrors[SubjectHasNoKeyForCert];
1103
c = (nCertsFound == nCerts) ? '=' : '!';
1104
PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts,
1105
info.dbErrors[NoSubjectForCert],
1106
info.dbErrors[SubjectHasNoKeyForCert]);
1107
PR_fprintf(info.out, "\nSubjects:\n");
1108
PR_fprintf(info.out,
1109
"N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
1110
NoNicknameOrSMimeForSubject,
1111
WrongNicknameForSubject,
1113
WrongSMimeForSubject,
1115
NoSubjectForNickname,
1117
NicknameAndSMimeEntries);
1118
nSubjFound = nNicknames + nSMime +
1119
info.dbErrors[NoNicknameOrSMimeForSubject] +
1120
info.dbErrors[WrongNicknameForSubject] +
1121
info.dbErrors[NoNicknameEntry] +
1122
info.dbErrors[WrongSMimeForSubject] +
1123
info.dbErrors[NoSMimeEntry] -
1124
info.dbErrors[NoSubjectForNickname] -
1125
info.dbErrors[NoSubjectForSMime] -
1126
info.dbErrors[NicknameAndSMimeEntries];
1127
c = (nSubjFound == nSubjects) ? '=' : '!';
1128
PR_fprintf(info.out,
1129
"%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
1130
nSubjects, c, nNicknames, nSMime,
1131
info.dbErrors[NoNicknameOrSMimeForSubject],
1132
info.dbErrors[WrongNicknameForSubject],
1133
info.dbErrors[NoNicknameEntry],
1134
info.dbErrors[WrongSMimeForSubject],
1135
info.dbErrors[NoSMimeEntry],
1136
info.dbErrors[NoSubjectForNickname],
1137
info.dbErrors[NoSubjectForSMime],
1138
info.dbErrors[NicknameAndSMimeEntries]);
1139
PR_fprintf(info.out, "\n");
1143
#include "dbrecover.c"
1144
#endif /* DORECOVER */
1161
opt_KeepNoSMimeProfile,
1166
static secuCommandFlag dbck_commands[] =
1168
{ /* cmd_Debug, */ 'D', PR_FALSE, 0, PR_FALSE },
1169
{ /* cmd_LongUsage,*/ 'H', PR_FALSE, 0, PR_FALSE },
1170
{ /* cmd_Recover, */ 'R', PR_FALSE, 0, PR_FALSE }
1173
static secuCommandFlag dbck_options[] =
1175
{ /* opt_KeepAll, */ 'a', PR_FALSE, 0, PR_FALSE },
1176
{ /* opt_CertDir, */ 'd', PR_TRUE, 0, PR_FALSE },
1177
{ /* opt_Dumpfile, */ 'f', PR_TRUE, 0, PR_FALSE },
1178
{ /* opt_InputDB, */ 'i', PR_TRUE, 0, PR_FALSE },
1179
{ /* opt_OutputDB, */ 'o', PR_TRUE, 0, PR_FALSE },
1180
{ /* opt_Mailfile, */ 'm', PR_FALSE, 0, PR_FALSE },
1181
{ /* opt_Prompt, */ 'p', PR_FALSE, 0, PR_FALSE },
1182
{ /* opt_KeepRedundant, */ 'r', PR_FALSE, 0, PR_FALSE },
1183
{ /* opt_KeepNoSMimeProfile,*/ 's', PR_FALSE, 0, PR_FALSE },
1184
{ /* opt_Verbose, */ 'v', PR_FALSE, 0, PR_FALSE },
1185
{ /* opt_KeepExpired, */ 'x', PR_FALSE, 0, PR_FALSE }
1188
#define CERT_DB_FMT "%s/cert%s.db"
1191
dbck_certdb_name_cb(void *arg, int dbVersion)
1193
const char *configdir = (const char *)arg;
1195
char *smpname = NULL;
1196
char *dbname = NULL;
1198
switch (dbVersion) {
1217
/* make sure we return something allocated with PORT_ so we have properly
1218
* matched frees at the end */
1219
smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
1221
dbname = PORT_Strdup(smpname);
1222
PR_smprintf_free(smpname);
1229
main(int argc, char **argv)
1231
NSSLOWCERTCertDBHandle *certHandle;
1233
PRFileDesc *mailfile = NULL;
1234
PRFileDesc *dumpfile = NULL;
1236
char * pathname = 0;
1237
char * fullname = 0;
1238
char * newdbname = 0;
1240
PRBool removeExpired, requireProfile, singleEntry;
1244
dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
1245
dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
1246
dbck.commands = dbck_commands;
1247
dbck.options = dbck_options;
1249
progName = strrchr(argv[0], '/');
1250
progName = progName ? progName+1 : argv[0];
1252
rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
1254
if (rv != SECSuccess)
1257
if (dbck.commands[cmd_LongUsage].activated)
1258
LongUsage(progName);
1260
if (!dbck.commands[cmd_Debug].activated &&
1261
!dbck.commands[cmd_Recover].activated) {
1262
PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
1266
removeExpired = !(dbck.options[opt_KeepAll].activated ||
1267
dbck.options[opt_KeepExpired].activated);
1269
requireProfile = !(dbck.options[opt_KeepAll].activated ||
1270
dbck.options[opt_KeepNoSMimeProfile].activated);
1272
singleEntry = !(dbck.options[opt_KeepAll].activated ||
1273
dbck.options[opt_KeepRedundant].activated);
1275
if (dbck.options[opt_OutputDB].activated) {
1276
newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
1278
newdbname = PL_strdup("new_cert8.db");
1281
/* Create a generic graph of the database. */
1282
if (dbck.options[opt_Mailfile].activated) {
1283
mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
1285
fprintf(stderr, "Unable to create mailfile.\n");
1290
/* Dump all debugging info while running. */
1291
if (dbck.options[opt_Verbose].activated) {
1292
if (dbck.options[opt_Dumpfile].activated) {
1293
dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
1294
PR_RDWR | PR_CREATE_FILE, 00660);
1296
fprintf(stderr, "Unable to create dumpfile.\n");
1300
dumpfile = PR_STDOUT;
1304
/* Set the cert database directory. */
1305
if (dbck.options[opt_CertDir].activated) {
1306
SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
1309
pathname = SECU_ConfigDirectory(NULL);
1311
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
1312
rv = NSS_NoDB_Init(pathname);
1313
if (rv != SECSuccess) {
1314
fprintf(stderr, "NSS_NoDB_Init failed\n");
1318
certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
1320
SECU_PrintError(progName, "unable to get database handle");
1323
certHandle->ref = 1;
1326
/* Open the possibly corrupt database. */
1327
if (dbck.options[opt_InputDB].activated) {
1328
PRFileInfo fileInfo;
1329
fullname = PR_smprintf("%s/%s", pathname,
1330
dbck.options[opt_InputDB].arg);
1331
if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
1332
fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
1335
rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
1339
/* Use the default. */
1341
fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
1342
if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
1343
fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
1347
rv = nsslowcert_OpenCertDB(certHandle,
1348
PR_TRUE, /* readOnly */
1349
NULL, /* rdb appName */
1350
"", /* rdb prefix */
1351
dbck_certdb_name_cb, /* namecb */
1352
pathname, /* configDir */
1353
PR_FALSE); /* volatile */
1357
SECU_PrintError(progName, "unable to open cert database");
1361
if (dbck.commands[cmd_Debug].activated) {
1362
DBCK_DebugDB(certHandle, dumpfile, mailfile);
1367
if (dbck.commands[cmd_Recover].activated) {
1368
DBCK_ReconstructDBFromCerts(certHandle, newdbname,
1369
dumpfile, removeExpired,
1370
requireProfile, singleEntry,
1371
dbck.options[opt_Prompt].activated);
1381
nsslowcert_ClosePermCertDB(certHandle);
1382
PORT_Free(certHandle);