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
Usage(char * progName)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
LongUsage(char * progName)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
printHexString(PRFileDesc * out,SECItem * hexval)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
dumpCertificate(CERTCertificate * cert,int num,PRFileDesc * outfile)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
dumpCertEntry(certDBEntryCert * entry,int num,PRFileDesc * outfile)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
dumpSubjectEntry(certDBEntrySubject * entry,int num,PRFileDesc * outfile)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
dumpNicknameEntry(certDBEntryNickname * entry,int num,PRFileDesc * outfile)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
dumpSMimeEntry(certDBEntrySMime * entry,int num,PRFileDesc * outfile)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
mapCertEntries(certDBArray * dbArray)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
mapSubjectEntries(certDBArray * dbArray)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
printnode(dbDebugInfo * info,const char * str,int num)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
map_handle_is_ok(dbDebugInfo * info,void * mapPtr,int indent)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
print_smime_graph(dbDebugInfo * info,certDBEntryMap * smimeMap,int direction)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
print_nickname_graph(dbDebugInfo * info,certDBEntryMap * nickMap,int direction)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
print_subject_graph(dbDebugInfo * info,certDBSubjectEntryMap * subjMap,int direction,int optindex,int opttype)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
print_cert_graph(dbDebugInfo * info,certDBEntryMap * certMap,int direction)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
computeDBGraph(certDBArray * dbArray,dbDebugInfo * info)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
verboseOutput(certDBArray * dbArray,dbDebugInfo * info)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
SEC_GetCertDBEntryList(SECItem * dbdata,SECItem * dbkey,certDBEntryType entryType,void * pdata)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
fillDBEntryArray(NSSLOWCERTCertDBHandle * handle,certDBEntryType type,certDBEntryListNode * list)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
freeDBEntryList(PRCList * list)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
DBCK_DebugDB(NSSLOWCERTCertDBHandle * handle,PRFileDesc * out,PRFileDesc * mailfile)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 {
1134 { /* cmd_Debug, */ 'D', PR_FALSE, 0, PR_FALSE },
1135 { /* cmd_LongUsage,*/ 'H', PR_FALSE, 0, PR_FALSE },
1136 { /* cmd_Recover, */ 'R', PR_FALSE, 0, PR_FALSE }
1137 };
1138
1139 static secuCommandFlag dbck_options[] =
1140 {
1141 { /* opt_KeepAll, */ 'a', PR_FALSE, 0, PR_FALSE },
1142 { /* opt_CertDir, */ 'd', PR_TRUE, 0, PR_FALSE },
1143 { /* opt_Dumpfile, */ 'f', PR_TRUE, 0, PR_FALSE },
1144 { /* opt_InputDB, */ 'i', PR_TRUE, 0, PR_FALSE },
1145 { /* opt_OutputDB, */ 'o', PR_TRUE, 0, PR_FALSE },
1146 { /* opt_Mailfile, */ 'm', PR_FALSE, 0, PR_FALSE },
1147 { /* opt_Prompt, */ 'p', PR_FALSE, 0, PR_FALSE },
1148 { /* opt_KeepRedundant, */ 'r', PR_FALSE, 0, PR_FALSE },
1149 { /* opt_KeepNoSMimeProfile,*/ 's', PR_FALSE, 0, PR_FALSE },
1150 { /* opt_Verbose, */ 'v', PR_FALSE, 0, PR_FALSE },
1151 { /* opt_KeepExpired, */ 'x', PR_FALSE, 0, PR_FALSE }
1152 };
1153
1154 #define CERT_DB_FMT "%s/cert%s.db"
1155
1156 static char *
dbck_certdb_name_cb(void * arg,int dbVersion)1157 dbck_certdb_name_cb(void *arg, int dbVersion)
1158 {
1159 const char *configdir = (const char *)arg;
1160 const char *dbver;
1161 char *smpname = NULL;
1162 char *dbname = NULL;
1163
1164 switch (dbVersion) {
1165 case 8:
1166 dbver = "8";
1167 break;
1168 case 7:
1169 dbver = "7";
1170 break;
1171 case 6:
1172 dbver = "6";
1173 break;
1174 case 5:
1175 dbver = "5";
1176 break;
1177 case 4:
1178 default:
1179 dbver = "";
1180 break;
1181 }
1182
1183 /* make sure we return something allocated with PORT_ so we have properly
1184 * matched frees at the end */
1185 smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
1186 if (smpname) {
1187 dbname = PORT_Strdup(smpname);
1188 PR_smprintf_free(smpname);
1189 }
1190 return dbname;
1191 }
1192
1193 int
main(int argc,char ** argv)1194 main(int argc, char **argv)
1195 {
1196 NSSLOWCERTCertDBHandle *certHandle;
1197
1198 PRFileDesc *mailfile = NULL;
1199 PRFileDesc *dumpfile = NULL;
1200
1201 char *pathname = 0;
1202 char *fullname = 0;
1203 char *newdbname = 0;
1204
1205 PRBool removeExpired, requireProfile, singleEntry;
1206 SECStatus rv;
1207 secuCommand dbck;
1208
1209 dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
1210 dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
1211 dbck.commands = dbck_commands;
1212 dbck.options = dbck_options;
1213
1214 progName = strrchr(argv[0], '/');
1215 progName = progName ? progName + 1 : argv[0];
1216
1217 rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
1218
1219 if (rv != SECSuccess)
1220 Usage(progName);
1221
1222 if (dbck.commands[cmd_LongUsage].activated)
1223 LongUsage(progName);
1224
1225 if (!dbck.commands[cmd_Debug].activated &&
1226 !dbck.commands[cmd_Recover].activated) {
1227 PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
1228 Usage(progName);
1229 }
1230
1231 removeExpired = !(dbck.options[opt_KeepAll].activated ||
1232 dbck.options[opt_KeepExpired].activated);
1233
1234 requireProfile = !(dbck.options[opt_KeepAll].activated ||
1235 dbck.options[opt_KeepNoSMimeProfile].activated);
1236
1237 singleEntry = !(dbck.options[opt_KeepAll].activated ||
1238 dbck.options[opt_KeepRedundant].activated);
1239
1240 if (dbck.options[opt_OutputDB].activated) {
1241 newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
1242 } else {
1243 newdbname = PL_strdup("new_cert8.db");
1244 }
1245
1246 /* Create a generic graph of the database. */
1247 if (dbck.options[opt_Mailfile].activated) {
1248 mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
1249 if (!mailfile) {
1250 fprintf(stderr, "Unable to create mailfile.\n");
1251 return -1;
1252 }
1253 }
1254
1255 /* Dump all debugging info while running. */
1256 if (dbck.options[opt_Verbose].activated) {
1257 if (dbck.options[opt_Dumpfile].activated) {
1258 dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
1259 PR_RDWR | PR_CREATE_FILE, 00660);
1260 if (!dumpfile) {
1261 fprintf(stderr, "Unable to create dumpfile.\n");
1262 return -1;
1263 }
1264 } else {
1265 dumpfile = PR_STDOUT;
1266 }
1267 }
1268
1269 /* Set the cert database directory. */
1270 if (dbck.options[opt_CertDir].activated) {
1271 SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
1272 }
1273
1274 pathname = SECU_ConfigDirectory(NULL);
1275
1276 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
1277 rv = NSS_NoDB_Init(pathname);
1278 if (rv != SECSuccess) {
1279 fprintf(stderr, "NSS_NoDB_Init failed\n");
1280 return -1;
1281 }
1282
1283 certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
1284 if (!certHandle) {
1285 SECU_PrintError(progName, "unable to get database handle");
1286 return -1;
1287 }
1288 certHandle->ref = 1;
1289
1290 #ifdef NOTYET
1291 /* Open the possibly corrupt database. */
1292 if (dbck.options[opt_InputDB].activated) {
1293 PRFileInfo fileInfo;
1294 fullname = PR_smprintf("%s/%s", pathname,
1295 dbck.options[opt_InputDB].arg);
1296 if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
1297 fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
1298 return -1;
1299 }
1300 rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
1301 } else
1302 #endif
1303 {
1304 /* Use the default. */
1305 #ifdef NOTYET
1306 fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
1307 if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
1308 fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
1309 return -1;
1310 }
1311 #endif
1312 rv = nsslowcert_OpenCertDB(certHandle,
1313 PR_TRUE, /* readOnly */
1314 NULL, /* rdb appName */
1315 "", /* rdb prefix */
1316 dbck_certdb_name_cb, /* namecb */
1317 pathname, /* configDir */
1318 PR_FALSE); /* volatile */
1319 }
1320
1321 if (rv) {
1322 SECU_PrintError(progName, "unable to open cert database");
1323 return -1;
1324 }
1325
1326 if (dbck.commands[cmd_Debug].activated) {
1327 DBCK_DebugDB(certHandle, dumpfile, mailfile);
1328 return 0;
1329 }
1330
1331 #ifdef DORECOVER
1332 if (dbck.commands[cmd_Recover].activated) {
1333 DBCK_ReconstructDBFromCerts(certHandle, newdbname,
1334 dumpfile, removeExpired,
1335 requireProfile, singleEntry,
1336 dbck.options[opt_Prompt].activated);
1337 return 0;
1338 }
1339 #endif
1340
1341 if (mailfile)
1342 PR_Close(mailfile);
1343 if (dumpfile)
1344 PR_Close(dumpfile);
1345 if (certHandle) {
1346 nsslowcert_ClosePermCertDB(certHandle);
1347 PORT_Free(certHandle);
1348 }
1349 return -1;
1350 }
1351