nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / dbck / dbck.c
bloba1bba5b0e1e1fc117f07e3acfdc706e5a3932f8b
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
12 * License.
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.
21 * Contributor(s):
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 ***** */
38 ** dbck.c
40 ** utility for fixing corrupt cert databases
43 #include <stdio.h>
44 #include <string.h>
46 #include "secutil.h"
47 #include "cdbhdl.h"
48 #include "certdb.h"
49 #include "cert.h"
50 #include "nspr.h"
51 #include "prtypes.h"
52 #include "prtime.h"
53 #include "prlong.h"
54 #include "pcert.h"
55 #include "nss.h"
57 static char *progName;
59 /* placeholders for pointer error types */
60 static void *WrongEntry;
61 static void *NoNickname;
62 static void *NoSMime;
64 typedef enum {
65 /* 0*/ NoSubjectForCert = 0,
66 /* 1*/ SubjectHasNoKeyForCert,
67 /* 2*/ NoNicknameOrSMimeForSubject,
68 /* 3*/ WrongNicknameForSubject,
69 /* 4*/ NoNicknameEntry,
70 /* 5*/ WrongSMimeForSubject,
71 /* 6*/ NoSMimeEntry,
72 /* 7*/ NoSubjectForNickname,
73 /* 8*/ NoSubjectForSMime,
74 /* 9*/ NicknameAndSMimeEntries,
75 NUM_ERROR_TYPES
76 } dbErrorType;
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."
104 enum {
105 GOBOTH = 0,
106 GORIGHT,
107 GOLEFT
110 typedef struct
112 PRBool verbose;
113 PRBool dograph;
114 PRFileDesc *out;
115 PRFileDesc *graphfile;
116 int dbErrors[NUM_ERROR_TYPES];
117 } dbDebugInfo;
119 struct certDBEntryListNodeStr {
120 PRCList link;
121 certDBEntry entry;
122 void *appData;
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.
133 typedef struct
135 PRArenaPool *arena;
136 int index;
137 certDBEntryListNode *pSubject;
138 } certDBEntryMap;
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.
146 typedef struct
148 PRArenaPool *arena;
149 int index;
150 int numCerts;
151 certDBEntryListNode **pCerts;
152 certDBEntryListNode *pNickname;
153 certDBEntryListNode *pSMime;
154 } certDBSubjectEntryMap;
157 * A map of a certdb.
159 typedef struct
161 int numCerts;
162 int numSubjects;
163 int numNicknames;
164 int numSMime;
165 int numRevocation;
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 */
171 } certDBArray;
173 /* Cast list to the base element, a certDBEntryListNode. */
174 #define LISTNODE_CAST(node) \
175 ((certDBEntryListNode *)(node))
177 static void
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",
183 progName);
184 #ifdef DORECOVER
185 FPS " %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n",
186 progName);
187 #endif
188 exit(-1);
191 static void
192 LongUsage(char *progName)
194 FPS "%-15s Display this help message.\n",
195 "-H");
196 FPS "%-15s Dump analysis. No changes will be made to the database.\n",
197 "-D");
198 FPS "%-15s Cert database directory (default is ~/.netscape)\n",
199 " -d certdir");
200 FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
201 " -m");
202 FPS "%-15s Verbose mode. Dumps the entire contents of your cert8.db.\n",
203 " -v");
204 FPS "%-15s File to dump verbose output into. (default is stdout)\n",
205 " -f dumpfile");
206 #ifdef DORECOVER
207 FPS "%-15s Repair the database. The program will look for broken\n",
208 "-R");
209 FPS "%-15s dependencies between subject entries and certificates,\n",
210 "");
211 FPS "%-15s between nickname entries and subjects, and between SMIME\n",
212 "");
213 FPS "%-15s profiles and subjects. Any duplicate entries will be\n",
214 "");
215 FPS "%-15s removed, any missing entries will be created.\n",
216 "");
217 FPS "%-15s File to store new database in (default is new_cert8.db)\n",
218 " -o newdbname");
219 FPS "%-15s Cert database directory (default is ~/.netscape)\n",
220 " -d certdir");
221 FPS "%-15s Prompt before removing any certificates.\n",
222 " -p");
223 FPS "%-15s Keep all possible certificates. Only remove certificates\n",
224 " -a");
225 FPS "%-15s which prevent creation of a consistent database. Thus any\n",
226 "");
227 FPS "%-15s expired or redundant entries will be kept.\n",
228 "");
229 FPS "%-15s Keep redundant nickname/email entries. It is possible\n",
230 " -r");
231 FPS "%-15s only one such entry will be usable.\n",
232 "");
233 FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
234 " -s");
235 FPS "%-15s cert. An empty profile will be created.\n",
236 "");
237 FPS "%-15s Keep expired certificates.\n",
238 " -x");
239 FPS "%-15s Verbose mode - report all activity while recovering db.\n",
240 " -v");
241 FPS "%-15s File to dump verbose output into.\n",
242 " -f dumpfile");
243 FPS "\n");
244 #endif
245 exit(-1);
246 #undef FPS
249 /*******************************************************************
251 * Functions for dbck.
253 ******************************************************************/
255 void
256 printHexString(PRFileDesc *out, SECItem *hexval)
258 unsigned int i;
259 for (i = 0; i < hexval->len; i++) {
260 if (i != hexval->len - 1) {
261 PR_fprintf(out, "%02x:", hexval->data[i]);
262 } else {
263 PR_fprintf(out, "%02x", hexval->data[i]);
266 PR_fprintf(out, "\n");
270 SECStatus
271 dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
273 int userCert = 0;
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);
278 if (num >= 0) {
279 PR_fprintf(outfile, "Certificate: %3d\n", num);
280 } else {
281 PR_fprintf(outfile, "Certificate:\n");
283 PR_fprintf(outfile, "----------------\n");
284 if (userCert)
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");
305 return SECSuccess;
308 SECStatus
309 dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
311 #if 0
312 NSSLOWCERTCertificate *cert;
313 /* should we check for existing duplicates? */
314 cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert,
315 entry->cert.nickname);
316 #else
317 CERTCertificate *cert;
318 cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
319 #endif
320 if (!cert) {
321 fprintf(stderr, "Failed to decode certificate.\n");
322 return SECFailure;
324 cert->trust = (CERTCertTrust *)&entry->trust;
325 dumpCertificate(cert, num, outfile);
326 CERT_DestroyCertificate(cert);
327 return SECSuccess;
330 SECStatus
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);
338 if (entry->nickname)
339 PR_fprintf(outfile, "## Subject nickname: %s\n", entry->nickname);
340 if (entry->emailAddrs) {
341 unsigned int n;
342 for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
343 char * emailAddr = entry->emailAddrs[n];
344 if (emailAddr[0]) {
345 PR_fprintf(outfile, "## Subject email address: %s\n",
346 emailAddr);
350 PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
351 PR_fprintf(outfile, "\n");
352 PORT_Free(subjectName);
353 return SECSuccess;
356 SECStatus
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);
362 return SECSuccess;
365 SECStatus
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);
371 #ifdef OLDWAY
372 PR_fprintf(outfile, "## OPTIONS: ");
373 printHexString(outfile, &entry->smimeOptions);
374 PR_fprintf(outfile, "## TIMESTAMP: ");
375 printHexString(outfile, &entry->optionsDate);
376 #else
377 SECU_PrintAny(stdout, &entry->smimeOptions, "## OPTIONS ", 0);
378 fflush(stdout);
379 if (entry->optionsDate.len && entry->optionsDate.data)
380 PR_fprintf(outfile, "## TIMESTAMP: %.*s\n",
381 entry->optionsDate.len, entry->optionsDate.data);
382 #endif
383 PR_fprintf(outfile, "\n");
384 return SECSuccess;
387 SECStatus
388 mapCertEntries(certDBArray *dbArray)
390 certDBEntryCert *certEntry;
391 certDBEntrySubject *subjectEntry;
392 certDBEntryListNode *certNode, *subjNode;
393 certDBSubjectEntryMap *smap;
394 certDBEntryMap *map;
395 PRArenaPool *tmparena;
396 SECItem derSubject;
397 SECItem certKey;
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);
404 return SECFailure;
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)) {
424 unsigned int i;
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;
434 break;
440 PORT_FreeArena(tmparena, PR_FALSE);
441 return SECSuccess;
444 SECStatus
445 mapSubjectEntries(certDBArray *dbArray)
447 certDBEntrySubject *subjectEntry;
448 certDBEntryListNode *subjNode;
449 certDBSubjectEntryMap *subjMap;
450 PRCList *sElem;
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. */
470 PRCList *nElem;
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) {
497 unsigned int n;
498 for (n = 0; n < subjectEntry->nemailAddrs &&
499 subjectEntry->emailAddrs[n]; ++n) {
500 char * emailAddr = subjectEntry->emailAddrs[n];
501 if (emailAddr[0]) {
502 PRCList *mElem;
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;
528 } /* end for */
529 } /* endif (emailAddr[0]) */
530 } /* end for */
531 } /* endif (subjectEntry->emailAddrs) */
533 return SECSuccess;
536 void
537 printnode(dbDebugInfo *info, const char *str, int num)
539 if (!info->dograph)
540 return;
541 if (num < 0) {
542 PR_fprintf(info->graphfile, str);
543 } else {
544 PR_fprintf(info->graphfile, str, num);
548 PRBool
549 map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
551 if (mapPtr == NULL) {
552 if (indent > 0)
553 printnode(info, " ", -1);
554 if (indent >= 0)
555 printnode(info, "******************* ", -1);
556 return PR_FALSE;
557 } else if (mapPtr == WrongEntry) {
558 if (indent > 0)
559 printnode(info, " ", -1);
560 if (indent >= 0)
561 printnode(info, "??????????????????? ", -1);
562 return PR_FALSE;
563 } else {
564 return PR_TRUE;
568 /* these call each other */
569 void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap,
570 int direction);
571 void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap,
572 int direction);
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,
576 int direction);
578 /* Given an smime entry, print its unique identifier. If GOLEFT is
579 * specified, print the cert<-subject<-smime map, else just print
580 * the smime entry.
582 void
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);
594 } else {
595 printnode(info, "<---- S/MIME %5d ", smimeMap->index);
596 info->dbErrors[NoSubjectForSMime]++;
598 } else {
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.
607 void
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);
619 } else {
620 printnode(info, "<---- Nickname %5d ", nickMap->index);
621 info->dbErrors[NoSubjectForNickname]++;
623 } else {
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.
632 void
633 print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap,
634 int direction, int optindex, int opttype)
636 certDBEntryMap *map;
637 certDBEntryListNode *node;
638 int i;
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
644 * Cert 8 /
645 * Cert 9 /
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)
658 /* XXX uh-oh */
659 return;
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);
666 } else {
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
723 * the cert entry.
725 void
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. */
733 return;
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);
741 } else {
742 info->dbErrors[NoSubjectForCert]++;
746 SECStatus
747 computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
749 PRCList *cElem, *sElem, *nElem, *mElem;
750 certDBEntryListNode *node;
751 certDBEntryMap *map;
752 certDBSubjectEntryMap *subjMap;
754 /* Graph is of this form:
756 * certs:
757 * cert ---> subject ---> (nickname|smime)
759 * subjects:
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);
811 return SECSuccess;
815 * List the entries in the db, showing handles between entry types.
817 void
818 verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
820 int i, ref;
821 PRCList *elem;
822 certDBEntryListNode *node;
823 certDBEntryMap *map;
824 certDBSubjectEntryMap *smap;
825 certDBEntrySubject *subjectEntry;
827 /* List certs */
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;
836 ref = smap->index;
837 PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
838 } else {
839 PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
842 /* List subjects */
843 for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
844 elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
845 int refs = 0;
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);
856 } else {
857 PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
860 if (subjectEntry->nickname) {
861 ++refs;
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);
866 } else {
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]) {
874 ++refs;
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);
879 } else {
880 PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
883 if (!refs) {
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,
893 info->out);
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);
897 } else {
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);
909 } else {
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.
920 SECStatus
921 SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey,
922 certDBEntryType entryType, void *pdata)
924 certDBEntry * entry;
925 certDBEntryListNode * node;
926 PRCList * list = (PRCList *)pdata;
928 if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
929 PORT_SetError(SEC_ERROR_INVALID_ARGS);
930 return SECFailure;
932 entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
933 if (!entry) {
934 return SECSuccess; /* skip it */
936 node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
937 if (!node) {
938 /* DestroyDBEntry(entry); */
939 PLArenaPool *arena = entry->common.arena;
940 PORT_Memset(&entry->common, 0, sizeof entry->common);
941 PORT_FreeArena(arena, PR_FALSE);
942 return SECFailure;
944 node->entry = *entry; /* crude but effective. */
945 PR_INIT_CLIST(&node->link);
946 PR_INSERT_BEFORE(&node->link, list);
947 return SECSuccess;
952 fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type,
953 certDBEntryListNode *list)
955 PRCList *elem;
956 certDBEntryListNode *node;
957 certDBEntryMap *mnode;
958 certDBSubjectEntryMap *smnode;
959 PRArenaPool *arena;
960 int count = 0;
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;
981 } else {
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;
989 count++;
991 return count;
994 void
995 freeDBEntryList(PRCList *list)
997 PRCList *next, *elem;
998 certDBEntryListNode *node;
999 certDBEntryMap *map;
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);
1008 elem = next;
1012 void
1013 DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out,
1014 PRFileDesc *mailfile)
1016 int i, nCertsFound, nSubjFound, nErr;
1017 int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
1018 PRCList *elem;
1019 char c;
1020 dbDebugInfo info;
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,
1032 &dbArray.certs);
1033 dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject,
1034 &dbArray.subjects);
1035 dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname,
1036 &dbArray.nicknames);
1037 dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile,
1038 &dbArray.smime);
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;
1053 nSubjCerts = 0;
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;
1061 if (info.verbose) {
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",
1075 nCerts);
1076 PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n",
1077 nSubjects);
1078 PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n",
1079 nSubjCerts);
1080 PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n",
1081 nNicknames);
1082 PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n",
1083 nSMime);
1084 PR_fprintf(info.out, "N5: Found %4d CRL entries.\n",
1085 nRevocation);
1086 PR_fprintf(info.out, "\n");
1088 nErr = 0;
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",
1095 nErr);
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,
1112 NoNicknameEntry,
1113 WrongSMimeForSubject,
1114 NoSMimeEntry,
1115 NoSubjectForNickname,
1116 NoSubjectForSMime,
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");
1142 #ifdef DORECOVER
1143 #include "dbrecover.c"
1144 #endif /* DORECOVER */
1146 enum {
1147 cmd_Debug = 0,
1148 cmd_LongUsage,
1149 cmd_Recover
1152 enum {
1153 opt_KeepAll = 0,
1154 opt_CertDir,
1155 opt_Dumpfile,
1156 opt_InputDB,
1157 opt_OutputDB,
1158 opt_Mailfile,
1159 opt_Prompt,
1160 opt_KeepRedundant,
1161 opt_KeepNoSMimeProfile,
1162 opt_Verbose,
1163 opt_KeepExpired
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"
1190 static char *
1191 dbck_certdb_name_cb(void *arg, int dbVersion)
1193 const char *configdir = (const char *)arg;
1194 const char *dbver;
1195 char *smpname = NULL;
1196 char *dbname = NULL;
1198 switch (dbVersion) {
1199 case 8:
1200 dbver = "8";
1201 break;
1202 case 7:
1203 dbver = "7";
1204 break;
1205 case 6:
1206 dbver = "6";
1207 break;
1208 case 5:
1209 dbver = "5";
1210 break;
1211 case 4:
1212 default:
1213 dbver = "";
1214 break;
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);
1220 if (smpname) {
1221 dbname = PORT_Strdup(smpname);
1222 PR_smprintf_free(smpname);
1224 return dbname;
1228 int
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;
1241 SECStatus rv;
1242 secuCommand dbck;
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)
1255 Usage(progName);
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");
1263 Usage(progName);
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);
1277 } else {
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);
1284 if (!mailfile) {
1285 fprintf(stderr, "Unable to create mailfile.\n");
1286 return -1;
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);
1295 if (!dumpfile) {
1296 fprintf(stderr, "Unable to create dumpfile.\n");
1297 return -1;
1299 } else {
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");
1315 return -1;
1318 certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
1319 if (!certHandle) {
1320 SECU_PrintError(progName, "unable to get database handle");
1321 return -1;
1323 certHandle->ref = 1;
1325 #ifdef NOTYET
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);
1333 return -1;
1335 rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
1336 } else
1337 #endif
1339 /* Use the default. */
1340 #ifdef NOTYET
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);
1344 return -1;
1346 #endif
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 */
1356 if (rv) {
1357 SECU_PrintError(progName, "unable to open cert database");
1358 return -1;
1361 if (dbck.commands[cmd_Debug].activated) {
1362 DBCK_DebugDB(certHandle, dumpfile, mailfile);
1363 return 0;
1366 #ifdef DORECOVER
1367 if (dbck.commands[cmd_Recover].activated) {
1368 DBCK_ReconstructDBFromCerts(certHandle, newdbname,
1369 dumpfile, removeExpired,
1370 requireProfile, singleEntry,
1371 dbck.options[opt_Prompt].activated);
1372 return 0;
1374 #endif
1376 if (mailfile)
1377 PR_Close(mailfile);
1378 if (dumpfile)
1379 PR_Close(dumpfile);
1380 if (certHandle) {
1381 nsslowcert_ClosePermCertDB(certHandle);
1382 PORT_Free(certHandle);
1384 return -1;