tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dbck.c (48770B)


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