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