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