Back to index

lightning-sunbird  0.9+nobinonly
dbck.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038 ** dbck.c
00039 **
00040 ** utility for fixing corrupt cert databases
00041 **
00042 */
00043 #include <stdio.h>
00044 #include <string.h>
00045 
00046 #include "secutil.h"
00047 #include "cdbhdl.h"
00048 #include "certdb.h"
00049 #include "cert.h"
00050 #include "nspr.h"
00051 #include "prtypes.h"
00052 #include "prtime.h"
00053 #include "prlong.h"
00054 #include "pcert.h"
00055 #include "nss.h"
00056 
00057 static char *progName;
00058 
00059 /* placeholders for pointer error types */
00060 static void *WrongEntry;
00061 static void *NoNickname;
00062 static void *NoSMime;
00063 
00064 typedef enum {
00065 /* 0*/ NoSubjectForCert = 0,
00066 /* 1*/ SubjectHasNoKeyForCert,
00067 /* 2*/ NoNicknameOrSMimeForSubject,
00068 /* 3*/ WrongNicknameForSubject,
00069 /* 4*/ NoNicknameEntry,
00070 /* 5*/ WrongSMimeForSubject,
00071 /* 6*/ NoSMimeEntry,
00072 /* 7*/ NoSubjectForNickname,
00073 /* 8*/ NoSubjectForSMime,
00074 /* 9*/ NicknameAndSMimeEntries,
00075     NUM_ERROR_TYPES
00076 } dbErrorType;
00077 
00078 static char *dbErrorString[NUM_ERROR_TYPES] = {
00079 /* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
00080 /* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
00081 /* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
00082 /* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
00083 /* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
00084 /* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
00085 /* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
00086 /* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
00087 /* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
00088 };
00089 
00090 static char *errResult[NUM_ERROR_TYPES] = {
00091     "Certificate entries that had no subject entry.", 
00092     "Subject entries with no corresponding Certificate entries.", 
00093     "Subject entries that had no nickname or S/MIME entries.",
00094     "Redundant nicknames (subjects with the same nickname).",
00095     "Subject entries that had no nickname entry.",
00096     "Redundant email addresses (subjects with the same email address).",
00097     "Subject entries that had no S/MIME entry.",
00098     "Nickname entries that had no subject entry.", 
00099     "S/MIME entries that had no subject entry.",
00100     "Subject entries with BOTH nickname and S/MIME entries."
00101 };
00102 
00103 
00104 enum {
00105     GOBOTH = 0,
00106     GORIGHT,
00107     GOLEFT
00108 };
00109 
00110 typedef struct
00111 {
00112     PRBool verbose;
00113     PRBool dograph;
00114     PRFileDesc *out;
00115     PRFileDesc *graphfile;
00116     int dbErrors[NUM_ERROR_TYPES];
00117 } dbDebugInfo;
00118 
00119 struct certDBEntryListNodeStr {
00120     PRCList link;
00121     certDBEntry entry;
00122     void *appData;
00123 };
00124 typedef struct certDBEntryListNodeStr  certDBEntryListNode;
00125 
00126 /*
00127  * A list node for a cert db entry.  The index is a unique identifier
00128  * to use for creating generic maps of a db.  This struct handles
00129  * the cert, nickname, and smime db entry types, as all three have a
00130  * single handle to a subject entry.
00131  * This structure is pointed to by certDBEntryListNode->appData.
00132  */
00133 typedef struct 
00134 {
00135     PRArenaPool *arena;
00136     int index;
00137     certDBEntryListNode *pSubject;
00138 } certDBEntryMap;
00139 
00140 /*
00141  * Subject entry is special case, it has bidirectional handles.  One
00142  * subject entry can point to several certs (using the same DN), and
00143  * a nickname and/or smime entry.
00144  * This structure is pointed to by certDBEntryListNode->appData.
00145  */
00146 typedef struct
00147 {
00148     PRArenaPool *arena;
00149     int index;
00150     int numCerts;
00151     certDBEntryListNode **pCerts;
00152     certDBEntryListNode *pNickname;
00153     certDBEntryListNode *pSMime;
00154 } certDBSubjectEntryMap;
00155 
00156 /*
00157  * A map of a certdb.
00158  */
00159 typedef struct
00160 {
00161     int numCerts;
00162     int numSubjects;
00163     int numNicknames;
00164     int numSMime;
00165     int numRevocation;
00166     certDBEntryListNode certs;      /* pointer to head of cert list */
00167     certDBEntryListNode subjects;   /* pointer to head of subject list */
00168     certDBEntryListNode nicknames;  /* pointer to head of nickname list */
00169     certDBEntryListNode smime;      /* pointer to head of smime list */
00170     certDBEntryListNode revocation; /* pointer to head of revocation list */
00171 } certDBArray;
00172 
00173 /* Cast list to the base element, a certDBEntryListNode. */
00174 #define LISTNODE_CAST(node) \
00175     ((certDBEntryListNode *)(node))
00176 
00177 static void 
00178 Usage(char *progName)
00179 {
00180 #define FPS fprintf(stderr, 
00181     FPS "Type %s -H for more detailed descriptions\n", progName);
00182     FPS "Usage:  %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", 
00183        progName);
00184 #ifdef DORECOVER
00185     FPS "        %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", 
00186        progName);
00187 #endif
00188     exit(-1);
00189 }
00190 
00191 static void
00192 LongUsage(char *progName)
00193 {
00194     FPS "%-15s Display this help message.\n",
00195        "-H");
00196     FPS "%-15s Dump analysis.  No changes will be made to the database.\n",
00197        "-D");
00198     FPS "%-15s Cert database directory (default is ~/.netscape)\n",
00199        "   -d certdir");
00200     FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
00201        "   -m");
00202     FPS "%-15s Verbose mode.  Dumps the entire contents of your cert8.db.\n",
00203        "   -v");
00204     FPS "%-15s File to dump verbose output into. (default is stdout)\n",
00205        "   -f dumpfile");
00206 #ifdef DORECOVER
00207     FPS "%-15s Repair the database.  The program will look for broken\n",
00208        "-R");
00209     FPS "%-15s dependencies between subject entries and certificates,\n",
00210         "");
00211     FPS "%-15s between nickname entries and subjects, and between SMIME\n",
00212         "");
00213     FPS "%-15s profiles and subjects.  Any duplicate entries will be\n",
00214         "");
00215     FPS "%-15s removed, any missing entries will be created.\n",
00216         "");
00217     FPS "%-15s File to store new database in (default is new_cert8.db)\n",
00218        "   -o newdbname");
00219     FPS "%-15s Cert database directory (default is ~/.netscape)\n",
00220        "   -d certdir");
00221     FPS "%-15s Prompt before removing any certificates.\n",
00222         "   -p");
00223     FPS "%-15s Keep all possible certificates.  Only remove certificates\n",
00224        "   -a");
00225     FPS "%-15s which prevent creation of a consistent database.  Thus any\n",
00226        "");
00227     FPS "%-15s expired or redundant entries will be kept.\n",
00228        "");
00229     FPS "%-15s Keep redundant nickname/email entries.  It is possible\n",
00230        "   -r");
00231     FPS "%-15s only one such entry will be usable.\n",
00232        "");
00233     FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
00234        "   -s");
00235     FPS "%-15s cert.  An empty profile will be created.\n",
00236        "");
00237     FPS "%-15s Keep expired certificates.\n",
00238        "   -x");
00239     FPS "%-15s Verbose mode - report all activity while recovering db.\n",
00240        "   -v");
00241     FPS "%-15s File to dump verbose output into.\n",
00242        "   -f dumpfile");
00243     FPS "\n");
00244 #endif
00245     exit(-1);
00246 #undef FPS
00247 }
00248 
00249 /*******************************************************************
00250  *
00251  *  Functions for dbck.
00252  *
00253  ******************************************************************/
00254 
00255 void
00256 printHexString(PRFileDesc *out, SECItem *hexval)
00257 {
00258     unsigned int i;
00259     for (i = 0; i < hexval->len; i++) {
00260        if (i != hexval->len - 1) {
00261            PR_fprintf(out, "%02x:", hexval->data[i]);
00262        } else {
00263            PR_fprintf(out, "%02x", hexval->data[i]);
00264        }
00265     }
00266     PR_fprintf(out, "\n");
00267 }
00268 
00269 
00270 SECStatus
00271 dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
00272 {
00273     int userCert = 0;
00274     CERTCertTrust *trust = cert->trust;
00275     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
00276                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
00277                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
00278     if (num >= 0) {
00279        PR_fprintf(outfile, "Certificate: %3d\n", num);
00280     } else {
00281        PR_fprintf(outfile, "Certificate:\n");
00282     }
00283     PR_fprintf(outfile, "----------------\n");
00284     if (userCert)
00285        PR_fprintf(outfile, "(User Cert)\n");
00286     PR_fprintf(outfile, "## SUBJECT:  %s\n", cert->subjectName);
00287     PR_fprintf(outfile, "## ISSUER:  %s\n", cert->issuerName);
00288     PR_fprintf(outfile, "## SERIAL NUMBER:  ");
00289     printHexString(outfile, &cert->serialNumber);
00290     {  /*  XXX should be separate function.  */
00291        int64 timeBefore, timeAfter;
00292        PRExplodedTime beforePrintable, afterPrintable;
00293        char *beforestr, *afterstr;
00294        DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
00295        DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
00296        PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
00297        PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
00298        beforestr = PORT_Alloc(100);
00299        afterstr = PORT_Alloc(100);
00300        PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
00301        PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
00302        PR_fprintf(outfile, "## VALIDITY:  %s to %s\n", beforestr, afterstr);
00303     }
00304     PR_fprintf(outfile, "\n");
00305     return SECSuccess;
00306 }
00307 
00308 SECStatus
00309 dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
00310 {
00311 #if 0
00312     NSSLOWCERTCertificate *cert;
00313     /* should we check for existing duplicates? */
00314     cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, 
00315                                        entry->cert.nickname);
00316 #else
00317     CERTCertificate *cert;
00318     cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
00319 #endif
00320     if (!cert) {
00321        fprintf(stderr, "Failed to decode certificate.\n");
00322        return SECFailure;
00323     }
00324     cert->trust = (CERTCertTrust *)&entry->trust;
00325     dumpCertificate(cert, num, outfile);
00326     CERT_DestroyCertificate(cert);
00327     return SECSuccess;
00328 }
00329 
00330 SECStatus
00331 dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
00332 {
00333     char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
00334 
00335     PR_fprintf(outfile, "Subject: %3d\n", num);
00336     PR_fprintf(outfile, "------------\n");
00337     PR_fprintf(outfile, "## %s\n", subjectName);
00338     if (entry->nickname)
00339        PR_fprintf(outfile, "## Subject nickname:  %s\n", entry->nickname);
00340     if (entry->emailAddrs) {
00341        unsigned int n;
00342        for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
00343            char * emailAddr = entry->emailAddrs[n];
00344            if (emailAddr[0]) {
00345               PR_fprintf(outfile, "## Subject email address:  %s\n", 
00346                   emailAddr);
00347            }
00348        }
00349     }
00350     PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
00351     PR_fprintf(outfile, "\n");
00352     PORT_Free(subjectName);
00353     return SECSuccess;
00354 }
00355 
00356 SECStatus
00357 dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
00358 {
00359     PR_fprintf(outfile, "Nickname: %3d\n", num);
00360     PR_fprintf(outfile, "-------------\n");
00361     PR_fprintf(outfile, "##  \"%s\"\n\n", entry->nickname);
00362     return SECSuccess;
00363 }
00364 
00365 SECStatus
00366 dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
00367 {
00368     PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
00369     PR_fprintf(outfile, "-------------------\n");
00370     PR_fprintf(outfile, "##  \"%s\"\n", entry->emailAddr);
00371 #ifdef OLDWAY
00372     PR_fprintf(outfile, "##  OPTIONS:  ");
00373     printHexString(outfile, &entry->smimeOptions);
00374     PR_fprintf(outfile, "##  TIMESTAMP:  ");
00375     printHexString(outfile, &entry->optionsDate);
00376 #else
00377     SECU_PrintAny(stdout, &entry->smimeOptions, "##  OPTIONS  ", 0);
00378     fflush(stdout);
00379     if (entry->optionsDate.len && entry->optionsDate.data)
00380        PR_fprintf(outfile, "##  TIMESTAMP: %.*s\n", 
00381                   entry->optionsDate.len, entry->optionsDate.data);
00382 #endif
00383     PR_fprintf(outfile, "\n");
00384     return SECSuccess;
00385 }
00386 
00387 SECStatus
00388 mapCertEntries(certDBArray *dbArray)
00389 {
00390     certDBEntryCert *certEntry;
00391     certDBEntrySubject *subjectEntry;
00392     certDBEntryListNode *certNode, *subjNode;
00393     certDBSubjectEntryMap *smap;
00394     certDBEntryMap *map;
00395     PRArenaPool *tmparena;
00396     SECItem derSubject;
00397     SECItem certKey;
00398     PRCList *cElem, *sElem;
00399 
00400     /* Arena for decoded entries */
00401     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00402     if (tmparena == NULL) {
00403        PORT_SetError(SEC_ERROR_NO_MEMORY);
00404        return SECFailure;
00405     }
00406 
00407     /* Iterate over cert entries and map them to subject entries. 
00408      * NOTE: mapSubjectEntries must be called first to alloc memory
00409      * for array of subject->cert map.
00410      */
00411     for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 
00412          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
00413        certNode = LISTNODE_CAST(cElem);
00414        certEntry = (certDBEntryCert *)&certNode->entry;
00415        map = (certDBEntryMap *)certNode->appData;
00416        CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
00417        CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
00418        /*  Loop over found subjects for cert's DN.  */
00419        for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
00420             sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
00421            subjNode = LISTNODE_CAST(sElem);
00422            subjectEntry = (certDBEntrySubject *)&subjNode->entry;
00423            if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
00424               unsigned int i;
00425               /*  Found matching subject name, create link.  */
00426               map->pSubject = subjNode;
00427               /*  Make sure subject entry has cert's key.  */
00428               for (i=0; i<subjectEntry->ncerts; i++) {
00429                   if (SECITEM_ItemsAreEqual(&certKey,
00430                                             &subjectEntry->certKeys[i])) {
00431                      /*  Found matching cert key.  */
00432                      smap = (certDBSubjectEntryMap *)subjNode->appData;
00433                      smap->pCerts[i] = certNode;
00434                      break;
00435                   }
00436               }
00437            }
00438        }
00439     }
00440     PORT_FreeArena(tmparena, PR_FALSE);
00441     return SECSuccess;
00442 }
00443 
00444 SECStatus
00445 mapSubjectEntries(certDBArray *dbArray)
00446 {
00447     certDBEntrySubject *subjectEntry;
00448     certDBEntryListNode *subjNode;
00449     certDBSubjectEntryMap *subjMap;
00450     PRCList *sElem;
00451 
00452     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
00453          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
00454        /* Iterate over subject entries and map subjects to nickname
00455         * and smime entries.  The cert<->subject map will be handled
00456         * by a subsequent call to mapCertEntries.
00457         */
00458        subjNode = LISTNODE_CAST(sElem);
00459        subjectEntry = (certDBEntrySubject *)&subjNode->entry;
00460        subjMap = (certDBSubjectEntryMap *)subjNode->appData;
00461        /* need to alloc memory here for array of matching certs. */
00462        subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 
00463                                          subjectEntry->ncerts*sizeof(int));
00464        subjMap->numCerts = subjectEntry->ncerts;
00465        subjMap->pNickname = NoNickname;
00466        subjMap->pSMime = NoSMime;
00467 
00468        if (subjectEntry->nickname) {
00469            /* Subject should have a nickname entry, so create a link. */
00470            PRCList *nElem;
00471            for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
00472                 nElem != &dbArray->nicknames.link; 
00473                 nElem = PR_NEXT_LINK(nElem)) {
00474               certDBEntryListNode *nickNode;
00475               certDBEntryNickname *nicknameEntry;
00476               /*  Look for subject's nickname in nickname entries.  */
00477               nickNode = LISTNODE_CAST(nElem);
00478               nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
00479               if (PL_strcmp(subjectEntry->nickname, 
00480                             nicknameEntry->nickname) == 0) {
00481                   /*  Found a nickname entry for subject's nickname.  */
00482                   if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
00483                                             &nicknameEntry->subjectName)) {
00484                      certDBEntryMap *nickMap;
00485                      nickMap = (certDBEntryMap *)nickNode->appData;
00486                      /*  Nickname and subject match.  */
00487                      subjMap->pNickname = nickNode;
00488                      nickMap->pSubject = subjNode;
00489                   } else if (subjMap->pNickname == NoNickname) {
00490                      /*  Nickname entry found is for diff. subject.  */
00491                      subjMap->pNickname = WrongEntry;
00492                   }
00493               }
00494            }
00495        }
00496        if (subjectEntry->emailAddrs) {
00497            unsigned int n;
00498            for (n = 0; n < subjectEntry->nemailAddrs && 
00499                        subjectEntry->emailAddrs[n]; ++n) {
00500               char * emailAddr = subjectEntry->emailAddrs[n];
00501               if (emailAddr[0]) {
00502                   PRCList *mElem;
00503                   /* Subject should have an smime entry, so create a link. */
00504                   for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
00505                       mElem != &dbArray->smime.link; 
00506                       mElem = PR_NEXT_LINK(mElem)) {
00507                      certDBEntryListNode *smimeNode;
00508                      certDBEntrySMime *smimeEntry;
00509                      /*  Look for subject's email in S/MIME entries.  */
00510                      smimeNode = LISTNODE_CAST(mElem);
00511                      smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
00512                      if (PL_strcmp(emailAddr, 
00513                                   smimeEntry->emailAddr) == 0) {
00514                          /*  Found a S/MIME entry for subject's email.  */
00515                          if (SECITEM_ItemsAreEqual(
00516                                    &subjectEntry->derSubject,
00517                                     &smimeEntry->subjectName)) {
00518                             certDBEntryMap *smimeMap;
00519                             /*  S/MIME entry and subject match.  */
00520                             subjMap->pSMime = smimeNode;
00521                             smimeMap = (certDBEntryMap *)smimeNode->appData;
00522                             smimeMap->pSubject = subjNode;
00523                          } else if (subjMap->pSMime == NoSMime) {
00524                             /*  S/MIME entry found is for diff. subject.  */
00525                             subjMap->pSMime = WrongEntry;
00526                          }
00527                      }
00528                   }   /* end for */
00529               }   /* endif (emailAddr[0]) */
00530            }   /* end for */
00531        }   /* endif (subjectEntry->emailAddrs) */
00532     }
00533     return SECSuccess;
00534 }
00535 
00536 void
00537 printnode(dbDebugInfo *info, const char *str, int num)
00538 {
00539     if (!info->dograph)
00540        return;
00541     if (num < 0) {
00542        PR_fprintf(info->graphfile, str);
00543     } else {
00544        PR_fprintf(info->graphfile, str, num);
00545     }
00546 }
00547 
00548 PRBool
00549 map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
00550 {
00551     if (mapPtr == NULL) {
00552        if (indent > 0)
00553            printnode(info, "                ", -1);
00554        if (indent >= 0)
00555            printnode(info, "******************* ", -1);
00556        return PR_FALSE;
00557     } else if (mapPtr == WrongEntry) {
00558        if (indent > 0)
00559            printnode(info, "                  ", -1);
00560        if (indent >= 0)
00561            printnode(info, "??????????????????? ", -1);
00562        return PR_FALSE;
00563     } else {
00564        return PR_TRUE;
00565     }
00566 }
00567 
00568 /* these call each other */
00569 void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 
00570                        int direction);
00571 void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 
00572                           int direction);
00573 void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
00574                          int direction, int optindex, int opttype);
00575 void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 
00576                       int direction);
00577 
00578 /* Given an smime entry, print its unique identifier.  If GOLEFT is 
00579  * specified, print the cert<-subject<-smime map, else just print
00580  * the smime entry.
00581  */
00582 void
00583 print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
00584 {
00585     certDBSubjectEntryMap *subjMap;
00586     certDBEntryListNode *subjNode;
00587     if (direction == GOLEFT) {
00588        /* Need to output subject and cert first, see print_subject_graph */
00589        subjNode = smimeMap->pSubject;
00590        if (map_handle_is_ok(info, (void *)subjNode, 1)) {
00591            subjMap = (certDBSubjectEntryMap *)subjNode->appData; 
00592            print_subject_graph(info, subjMap, GOLEFT,
00593                                smimeMap->index, certDBEntryTypeSMimeProfile);
00594        } else {
00595            printnode(info, "<---- S/MIME   %5d   ", smimeMap->index);
00596            info->dbErrors[NoSubjectForSMime]++;
00597        }
00598     } else {
00599        printnode(info, "S/MIME   %5d   ", smimeMap->index);
00600     }
00601 }
00602 
00603 /* Given a nickname entry, print its unique identifier.  If GOLEFT is 
00604  * specified, print the cert<-subject<-nickname map, else just print
00605  * the nickname entry.
00606  */
00607 void
00608 print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
00609 {
00610     certDBSubjectEntryMap *subjMap;
00611     certDBEntryListNode *subjNode;
00612     if (direction == GOLEFT) {
00613        /* Need to output subject and cert first, see print_subject_graph */
00614        subjNode = nickMap->pSubject;
00615        if (map_handle_is_ok(info, (void *)subjNode, 1)) {
00616            subjMap = (certDBSubjectEntryMap *)subjNode->appData;
00617            print_subject_graph(info, subjMap, GOLEFT,
00618                                nickMap->index, certDBEntryTypeNickname);
00619        } else {
00620            printnode(info, "<---- Nickname %5d   ", nickMap->index);
00621            info->dbErrors[NoSubjectForNickname]++;
00622        }
00623     } else {
00624        printnode(info, "Nickname %5d   ", nickMap->index);
00625     }
00626 }
00627 
00628 /* Given a subject entry, if going right print the graph of the nickname|smime
00629  * that it maps to (by its unique identifier); and if going left
00630  * print the list of certs that it points to.
00631  */
00632 void
00633 print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
00634                     int direction, int optindex, int opttype)
00635 {
00636     certDBEntryMap *map;
00637     certDBEntryListNode *node;
00638     int i;
00639     /* The first line of output always contains the cert id, subject id,
00640      * and nickname|smime id.  Subsequent lines may contain additional
00641      * cert id's for the subject if going left or both directions.
00642      * Ex. of printing the graph for a subject entry:
00643      * Cert 3 <- Subject 5 -> Nickname 32
00644      * Cert 8 /
00645      * Cert 9 /
00646      * means subject 5 has 3 certs, 3, 8, and 9, and corresponds
00647      * to nickname entry 32.
00648      * To accomplish the above, it is required to dump the entire first
00649      * line left-to-right, regardless of the input direction, and then
00650      * finish up any remaining cert entries.  Hence the code is uglier
00651      * than one may expect.
00652      */
00653     if (direction == GOLEFT || direction == GOBOTH) {
00654        /* In this case, nothing should be output until the first cert is
00655         * located and output (cert 3 in the above example).
00656         */
00657        if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
00658            /* XXX uh-oh */
00659            return;
00660        /* get the first cert and dump it. */
00661        node = subjMap->pCerts[0];
00662        if (map_handle_is_ok(info, (void *)node, 0)) {
00663            map = (certDBEntryMap *)node->appData;
00664            /* going left here stops. */
00665            print_cert_graph(info, map, GOLEFT); 
00666        } else {
00667            info->dbErrors[SubjectHasNoKeyForCert]++;
00668        }
00669        /* Now it is safe to output the subject id. */
00670        if (direction == GOLEFT)
00671            printnode(info, "Subject  %5d <---- ", subjMap->index);
00672        else /* direction == GOBOTH */
00673            printnode(info, "Subject  %5d ----> ", subjMap->index);
00674     }
00675     if (direction == GORIGHT || direction == GOBOTH) { 
00676        /* Okay, now output the nickname|smime for this subject. */
00677        if (direction != GOBOTH) /* handled above */
00678           printnode(info, "Subject  %5d ----> ", subjMap->index);
00679        if (subjMap->pNickname) {
00680            node = subjMap->pNickname;
00681            if (map_handle_is_ok(info, (void *)node, 0)) {
00682               map = (certDBEntryMap *)node->appData;
00683               /* going right here stops. */
00684               print_nickname_graph(info, map, GORIGHT);
00685            }
00686        }
00687        if (subjMap->pSMime) {
00688            node = subjMap->pSMime;
00689            if (map_handle_is_ok(info, (void *)node, 0)) {
00690               map = (certDBEntryMap *)node->appData;
00691               /* going right here stops. */
00692               print_smime_graph(info, map, GORIGHT); 
00693            }
00694        }
00695        if (!subjMap->pNickname && !subjMap->pSMime) {
00696            printnode(info, "******************* ", -1);
00697            info->dbErrors[NoNicknameOrSMimeForSubject]++;
00698        }
00699        if (subjMap->pNickname && subjMap->pSMime) {
00700            info->dbErrors[NicknameAndSMimeEntries]++;
00701        }
00702     }
00703     if (direction != GORIGHT) { /* going right has only one cert */
00704        if (opttype == certDBEntryTypeNickname)
00705            printnode(info, "Nickname %5d   ", optindex);
00706        else if (opttype == certDBEntryTypeSMimeProfile)
00707            printnode(info, "S/MIME   %5d   ", optindex);
00708        for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
00709            printnode(info, "\n", -1); /* start a new line */
00710            node = subjMap->pCerts[i];
00711            if (map_handle_is_ok(info, (void *)node, 0)) {
00712               map = (certDBEntryMap *)node->appData;
00713               /* going left here stops. */
00714               print_cert_graph(info, map, GOLEFT); 
00715               printnode(info, "/", -1);
00716            }
00717        }
00718     }
00719 }
00720 
00721 /* Given a cert entry, print its unique identifer.  If GORIGHT is specified,
00722  * print the cert->subject->nickname|smime map, else just print
00723  * the cert entry.
00724  */
00725 void
00726 print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
00727 {
00728     certDBSubjectEntryMap *subjMap;
00729     certDBEntryListNode *subjNode;
00730     if (direction == GOLEFT) {
00731        printnode(info, "Cert     %5d <---- ", certMap->index);
00732        /* only want cert entry, terminate here. */
00733        return;
00734     }
00735     /* Keep going right then. */
00736     printnode(info, "Cert     %5d ----> ", certMap->index);
00737     subjNode = certMap->pSubject;
00738     if (map_handle_is_ok(info, (void *)subjNode, 0)) {
00739        subjMap = (certDBSubjectEntryMap *)subjNode->appData;
00740        print_subject_graph(info, subjMap, GORIGHT, -1, -1);
00741     } else {
00742        info->dbErrors[NoSubjectForCert]++;
00743     }
00744 }
00745 
00746 SECStatus
00747 computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
00748 {
00749     PRCList *cElem, *sElem, *nElem, *mElem;
00750     certDBEntryListNode *node;
00751     certDBEntryMap *map;
00752     certDBSubjectEntryMap *subjMap;
00753 
00754     /* Graph is of this form:
00755      *
00756      * certs:
00757      * cert ---> subject ---> (nickname|smime)
00758      *
00759      * subjects:
00760      * cert <--- subject ---> (nickname|smime)
00761      *
00762      * nicknames and smime:
00763      * cert <--- subject <--- (nickname|smime)
00764      */
00765 
00766     /* Print cert graph. */
00767     for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
00768          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
00769        /* Print graph of everything to right of cert entry. */
00770        node = LISTNODE_CAST(cElem);
00771        map = (certDBEntryMap *)node->appData;
00772        print_cert_graph(info, map, GORIGHT);
00773        printnode(info, "\n", -1);
00774     }
00775     printnode(info, "\n", -1);
00776 
00777     /* Print subject graph. */
00778     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
00779          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
00780        /* Print graph of everything to both sides of subject entry. */
00781        node = LISTNODE_CAST(sElem);
00782        subjMap = (certDBSubjectEntryMap *)node->appData;
00783        print_subject_graph(info, subjMap, GOBOTH, -1, -1);
00784        printnode(info, "\n", -1);
00785     }
00786     printnode(info, "\n", -1);
00787 
00788     /* Print nickname graph. */
00789     for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
00790          nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
00791        /* Print graph of everything to left of nickname entry. */
00792        node = LISTNODE_CAST(nElem);
00793        map = (certDBEntryMap *)node->appData;
00794        print_nickname_graph(info, map, GOLEFT);
00795        printnode(info, "\n", -1);
00796     }
00797     printnode(info, "\n", -1);
00798 
00799     /* Print smime graph. */
00800     for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
00801          mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
00802        /* Print graph of everything to left of smime entry. */
00803        node = LISTNODE_CAST(mElem);
00804        if (node == NULL) break;
00805        map = (certDBEntryMap *)node->appData;
00806        print_smime_graph(info, map, GOLEFT);
00807        printnode(info, "\n", -1);
00808     }
00809     printnode(info, "\n", -1);
00810 
00811     return SECSuccess;
00812 }
00813 
00814 /*
00815  * List the entries in the db, showing handles between entry types.
00816  */
00817 void
00818 verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
00819 {
00820     int i, ref;
00821     PRCList *elem;
00822     certDBEntryListNode *node;
00823     certDBEntryMap *map;
00824     certDBSubjectEntryMap *smap;
00825     certDBEntrySubject *subjectEntry;
00826 
00827     /* List certs */
00828     for (elem = PR_LIST_HEAD(&dbArray->certs.link);
00829          elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
00830        node = LISTNODE_CAST(elem);
00831        map = (certDBEntryMap *)node->appData;
00832        dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
00833        /* walk the cert handle to it's subject entry */
00834        if (map_handle_is_ok(info, map->pSubject, -1)) {
00835            smap = (certDBSubjectEntryMap *)map->pSubject->appData;
00836            ref = smap->index;
00837            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
00838        } else {
00839            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
00840        }
00841     }
00842     /* List subjects */
00843     for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
00844          elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
00845        int refs = 0;
00846        node = LISTNODE_CAST(elem);
00847        subjectEntry = (certDBEntrySubject *)&node->entry;
00848        smap = (certDBSubjectEntryMap *)node->appData;
00849        dumpSubjectEntry(subjectEntry, smap->index, info->out);
00850        /* iterate over subject's certs */
00851        for (i=0; i<smap->numCerts; i++) {
00852            /* walk each subject handle to it's cert entries */
00853            if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
00854               ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
00855               PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
00856            } else {
00857               PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
00858            }
00859        }
00860        if (subjectEntry->nickname) {
00861            ++refs;
00862            /* walk each subject handle to it's nickname entry */
00863            if (map_handle_is_ok(info, smap->pNickname, -1)) {
00864               ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
00865               PR_fprintf(info->out, "-->(nickname %d)\n", ref);
00866            } else {
00867               PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
00868            }
00869        }
00870        if (subjectEntry->nemailAddrs && 
00871            subjectEntry->emailAddrs &&
00872            subjectEntry->emailAddrs[0] &&
00873            subjectEntry->emailAddrs[0][0]) {
00874            ++refs;
00875            /* walk each subject handle to it's smime entry */
00876            if (map_handle_is_ok(info, smap->pSMime, -1)) {
00877               ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
00878               PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
00879            } else {
00880               PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
00881            }
00882        }
00883        if (!refs) {
00884            PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
00885        }
00886        PR_fprintf(info->out, "\n\n");
00887     }
00888     for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
00889          elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
00890        node = LISTNODE_CAST(elem);
00891        map = (certDBEntryMap *)node->appData;
00892        dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index, 
00893                          info->out);
00894        if (map_handle_is_ok(info, map->pSubject, -1)) {
00895            ref = ((certDBEntryMap *)map->pSubject->appData)->index;
00896            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
00897        } else {
00898            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
00899        }
00900     }
00901     for (elem = PR_LIST_HEAD(&dbArray->smime.link);
00902          elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
00903        node = LISTNODE_CAST(elem);
00904        map = (certDBEntryMap *)node->appData;
00905        dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
00906        if (map_handle_is_ok(info, map->pSubject, -1)) {
00907            ref = ((certDBEntryMap *)map->pSubject->appData)->index;
00908            PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
00909        } else {
00910            PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
00911        }
00912     }
00913     PR_fprintf(info->out, "\n\n");
00914 }
00915 
00916 
00917 /* A callback function, intended to be called from nsslowcert_TraverseDBEntries
00918  * Builds a PRCList of DB entries of the specified type.
00919  */
00920 SECStatus 
00921 SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, 
00922                        certDBEntryType entryType, void *pdata)
00923 {
00924     certDBEntry         * entry;
00925     certDBEntryListNode * node;
00926     PRCList             * list = (PRCList *)pdata;
00927 
00928     if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
00929        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00930        return SECFailure;
00931     }
00932     entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
00933     if (!entry) {
00934        return SECSuccess; /* skip it */
00935     }
00936     node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
00937     if (!node) {
00938        /* DestroyDBEntry(entry); */
00939        PLArenaPool *arena = entry->common.arena;
00940        PORT_Memset(&entry->common, 0, sizeof entry->common);
00941        PORT_FreeArena(arena, PR_FALSE);
00942        return SECFailure;
00943     }
00944     node->entry = *entry;          /* crude but effective. */
00945     PR_INIT_CLIST(&node->link);
00946     PR_INSERT_BEFORE(&node->link, list);
00947     return SECSuccess;
00948 }
00949 
00950 
00951 int
00952 fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, 
00953                  certDBEntryListNode *list)
00954 {
00955     PRCList *elem;
00956     certDBEntryListNode *node;
00957     certDBEntryMap *mnode;
00958     certDBSubjectEntryMap *smnode;
00959     PRArenaPool *arena;
00960     int count = 0;
00961 
00962     /* Initialize a dummy entry in the list.  The list head will be the
00963      * next element, so this element is skipped by for loops.
00964      */
00965     PR_INIT_CLIST((PRCList *)list);
00966     /* Collect all of the cert db entries for this type into a list. */
00967     nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
00968 
00969     for (elem = PR_LIST_HEAD(&list->link); 
00970          elem != &list->link; elem = PR_NEXT_LINK(elem)) {
00971        /* Iterate over the entries and ... */
00972        node = (certDBEntryListNode *)elem;
00973        if (type != certDBEntryTypeSubject) {
00974            arena = PORT_NewArena(sizeof(*mnode));
00975            mnode = PORT_ArenaZNew(arena, certDBEntryMap);
00976            mnode->arena = arena;
00977            /* ... assign a unique index number to each node, and ... */
00978            mnode->index = count;
00979            /* ... set the map pointer for the node. */
00980            node->appData = (void *)mnode;
00981        } else {
00982            /* allocate some room for the cert pointers also */
00983            arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
00984            smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
00985            smnode->arena = arena;
00986            smnode->index = count;
00987            node->appData = (void *)smnode;
00988        }
00989        count++;
00990     }
00991     return count;
00992 }
00993 
00994 void
00995 freeDBEntryList(PRCList *list)
00996 {
00997     PRCList *next, *elem;
00998     certDBEntryListNode *node;
00999     certDBEntryMap *map;
01000 
01001     for (elem = PR_LIST_HEAD(list); elem != list;) { 
01002        next = PR_NEXT_LINK(elem);
01003        node = (certDBEntryListNode *)elem;
01004        map = (certDBEntryMap *)node->appData;
01005        PR_REMOVE_LINK(&node->link);
01006        PORT_FreeArena(map->arena, PR_TRUE);
01007        PORT_FreeArena(node->entry.common.arena, PR_TRUE);
01008        elem = next;
01009     }
01010 }
01011 
01012 void
01013 DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, 
01014             PRFileDesc *mailfile)
01015 {
01016     int i, nCertsFound, nSubjFound, nErr;
01017     int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
01018     PRCList *elem;
01019     char c;
01020     dbDebugInfo info;
01021     certDBArray dbArray;
01022 
01023     PORT_Memset(&dbArray, 0, sizeof(dbArray));
01024     PORT_Memset(&info, 0, sizeof(info));
01025     info.verbose = (PRBool)(out != NULL);
01026     info.dograph = info.verbose;
01027     info.out       = (out)    ? out      : PR_STDOUT;
01028     info.graphfile = mailfile ? mailfile : PR_STDOUT;
01029 
01030     /*  Fill the array structure with cert/subject/nickname/smime entries.  */
01031     dbArray.numCerts     = fillDBEntryArray(handle, certDBEntryTypeCert, 
01032                                             &dbArray.certs);
01033     dbArray.numSubjects  = fillDBEntryArray(handle, certDBEntryTypeSubject, 
01034                                             &dbArray.subjects);
01035     dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 
01036                                             &dbArray.nicknames);
01037     dbArray.numSMime     = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 
01038                                             &dbArray.smime);
01039     dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation, 
01040                                             &dbArray.revocation);
01041 
01042     /*  Compute the map between the database entries.  */
01043     mapSubjectEntries(&dbArray);
01044     mapCertEntries(&dbArray);
01045     computeDBGraph(&dbArray, &info);
01046 
01047     /*  Store the totals for later reference.  */
01048     nCerts     = dbArray.numCerts;
01049     nSubjects  = dbArray.numSubjects;
01050     nNicknames = dbArray.numNicknames;
01051     nSMime     = dbArray.numSMime;
01052     nRevocation= dbArray.numRevocation;
01053     nSubjCerts = 0;
01054     for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
01055          elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
01056        certDBSubjectEntryMap *smap;
01057        smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
01058        nSubjCerts += smap->numCerts;
01059     }
01060 
01061     if (info.verbose) {
01062        /*  Dump the database contents.  */
01063        verboseOutput(&dbArray, &info);
01064     }
01065 
01066     freeDBEntryList(&dbArray.certs.link);
01067     freeDBEntryList(&dbArray.subjects.link);
01068     freeDBEntryList(&dbArray.nicknames.link);
01069     freeDBEntryList(&dbArray.smime.link);
01070     freeDBEntryList(&dbArray.revocation.link);
01071 
01072     PR_fprintf(info.out, "\n");
01073     PR_fprintf(info.out, "Database statistics:\n");
01074     PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", 
01075                           nCerts);
01076     PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", 
01077                           nSubjects);
01078     PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", 
01079                           nSubjCerts);
01080     PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", 
01081                           nNicknames);
01082     PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", 
01083                           nSMime);
01084     PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", 
01085                           nRevocation);
01086     PR_fprintf(info.out, "\n");
01087 
01088     nErr = 0;
01089     for (i=0; i < NUM_ERROR_TYPES; i++) {
01090        PR_fprintf(info.out, "E%d: Found %4d %s\n", 
01091                   i, info.dbErrors[i], errResult[i]);
01092        nErr += info.dbErrors[i];
01093     }
01094     PR_fprintf(info.out, "--------------\n    Found %4d errors in database.\n", 
01095                nErr);
01096 
01097     PR_fprintf(info.out, "\nCertificates:\n");
01098     PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, 
01099                                                    SubjectHasNoKeyForCert);
01100     nCertsFound = nSubjCerts +
01101                   info.dbErrors[NoSubjectForCert] +
01102                   info.dbErrors[SubjectHasNoKeyForCert];
01103     c = (nCertsFound == nCerts) ? '=' : '!';
01104     PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, 
01105                   info.dbErrors[NoSubjectForCert],
01106                   info.dbErrors[SubjectHasNoKeyForCert]);
01107     PR_fprintf(info.out, "\nSubjects:\n");
01108     PR_fprintf(info.out, 
01109     "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
01110                   NoNicknameOrSMimeForSubject, 
01111                 WrongNicknameForSubject,
01112                 NoNicknameEntry, 
01113                 WrongSMimeForSubject, 
01114                 NoSMimeEntry,
01115                 NoSubjectForNickname, 
01116                 NoSubjectForSMime,
01117                 NicknameAndSMimeEntries);
01118     nSubjFound = nNicknames + nSMime + 
01119                  info.dbErrors[NoNicknameOrSMimeForSubject] +
01120                info.dbErrors[WrongNicknameForSubject] +
01121                info.dbErrors[NoNicknameEntry] +
01122                info.dbErrors[WrongSMimeForSubject] +
01123                  info.dbErrors[NoSMimeEntry] -
01124                info.dbErrors[NoSubjectForNickname] -
01125                info.dbErrors[NoSubjectForSMime] -
01126                info.dbErrors[NicknameAndSMimeEntries];
01127     c = (nSubjFound == nSubjects) ? '=' : '!';
01128     PR_fprintf(info.out, 
01129     "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
01130                   nSubjects, c, nNicknames, nSMime,
01131                   info.dbErrors[NoNicknameOrSMimeForSubject],
01132                 info.dbErrors[WrongNicknameForSubject],
01133                 info.dbErrors[NoNicknameEntry],
01134                 info.dbErrors[WrongSMimeForSubject],
01135                   info.dbErrors[NoSMimeEntry],
01136                 info.dbErrors[NoSubjectForNickname],
01137                 info.dbErrors[NoSubjectForSMime],
01138                 info.dbErrors[NicknameAndSMimeEntries]);
01139     PR_fprintf(info.out, "\n");
01140 }
01141 
01142 #ifdef DORECOVER
01143 #include "dbrecover.c"
01144 #endif /* DORECOVER */
01145 
01146 enum {
01147     cmd_Debug = 0,
01148     cmd_LongUsage,
01149     cmd_Recover
01150 };
01151 
01152 enum {
01153     opt_KeepAll = 0,
01154     opt_CertDir,
01155     opt_Dumpfile,
01156     opt_InputDB,
01157     opt_OutputDB,
01158     opt_Mailfile,
01159     opt_Prompt,
01160     opt_KeepRedundant,
01161     opt_KeepNoSMimeProfile,
01162     opt_Verbose,
01163     opt_KeepExpired
01164 };
01165 
01166 static secuCommandFlag dbck_commands[] =
01167 {
01168     { /* cmd_Debug,    */  'D', PR_FALSE, 0, PR_FALSE },
01169     { /* cmd_LongUsage,*/  'H', PR_FALSE, 0, PR_FALSE },
01170     { /* cmd_Recover,  */  'R', PR_FALSE, 0, PR_FALSE }
01171 };
01172 
01173 static secuCommandFlag dbck_options[] =
01174 {
01175     { /* opt_KeepAll,           */  'a', PR_FALSE, 0, PR_FALSE },
01176     { /* opt_CertDir,           */  'd', PR_TRUE,  0, PR_FALSE },
01177     { /* opt_Dumpfile,          */  'f', PR_TRUE,  0, PR_FALSE },
01178     { /* opt_InputDB,           */  'i', PR_TRUE,  0, PR_FALSE },
01179     { /* opt_OutputDB,          */  'o', PR_TRUE,  0, PR_FALSE },
01180     { /* opt_Mailfile,          */  'm', PR_FALSE, 0, PR_FALSE },
01181     { /* opt_Prompt,            */  'p', PR_FALSE, 0, PR_FALSE },
01182     { /* opt_KeepRedundant,     */  'r', PR_FALSE, 0, PR_FALSE },
01183     { /* opt_KeepNoSMimeProfile,*/  's', PR_FALSE, 0, PR_FALSE },
01184     { /* opt_Verbose,           */  'v', PR_FALSE, 0, PR_FALSE },
01185     { /* opt_KeepExpired,       */  'x', PR_FALSE, 0, PR_FALSE }
01186 };
01187 
01188 #define CERT_DB_FMT "%s/cert%s.db"
01189 
01190 static char *
01191 dbck_certdb_name_cb(void *arg, int dbVersion)
01192 {
01193     const char *configdir = (const char *)arg;
01194     const char *dbver;
01195     char *smpname = NULL;
01196     char *dbname = NULL;
01197 
01198     switch (dbVersion) {
01199       case 8:
01200        dbver = "8";
01201        break;
01202       case 7:
01203        dbver = "7";
01204        break;
01205       case 6:
01206        dbver = "6";
01207        break;
01208       case 5:
01209        dbver = "5";
01210        break;
01211       case 4:
01212       default:
01213        dbver = "";
01214        break;
01215     }
01216 
01217     /* make sure we return something allocated with PORT_ so we have properly
01218      * matched frees at the end */
01219     smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
01220     if (smpname) {
01221        dbname = PORT_Strdup(smpname);
01222        PR_smprintf_free(smpname);
01223     }
01224     return dbname;
01225 }
01226     
01227 
01228 int 
01229 main(int argc, char **argv)
01230 {
01231     NSSLOWCERTCertDBHandle *certHandle;
01232 
01233     PRFileDesc *mailfile = NULL;
01234     PRFileDesc *dumpfile = NULL;
01235 
01236     char * pathname     = 0;
01237     char * fullname     = 0;
01238     char * newdbname    = 0;
01239 
01240     PRBool removeExpired, requireProfile, singleEntry;
01241     SECStatus   rv;
01242     secuCommand dbck;
01243 
01244     dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
01245     dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
01246     dbck.commands = dbck_commands;
01247     dbck.options = dbck_options;
01248 
01249     progName = strrchr(argv[0], '/');
01250     progName = progName ? progName+1 : argv[0];
01251 
01252     rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
01253 
01254     if (rv != SECSuccess)
01255        Usage(progName);
01256 
01257     if (dbck.commands[cmd_LongUsage].activated)
01258        LongUsage(progName);
01259 
01260     if (!dbck.commands[cmd_Debug].activated &&
01261         !dbck.commands[cmd_Recover].activated) {
01262        PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
01263        Usage(progName);
01264     }
01265 
01266     removeExpired = !(dbck.options[opt_KeepAll].activated ||
01267                       dbck.options[opt_KeepExpired].activated);
01268 
01269     requireProfile = !(dbck.options[opt_KeepAll].activated ||
01270                     dbck.options[opt_KeepNoSMimeProfile].activated);
01271 
01272     singleEntry = !(dbck.options[opt_KeepAll].activated ||
01273                     dbck.options[opt_KeepRedundant].activated);
01274 
01275     if (dbck.options[opt_OutputDB].activated) {
01276        newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
01277     } else {
01278        newdbname = PL_strdup("new_cert8.db");
01279     }
01280 
01281     /*  Create a generic graph of the database.  */
01282     if (dbck.options[opt_Mailfile].activated) {
01283        mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
01284        if (!mailfile) {
01285            fprintf(stderr, "Unable to create mailfile.\n");
01286            return -1;
01287        }
01288     }
01289 
01290     /*  Dump all debugging info while running.  */
01291     if (dbck.options[opt_Verbose].activated) {
01292        if (dbck.options[opt_Dumpfile].activated) {
01293            dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
01294                               PR_RDWR | PR_CREATE_FILE, 00660);
01295            if (!dumpfile) {
01296               fprintf(stderr, "Unable to create dumpfile.\n");
01297               return -1;
01298            }
01299        } else {
01300            dumpfile = PR_STDOUT;
01301        }
01302     }
01303 
01304     /*  Set the cert database directory.  */
01305     if (dbck.options[opt_CertDir].activated) {
01306        SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
01307     }
01308 
01309     pathname = SECU_ConfigDirectory(NULL);
01310 
01311     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
01312     rv = NSS_NoDB_Init(pathname);
01313     if (rv != SECSuccess) {
01314        fprintf(stderr, "NSS_NoDB_Init failed\n");
01315        return -1;
01316     }
01317 
01318     certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
01319     if (!certHandle) {
01320        SECU_PrintError(progName, "unable to get database handle");
01321        return -1;
01322     }
01323     certHandle->ref = 1;
01324 
01325 #ifdef NOTYET
01326     /*  Open the possibly corrupt database.  */
01327     if (dbck.options[opt_InputDB].activated) {
01328        PRFileInfo fileInfo;
01329        fullname = PR_smprintf("%s/%s", pathname, 
01330                                        dbck.options[opt_InputDB].arg);
01331        if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
01332            fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
01333            return -1;
01334        }
01335        rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
01336     } else 
01337 #endif
01338     {
01339        /*  Use the default.  */
01340 #ifdef NOTYET
01341        fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
01342        if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
01343            fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
01344            return -1;
01345        }
01346 #endif
01347        rv = nsslowcert_OpenCertDB(certHandle, 
01348                                   PR_TRUE,                  /* readOnly */
01349                                NULL,                    /* rdb appName */
01350                                "",                      /* rdb prefix */
01351                                   dbck_certdb_name_cb,     /* namecb */
01352                                pathname,             /* configDir */
01353                                PR_FALSE);            /* volatile */
01354     }
01355 
01356     if (rv) {
01357        SECU_PrintError(progName, "unable to open cert database");
01358        return -1;
01359     }
01360 
01361     if (dbck.commands[cmd_Debug].activated) {
01362        DBCK_DebugDB(certHandle, dumpfile, mailfile);
01363        return 0;
01364     }
01365 
01366 #ifdef DORECOVER
01367     if (dbck.commands[cmd_Recover].activated) {
01368        DBCK_ReconstructDBFromCerts(certHandle, newdbname,
01369                                    dumpfile, removeExpired, 
01370                                    requireProfile, singleEntry, 
01371                                    dbck.options[opt_Prompt].activated);
01372        return 0;
01373     }
01374 #endif
01375 
01376     if (mailfile)
01377        PR_Close(mailfile);
01378     if (dumpfile)
01379        PR_Close(dumpfile);
01380     if (certHandle) {
01381        nsslowcert_ClosePermCertDB(certHandle);
01382        PORT_Free(certHandle);
01383     }
01384     return -1;
01385 }