diff options
Diffstat (limited to 'security/nss/cmd/dbck/dbck.c')
-rw-r--r-- | security/nss/cmd/dbck/dbck.c | 1350 |
1 files changed, 1350 insertions, 0 deletions
diff --git a/security/nss/cmd/dbck/dbck.c b/security/nss/cmd/dbck/dbck.c new file mode 100644 index 000000000..6791a0d19 --- /dev/null +++ b/security/nss/cmd/dbck/dbck.c @@ -0,0 +1,1350 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** dbck.c +** +** utility for fixing corrupt cert databases +** +*/ +#include <stdio.h> +#include <string.h> + +#include "secutil.h" +#include "cdbhdl.h" +#include "certdb.h" +#include "cert.h" +#include "nspr.h" +#include "prtypes.h" +#include "prtime.h" +#include "prlong.h" +#include "pcert.h" +#include "nss.h" + +static char *progName; + +/* placeholders for pointer error types */ +static void *WrongEntry; +static void *NoNickname; +static void *NoSMime; + +typedef enum { + /* 0*/ NoSubjectForCert = 0, + /* 1*/ SubjectHasNoKeyForCert, + /* 2*/ NoNicknameOrSMimeForSubject, + /* 3*/ WrongNicknameForSubject, + /* 4*/ NoNicknameEntry, + /* 5*/ WrongSMimeForSubject, + /* 6*/ NoSMimeEntry, + /* 7*/ NoSubjectForNickname, + /* 8*/ NoSubjectForSMime, + /* 9*/ NicknameAndSMimeEntries, + NUM_ERROR_TYPES +} dbErrorType; + +static char *dbErrorString[NUM_ERROR_TYPES] = { + /* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.", + /* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.", + /* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.", + /* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.", + /* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.", + /* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.", + /* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.", + /* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.", + /* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.", +}; + +static char *errResult[NUM_ERROR_TYPES] = { + "Certificate entries that had no subject entry.", + "Subject entries with no corresponding Certificate entries.", + "Subject entries that had no nickname or S/MIME entries.", + "Redundant nicknames (subjects with the same nickname).", + "Subject entries that had no nickname entry.", + "Redundant email addresses (subjects with the same email address).", + "Subject entries that had no S/MIME entry.", + "Nickname entries that had no subject entry.", + "S/MIME entries that had no subject entry.", + "Subject entries with BOTH nickname and S/MIME entries." +}; + +enum { + GOBOTH = 0, + GORIGHT, + GOLEFT +}; + +typedef struct +{ + PRBool verbose; + PRBool dograph; + PRFileDesc *out; + PRFileDesc *graphfile; + int dbErrors[NUM_ERROR_TYPES]; +} dbDebugInfo; + +struct certDBEntryListNodeStr { + PRCList link; + certDBEntry entry; + void *appData; +}; +typedef struct certDBEntryListNodeStr certDBEntryListNode; + +/* + * A list node for a cert db entry. The index is a unique identifier + * to use for creating generic maps of a db. This struct handles + * the cert, nickname, and smime db entry types, as all three have a + * single handle to a subject entry. + * This structure is pointed to by certDBEntryListNode->appData. + */ +typedef struct +{ + PLArenaPool *arena; + int index; + certDBEntryListNode *pSubject; +} certDBEntryMap; + +/* + * Subject entry is special case, it has bidirectional handles. One + * subject entry can point to several certs (using the same DN), and + * a nickname and/or smime entry. + * This structure is pointed to by certDBEntryListNode->appData. + */ +typedef struct +{ + PLArenaPool *arena; + int index; + int numCerts; + certDBEntryListNode **pCerts; + certDBEntryListNode *pNickname; + certDBEntryListNode *pSMime; +} certDBSubjectEntryMap; + +/* + * A map of a certdb. + */ +typedef struct +{ + int numCerts; + int numSubjects; + int numNicknames; + int numSMime; + int numRevocation; + certDBEntryListNode certs; /* pointer to head of cert list */ + certDBEntryListNode subjects; /* pointer to head of subject list */ + certDBEntryListNode nicknames; /* pointer to head of nickname list */ + certDBEntryListNode smime; /* pointer to head of smime list */ + certDBEntryListNode revocation; /* pointer to head of revocation list */ +} certDBArray; + +/* Cast list to the base element, a certDBEntryListNode. */ +#define LISTNODE_CAST(node) \ + ((certDBEntryListNode *)(node)) + +static void +Usage(char *progName) +{ +#define FPS fprintf(stderr, + FPS "Type %s -H for more detailed descriptions\n", progName); + FPS "Usage: %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", + progName); +#ifdef DORECOVER + FPS " %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", + progName); +#endif + exit(-1); +} + +static void +LongUsage(char *progName) +{ + FPS "%-15s Display this help message.\n", + "-H"); + FPS "%-15s Dump analysis. No changes will be made to the database.\n", + "-D"); + FPS "%-15s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-15s Put database graph in ./mailfile (default is stdout).\n", + " -m"); + FPS "%-15s Verbose mode. Dumps the entire contents of your cert8.db.\n", + " -v"); + FPS "%-15s File to dump verbose output into. (default is stdout)\n", + " -f dumpfile"); +#ifdef DORECOVER + FPS "%-15s Repair the database. The program will look for broken\n", + "-R"); + FPS "%-15s dependencies between subject entries and certificates,\n", + ""); + FPS "%-15s between nickname entries and subjects, and between SMIME\n", + ""); + FPS "%-15s profiles and subjects. Any duplicate entries will be\n", + ""); + FPS "%-15s removed, any missing entries will be created.\n", + ""); + FPS "%-15s File to store new database in (default is new_cert8.db)\n", + " -o newdbname"); + FPS "%-15s Cert database directory (default is ~/.netscape)\n", + " -d certdir"); + FPS "%-15s Prompt before removing any certificates.\n", + " -p"); + FPS "%-15s Keep all possible certificates. Only remove certificates\n", + " -a"); + FPS "%-15s which prevent creation of a consistent database. Thus any\n", + ""); + FPS "%-15s expired or redundant entries will be kept.\n", + ""); + FPS "%-15s Keep redundant nickname/email entries. It is possible\n", + " -r"); + FPS "%-15s only one such entry will be usable.\n", + ""); + FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n", + " -s"); + FPS "%-15s cert. An empty profile will be created.\n", + ""); + FPS "%-15s Keep expired certificates.\n", + " -x"); + FPS "%-15s Verbose mode - report all activity while recovering db.\n", + " -v"); + FPS "%-15s File to dump verbose output into.\n", + " -f dumpfile"); + FPS "\n"); +#endif + exit(-1); +#undef FPS +} + +/******************************************************************* + * + * Functions for dbck. + * + ******************************************************************/ + +void +printHexString(PRFileDesc *out, SECItem *hexval) +{ + unsigned int i; + for (i = 0; i < hexval->len; i++) { + if (i != hexval->len - 1) { + PR_fprintf(out, "%02x:", hexval->data[i]); + } else { + PR_fprintf(out, "%02x", hexval->data[i]); + } + } + PR_fprintf(out, "\n"); +} + +SECStatus +dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile) +{ + int userCert = 0; + CERTCertTrust *trust = cert->trust; + userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) || + (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) || + (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER); + if (num >= 0) { + PR_fprintf(outfile, "Certificate: %3d\n", num); + } else { + PR_fprintf(outfile, "Certificate:\n"); + } + PR_fprintf(outfile, "----------------\n"); + if (userCert) + PR_fprintf(outfile, "(User Cert)\n"); + PR_fprintf(outfile, "## SUBJECT: %s\n", cert->subjectName); + PR_fprintf(outfile, "## ISSUER: %s\n", cert->issuerName); + PR_fprintf(outfile, "## SERIAL NUMBER: "); + printHexString(outfile, &cert->serialNumber); + { /* XXX should be separate function. */ + PRTime timeBefore, timeAfter; + PRExplodedTime beforePrintable, afterPrintable; + char *beforestr, *afterstr; + DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore); + DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter); + PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable); + PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable); + beforestr = PORT_Alloc(100); + afterstr = PORT_Alloc(100); + PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable); + PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable); + PR_fprintf(outfile, "## VALIDITY: %s to %s\n", beforestr, afterstr); + } + PR_fprintf(outfile, "\n"); + return SECSuccess; +} + +SECStatus +dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile) +{ +#if 0 + NSSLOWCERTCertificate *cert; + /* should we check for existing duplicates? */ + cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, + entry->cert.nickname); +#else + CERTCertificate *cert; + cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL); +#endif + if (!cert) { + fprintf(stderr, "Failed to decode certificate.\n"); + return SECFailure; + } + cert->trust = (CERTCertTrust *)&entry->trust; + dumpCertificate(cert, num, outfile); + CERT_DestroyCertificate(cert); + return SECSuccess; +} + +SECStatus +dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile) +{ + char *subjectName = CERT_DerNameToAscii(&entry->derSubject); + + PR_fprintf(outfile, "Subject: %3d\n", num); + PR_fprintf(outfile, "------------\n"); + PR_fprintf(outfile, "## %s\n", subjectName); + if (entry->nickname) + PR_fprintf(outfile, "## Subject nickname: %s\n", entry->nickname); + if (entry->emailAddrs) { + unsigned int n; + for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) { + char *emailAddr = entry->emailAddrs[n]; + if (emailAddr[0]) { + PR_fprintf(outfile, "## Subject email address: %s\n", + emailAddr); + } + } + } + PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts); + PR_fprintf(outfile, "\n"); + PORT_Free(subjectName); + return SECSuccess; +} + +SECStatus +dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile) +{ + PR_fprintf(outfile, "Nickname: %3d\n", num); + PR_fprintf(outfile, "-------------\n"); + PR_fprintf(outfile, "## \"%s\"\n\n", entry->nickname); + return SECSuccess; +} + +SECStatus +dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile) +{ + PR_fprintf(outfile, "S/MIME Profile: %3d\n", num); + PR_fprintf(outfile, "-------------------\n"); + PR_fprintf(outfile, "## \"%s\"\n", entry->emailAddr); +#ifdef OLDWAY + PR_fprintf(outfile, "## OPTIONS: "); + printHexString(outfile, &entry->smimeOptions); + PR_fprintf(outfile, "## TIMESTAMP: "); + printHexString(outfile, &entry->optionsDate); +#else + SECU_PrintAny(stdout, &entry->smimeOptions, "## OPTIONS ", 0); + fflush(stdout); + if (entry->optionsDate.len && entry->optionsDate.data) + PR_fprintf(outfile, "## TIMESTAMP: %.*s\n", + entry->optionsDate.len, entry->optionsDate.data); +#endif + PR_fprintf(outfile, "\n"); + return SECSuccess; +} + +SECStatus +mapCertEntries(certDBArray *dbArray) +{ + certDBEntryCert *certEntry; + certDBEntrySubject *subjectEntry; + certDBEntryListNode *certNode, *subjNode; + certDBSubjectEntryMap *smap; + certDBEntryMap *map; + PLArenaPool *tmparena; + SECItem derSubject; + SECItem certKey; + PRCList *cElem, *sElem; + + /* Arena for decoded entries */ + tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (tmparena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* Iterate over cert entries and map them to subject entries. + * NOTE: mapSubjectEntries must be called first to alloc memory + * for array of subject->cert map. + */ + for (cElem = PR_LIST_HEAD(&dbArray->certs.link); + cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) { + certNode = LISTNODE_CAST(cElem); + certEntry = (certDBEntryCert *)&certNode->entry; + map = (certDBEntryMap *)certNode->appData; + CERT_NameFromDERCert(&certEntry->derCert, &derSubject); + CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey); + /* Loop over found subjects for cert's DN. */ + for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); + sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { + subjNode = LISTNODE_CAST(sElem); + subjectEntry = (certDBEntrySubject *)&subjNode->entry; + if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) { + unsigned int i; + /* Found matching subject name, create link. */ + map->pSubject = subjNode; + /* Make sure subject entry has cert's key. */ + for (i = 0; i < subjectEntry->ncerts; i++) { + if (SECITEM_ItemsAreEqual(&certKey, + &subjectEntry->certKeys[i])) { + /* Found matching cert key. */ + smap = (certDBSubjectEntryMap *)subjNode->appData; + smap->pCerts[i] = certNode; + break; + } + } + } + } + } + PORT_FreeArena(tmparena, PR_FALSE); + return SECSuccess; +} + +SECStatus +mapSubjectEntries(certDBArray *dbArray) +{ + certDBEntrySubject *subjectEntry; + certDBEntryListNode *subjNode; + certDBSubjectEntryMap *subjMap; + PRCList *sElem; + + for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); + sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { + /* Iterate over subject entries and map subjects to nickname + * and smime entries. The cert<->subject map will be handled + * by a subsequent call to mapCertEntries. + */ + subjNode = LISTNODE_CAST(sElem); + subjectEntry = (certDBEntrySubject *)&subjNode->entry; + subjMap = (certDBSubjectEntryMap *)subjNode->appData; + /* need to alloc memory here for array of matching certs. */ + subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, + subjectEntry->ncerts * sizeof(int)); + subjMap->numCerts = subjectEntry->ncerts; + subjMap->pNickname = NoNickname; + subjMap->pSMime = NoSMime; + + if (subjectEntry->nickname) { + /* Subject should have a nickname entry, so create a link. */ + PRCList *nElem; + for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link); + nElem != &dbArray->nicknames.link; + nElem = PR_NEXT_LINK(nElem)) { + certDBEntryListNode *nickNode; + certDBEntryNickname *nicknameEntry; + /* Look for subject's nickname in nickname entries. */ + nickNode = LISTNODE_CAST(nElem); + nicknameEntry = (certDBEntryNickname *)&nickNode->entry; + if (PL_strcmp(subjectEntry->nickname, + nicknameEntry->nickname) == 0) { + /* Found a nickname entry for subject's nickname. */ + if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject, + &nicknameEntry->subjectName)) { + certDBEntryMap *nickMap; + nickMap = (certDBEntryMap *)nickNode->appData; + /* Nickname and subject match. */ + subjMap->pNickname = nickNode; + nickMap->pSubject = subjNode; + } else if (subjMap->pNickname == NoNickname) { + /* Nickname entry found is for diff. subject. */ + subjMap->pNickname = WrongEntry; + } + } + } + } + if (subjectEntry->emailAddrs) { + unsigned int n; + for (n = 0; n < subjectEntry->nemailAddrs && + subjectEntry->emailAddrs[n]; + ++n) { + char *emailAddr = subjectEntry->emailAddrs[n]; + if (emailAddr[0]) { + PRCList *mElem; + /* Subject should have an smime entry, so create a link. */ + for (mElem = PR_LIST_HEAD(&dbArray->smime.link); + mElem != &dbArray->smime.link; + mElem = PR_NEXT_LINK(mElem)) { + certDBEntryListNode *smimeNode; + certDBEntrySMime *smimeEntry; + /* Look for subject's email in S/MIME entries. */ + smimeNode = LISTNODE_CAST(mElem); + smimeEntry = (certDBEntrySMime *)&smimeNode->entry; + if (PL_strcmp(emailAddr, + smimeEntry->emailAddr) == 0) { + /* Found a S/MIME entry for subject's email. */ + if (SECITEM_ItemsAreEqual( + &subjectEntry->derSubject, + &smimeEntry->subjectName)) { + certDBEntryMap *smimeMap; + /* S/MIME entry and subject match. */ + subjMap->pSMime = smimeNode; + smimeMap = (certDBEntryMap *)smimeNode->appData; + smimeMap->pSubject = subjNode; + } else if (subjMap->pSMime == NoSMime) { + /* S/MIME entry found is for diff. subject. */ + subjMap->pSMime = WrongEntry; + } + } + } /* end for */ + } /* endif (emailAddr[0]) */ + } /* end for */ + } /* endif (subjectEntry->emailAddrs) */ + } + return SECSuccess; +} + +void +printnode(dbDebugInfo *info, const char *str, int num) +{ + if (!info->dograph) + return; + if (num < 0) { + PR_fprintf(info->graphfile, str); + } else { + PR_fprintf(info->graphfile, str, num); + } +} + +PRBool +map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent) +{ + if (mapPtr == NULL) { + if (indent > 0) + printnode(info, " ", -1); + if (indent >= 0) + printnode(info, "******************* ", -1); + return PR_FALSE; + } else if (mapPtr == WrongEntry) { + if (indent > 0) + printnode(info, " ", -1); + if (indent >= 0) + printnode(info, "??????????????????? ", -1); + return PR_FALSE; + } else { + return PR_TRUE; + } +} + +/* these call each other */ +void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, + int direction); +void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, + int direction); +void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, + int direction, int optindex, int opttype); +void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, + int direction); + +/* Given an smime entry, print its unique identifier. If GOLEFT is + * specified, print the cert<-subject<-smime map, else just print + * the smime entry. + */ +void +print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction) +{ + certDBSubjectEntryMap *subjMap; + certDBEntryListNode *subjNode; + if (direction == GOLEFT) { + /* Need to output subject and cert first, see print_subject_graph */ + subjNode = smimeMap->pSubject; + if (map_handle_is_ok(info, (void *)subjNode, 1)) { + subjMap = (certDBSubjectEntryMap *)subjNode->appData; + print_subject_graph(info, subjMap, GOLEFT, + smimeMap->index, certDBEntryTypeSMimeProfile); + } else { + printnode(info, "<---- S/MIME %5d ", smimeMap->index); + info->dbErrors[NoSubjectForSMime]++; + } + } else { + printnode(info, "S/MIME %5d ", smimeMap->index); + } +} + +/* Given a nickname entry, print its unique identifier. If GOLEFT is + * specified, print the cert<-subject<-nickname map, else just print + * the nickname entry. + */ +void +print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction) +{ + certDBSubjectEntryMap *subjMap; + certDBEntryListNode *subjNode; + if (direction == GOLEFT) { + /* Need to output subject and cert first, see print_subject_graph */ + subjNode = nickMap->pSubject; + if (map_handle_is_ok(info, (void *)subjNode, 1)) { + subjMap = (certDBSubjectEntryMap *)subjNode->appData; + print_subject_graph(info, subjMap, GOLEFT, + nickMap->index, certDBEntryTypeNickname); + } else { + printnode(info, "<---- Nickname %5d ", nickMap->index); + info->dbErrors[NoSubjectForNickname]++; + } + } else { + printnode(info, "Nickname %5d ", nickMap->index); + } +} + +/* Given a subject entry, if going right print the graph of the nickname|smime + * that it maps to (by its unique identifier); and if going left + * print the list of certs that it points to. + */ +void +print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, + int direction, int optindex, int opttype) +{ + certDBEntryMap *map; + certDBEntryListNode *node; + int i; + /* The first line of output always contains the cert id, subject id, + * and nickname|smime id. Subsequent lines may contain additional + * cert id's for the subject if going left or both directions. + * Ex. of printing the graph for a subject entry: + * Cert 3 <- Subject 5 -> Nickname 32 + * Cert 8 / + * Cert 9 / + * means subject 5 has 3 certs, 3, 8, and 9, and corresponds + * to nickname entry 32. + * To accomplish the above, it is required to dump the entire first + * line left-to-right, regardless of the input direction, and then + * finish up any remaining cert entries. Hence the code is uglier + * than one may expect. + */ + if (direction == GOLEFT || direction == GOBOTH) { + /* In this case, nothing should be output until the first cert is + * located and output (cert 3 in the above example). + */ + if (subjMap->numCerts == 0 || subjMap->pCerts == NULL) + /* XXX uh-oh */ + return; + /* get the first cert and dump it. */ + node = subjMap->pCerts[0]; + if (map_handle_is_ok(info, (void *)node, 0)) { + map = (certDBEntryMap *)node->appData; + /* going left here stops. */ + print_cert_graph(info, map, GOLEFT); + } else { + info->dbErrors[SubjectHasNoKeyForCert]++; + } + /* Now it is safe to output the subject id. */ + if (direction == GOLEFT) + printnode(info, "Subject %5d <---- ", subjMap->index); + else /* direction == GOBOTH */ + printnode(info, "Subject %5d ----> ", subjMap->index); + } + if (direction == GORIGHT || direction == GOBOTH) { + /* Okay, now output the nickname|smime for this subject. */ + if (direction != GOBOTH) /* handled above */ + printnode(info, "Subject %5d ----> ", subjMap->index); + if (subjMap->pNickname) { + node = subjMap->pNickname; + if (map_handle_is_ok(info, (void *)node, 0)) { + map = (certDBEntryMap *)node->appData; + /* going right here stops. */ + print_nickname_graph(info, map, GORIGHT); + } + } + if (subjMap->pSMime) { + node = subjMap->pSMime; + if (map_handle_is_ok(info, (void *)node, 0)) { + map = (certDBEntryMap *)node->appData; + /* going right here stops. */ + print_smime_graph(info, map, GORIGHT); + } + } + if (!subjMap->pNickname && !subjMap->pSMime) { + printnode(info, "******************* ", -1); + info->dbErrors[NoNicknameOrSMimeForSubject]++; + } + if (subjMap->pNickname && subjMap->pSMime) { + info->dbErrors[NicknameAndSMimeEntries]++; + } + } + if (direction != GORIGHT) { /* going right has only one cert */ + if (opttype == certDBEntryTypeNickname) + printnode(info, "Nickname %5d ", optindex); + else if (opttype == certDBEntryTypeSMimeProfile) + printnode(info, "S/MIME %5d ", optindex); + for (i = 1 /* 1st one already done */; i < subjMap->numCerts; i++) { + printnode(info, "\n", -1); /* start a new line */ + node = subjMap->pCerts[i]; + if (map_handle_is_ok(info, (void *)node, 0)) { + map = (certDBEntryMap *)node->appData; + /* going left here stops. */ + print_cert_graph(info, map, GOLEFT); + printnode(info, "/", -1); + } + } + } +} + +/* Given a cert entry, print its unique identifer. If GORIGHT is specified, + * print the cert->subject->nickname|smime map, else just print + * the cert entry. + */ +void +print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction) +{ + certDBSubjectEntryMap *subjMap; + certDBEntryListNode *subjNode; + if (direction == GOLEFT) { + printnode(info, "Cert %5d <---- ", certMap->index); + /* only want cert entry, terminate here. */ + return; + } + /* Keep going right then. */ + printnode(info, "Cert %5d ----> ", certMap->index); + subjNode = certMap->pSubject; + if (map_handle_is_ok(info, (void *)subjNode, 0)) { + subjMap = (certDBSubjectEntryMap *)subjNode->appData; + print_subject_graph(info, subjMap, GORIGHT, -1, -1); + } else { + info->dbErrors[NoSubjectForCert]++; + } +} + +SECStatus +computeDBGraph(certDBArray *dbArray, dbDebugInfo *info) +{ + PRCList *cElem, *sElem, *nElem, *mElem; + certDBEntryListNode *node; + certDBEntryMap *map; + certDBSubjectEntryMap *subjMap; + + /* Graph is of this form: + * + * certs: + * cert ---> subject ---> (nickname|smime) + * + * subjects: + * cert <--- subject ---> (nickname|smime) + * + * nicknames and smime: + * cert <--- subject <--- (nickname|smime) + */ + + /* Print cert graph. */ + for (cElem = PR_LIST_HEAD(&dbArray->certs.link); + cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) { + /* Print graph of everything to right of cert entry. */ + node = LISTNODE_CAST(cElem); + map = (certDBEntryMap *)node->appData; + print_cert_graph(info, map, GORIGHT); + printnode(info, "\n", -1); + } + printnode(info, "\n", -1); + + /* Print subject graph. */ + for (sElem = PR_LIST_HEAD(&dbArray->subjects.link); + sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) { + /* Print graph of everything to both sides of subject entry. */ + node = LISTNODE_CAST(sElem); + subjMap = (certDBSubjectEntryMap *)node->appData; + print_subject_graph(info, subjMap, GOBOTH, -1, -1); + printnode(info, "\n", -1); + } + printnode(info, "\n", -1); + + /* Print nickname graph. */ + for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link); + nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) { + /* Print graph of everything to left of nickname entry. */ + node = LISTNODE_CAST(nElem); + map = (certDBEntryMap *)node->appData; + print_nickname_graph(info, map, GOLEFT); + printnode(info, "\n", -1); + } + printnode(info, "\n", -1); + + /* Print smime graph. */ + for (mElem = PR_LIST_HEAD(&dbArray->smime.link); + mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) { + /* Print graph of everything to left of smime entry. */ + node = LISTNODE_CAST(mElem); + if (node == NULL) + break; + map = (certDBEntryMap *)node->appData; + print_smime_graph(info, map, GOLEFT); + printnode(info, "\n", -1); + } + printnode(info, "\n", -1); + + return SECSuccess; +} + +/* + * List the entries in the db, showing handles between entry types. + */ +void +verboseOutput(certDBArray *dbArray, dbDebugInfo *info) +{ + int i, ref; + PRCList *elem; + certDBEntryListNode *node; + certDBEntryMap *map; + certDBSubjectEntryMap *smap; + certDBEntrySubject *subjectEntry; + + /* List certs */ + for (elem = PR_LIST_HEAD(&dbArray->certs.link); + elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) { + node = LISTNODE_CAST(elem); + map = (certDBEntryMap *)node->appData; + dumpCertEntry((certDBEntryCert *)&node->entry, map->index, info->out); + /* walk the cert handle to it's subject entry */ + if (map_handle_is_ok(info, map->pSubject, -1)) { + smap = (certDBSubjectEntryMap *)map->pSubject->appData; + ref = smap->index; + PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); + } else { + PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); + } + } + /* List subjects */ + for (elem = PR_LIST_HEAD(&dbArray->subjects.link); + elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) { + int refs = 0; + node = LISTNODE_CAST(elem); + subjectEntry = (certDBEntrySubject *)&node->entry; + smap = (certDBSubjectEntryMap *)node->appData; + dumpSubjectEntry(subjectEntry, smap->index, info->out); + /* iterate over subject's certs */ + for (i = 0; i < smap->numCerts; i++) { + /* walk each subject handle to it's cert entries */ + if (map_handle_is_ok(info, smap->pCerts[i], -1)) { + ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index; + PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref); + } else { + PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i); + } + } + if (subjectEntry->nickname) { + ++refs; + /* walk each subject handle to it's nickname entry */ + if (map_handle_is_ok(info, smap->pNickname, -1)) { + ref = ((certDBEntryMap *)smap->pNickname->appData)->index; + PR_fprintf(info->out, "-->(nickname %d)\n", ref); + } else { + PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n"); + } + } + if (subjectEntry->nemailAddrs && + subjectEntry->emailAddrs && + subjectEntry->emailAddrs[0] && + subjectEntry->emailAddrs[0][0]) { + ++refs; + /* walk each subject handle to it's smime entry */ + if (map_handle_is_ok(info, smap->pSMime, -1)) { + ref = ((certDBEntryMap *)smap->pSMime->appData)->index; + PR_fprintf(info->out, "-->(s/mime %d)\n", ref); + } else { + PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n"); + } + } + if (!refs) { + PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n"); + } + PR_fprintf(info->out, "\n\n"); + } + for (elem = PR_LIST_HEAD(&dbArray->nicknames.link); + elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) { + node = LISTNODE_CAST(elem); + map = (certDBEntryMap *)node->appData; + dumpNicknameEntry((certDBEntryNickname *)&node->entry, map->index, + info->out); + if (map_handle_is_ok(info, map->pSubject, -1)) { + ref = ((certDBEntryMap *)map->pSubject->appData)->index; + PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); + } else { + PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); + } + } + for (elem = PR_LIST_HEAD(&dbArray->smime.link); + elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) { + node = LISTNODE_CAST(elem); + map = (certDBEntryMap *)node->appData; + dumpSMimeEntry((certDBEntrySMime *)&node->entry, map->index, info->out); + if (map_handle_is_ok(info, map->pSubject, -1)) { + ref = ((certDBEntryMap *)map->pSubject->appData)->index; + PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref); + } else { + PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n"); + } + } + PR_fprintf(info->out, "\n\n"); +} + +/* A callback function, intended to be called from nsslowcert_TraverseDBEntries + * Builds a PRCList of DB entries of the specified type. + */ +SECStatus +SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, + certDBEntryType entryType, void *pdata) +{ + certDBEntry *entry; + certDBEntryListNode *node; + PRCList *list = (PRCList *)pdata; + + if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL); + if (!entry) { + return SECSuccess; /* skip it */ + } + node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode); + if (!node) { + /* DestroyDBEntry(entry); */ + PLArenaPool *arena = entry->common.arena; + PORT_Memset(&entry->common, 0, sizeof entry->common); + PORT_FreeArena(arena, PR_FALSE); + return SECFailure; + } + node->entry = *entry; /* crude but effective. */ + PR_INIT_CLIST(&node->link); + PR_INSERT_BEFORE(&node->link, list); + return SECSuccess; +} + +int +fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, + certDBEntryListNode *list) +{ + PRCList *elem; + certDBEntryListNode *node; + certDBEntryMap *mnode; + certDBSubjectEntryMap *smnode; + PLArenaPool *arena; + int count = 0; + + /* Initialize a dummy entry in the list. The list head will be the + * next element, so this element is skipped by for loops. + */ + PR_INIT_CLIST((PRCList *)list); + /* Collect all of the cert db entries for this type into a list. */ + nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list); + + for (elem = PR_LIST_HEAD(&list->link); + elem != &list->link; elem = PR_NEXT_LINK(elem)) { + /* Iterate over the entries and ... */ + node = (certDBEntryListNode *)elem; + if (type != certDBEntryTypeSubject) { + arena = PORT_NewArena(sizeof(*mnode)); + mnode = PORT_ArenaZNew(arena, certDBEntryMap); + mnode->arena = arena; + /* ... assign a unique index number to each node, and ... */ + mnode->index = count; + /* ... set the map pointer for the node. */ + node->appData = (void *)mnode; + } else { + /* allocate some room for the cert pointers also */ + arena = PORT_NewArena(sizeof(*smnode) + 20 * sizeof(void *)); + smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap); + smnode->arena = arena; + smnode->index = count; + node->appData = (void *)smnode; + } + count++; + } + return count; +} + +void +freeDBEntryList(PRCList *list) +{ + PRCList *next, *elem; + certDBEntryListNode *node; + certDBEntryMap *map; + + for (elem = PR_LIST_HEAD(list); elem != list;) { + next = PR_NEXT_LINK(elem); + node = (certDBEntryListNode *)elem; + map = (certDBEntryMap *)node->appData; + PR_REMOVE_LINK(&node->link); + PORT_FreeArena(map->arena, PR_TRUE); + PORT_FreeArena(node->entry.common.arena, PR_TRUE); + elem = next; + } +} + +void +DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, + PRFileDesc *mailfile) +{ + int i, nCertsFound, nSubjFound, nErr; + int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation; + PRCList *elem; + char c; + dbDebugInfo info; + certDBArray dbArray; + + PORT_Memset(&dbArray, 0, sizeof(dbArray)); + PORT_Memset(&info, 0, sizeof(info)); + info.verbose = (PRBool)(out != NULL); + info.dograph = info.verbose; + info.out = (out) ? out : PR_STDOUT; + info.graphfile = mailfile ? mailfile : PR_STDOUT; + + /* Fill the array structure with cert/subject/nickname/smime entries. */ + dbArray.numCerts = fillDBEntryArray(handle, certDBEntryTypeCert, + &dbArray.certs); + dbArray.numSubjects = fillDBEntryArray(handle, certDBEntryTypeSubject, + &dbArray.subjects); + dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, + &dbArray.nicknames); + dbArray.numSMime = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, + &dbArray.smime); + dbArray.numRevocation = fillDBEntryArray(handle, certDBEntryTypeRevocation, + &dbArray.revocation); + + /* Compute the map between the database entries. */ + mapSubjectEntries(&dbArray); + mapCertEntries(&dbArray); + computeDBGraph(&dbArray, &info); + + /* Store the totals for later reference. */ + nCerts = dbArray.numCerts; + nSubjects = dbArray.numSubjects; + nNicknames = dbArray.numNicknames; + nSMime = dbArray.numSMime; + nRevocation = dbArray.numRevocation; + nSubjCerts = 0; + for (elem = PR_LIST_HEAD(&dbArray.subjects.link); + elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) { + certDBSubjectEntryMap *smap; + smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData; + nSubjCerts += smap->numCerts; + } + + if (info.verbose) { + /* Dump the database contents. */ + verboseOutput(&dbArray, &info); + } + + freeDBEntryList(&dbArray.certs.link); + freeDBEntryList(&dbArray.subjects.link); + freeDBEntryList(&dbArray.nicknames.link); + freeDBEntryList(&dbArray.smime.link); + freeDBEntryList(&dbArray.revocation.link); + + PR_fprintf(info.out, "\n"); + PR_fprintf(info.out, "Database statistics:\n"); + PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", + nCerts); + PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", + nSubjects); + PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", + nSubjCerts); + PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", + nNicknames); + PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", + nSMime); + PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", + nRevocation); + PR_fprintf(info.out, "\n"); + + nErr = 0; + for (i = 0; i < NUM_ERROR_TYPES; i++) { + PR_fprintf(info.out, "E%d: Found %4d %s\n", + i, info.dbErrors[i], errResult[i]); + nErr += info.dbErrors[i]; + } + PR_fprintf(info.out, "--------------\n Found %4d errors in database.\n", + nErr); + + PR_fprintf(info.out, "\nCertificates:\n"); + PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, + SubjectHasNoKeyForCert); + nCertsFound = nSubjCerts + + info.dbErrors[NoSubjectForCert] + + info.dbErrors[SubjectHasNoKeyForCert]; + c = (nCertsFound == nCerts) ? '=' : '!'; + PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, + info.dbErrors[NoSubjectForCert], + info.dbErrors[SubjectHasNoKeyForCert]); + PR_fprintf(info.out, "\nSubjects:\n"); + PR_fprintf(info.out, + "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n", + NoNicknameOrSMimeForSubject, + WrongNicknameForSubject, + NoNicknameEntry, + WrongSMimeForSubject, + NoSMimeEntry, + NoSubjectForNickname, + NoSubjectForSMime, + NicknameAndSMimeEntries); + nSubjFound = nNicknames + nSMime + + info.dbErrors[NoNicknameOrSMimeForSubject] + + info.dbErrors[WrongNicknameForSubject] + + info.dbErrors[NoNicknameEntry] + + info.dbErrors[WrongSMimeForSubject] + + info.dbErrors[NoSMimeEntry] - + info.dbErrors[NoSubjectForNickname] - + info.dbErrors[NoSubjectForSMime] - + info.dbErrors[NicknameAndSMimeEntries]; + c = (nSubjFound == nSubjects) ? '=' : '!'; + PR_fprintf(info.out, + "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n", + nSubjects, c, nNicknames, nSMime, + info.dbErrors[NoNicknameOrSMimeForSubject], + info.dbErrors[WrongNicknameForSubject], + info.dbErrors[NoNicknameEntry], + info.dbErrors[WrongSMimeForSubject], + info.dbErrors[NoSMimeEntry], + info.dbErrors[NoSubjectForNickname], + info.dbErrors[NoSubjectForSMime], + info.dbErrors[NicknameAndSMimeEntries]); + PR_fprintf(info.out, "\n"); +} + +#ifdef DORECOVER +#include "dbrecover.c" +#endif /* DORECOVER */ + +enum { + cmd_Debug = 0, + cmd_LongUsage, + cmd_Recover +}; + +enum { + opt_KeepAll = 0, + opt_CertDir, + opt_Dumpfile, + opt_InputDB, + opt_OutputDB, + opt_Mailfile, + opt_Prompt, + opt_KeepRedundant, + opt_KeepNoSMimeProfile, + opt_Verbose, + opt_KeepExpired +}; + +static secuCommandFlag dbck_commands[] = + { + { /* cmd_Debug, */ 'D', PR_FALSE, 0, PR_FALSE }, + { /* cmd_LongUsage,*/ 'H', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Recover, */ 'R', PR_FALSE, 0, PR_FALSE } + }; + +static secuCommandFlag dbck_options[] = + { + { /* opt_KeepAll, */ 'a', PR_FALSE, 0, PR_FALSE }, + { /* opt_CertDir, */ 'd', PR_TRUE, 0, PR_FALSE }, + { /* opt_Dumpfile, */ 'f', PR_TRUE, 0, PR_FALSE }, + { /* opt_InputDB, */ 'i', PR_TRUE, 0, PR_FALSE }, + { /* opt_OutputDB, */ 'o', PR_TRUE, 0, PR_FALSE }, + { /* opt_Mailfile, */ 'm', PR_FALSE, 0, PR_FALSE }, + { /* opt_Prompt, */ 'p', PR_FALSE, 0, PR_FALSE }, + { /* opt_KeepRedundant, */ 'r', PR_FALSE, 0, PR_FALSE }, + { /* opt_KeepNoSMimeProfile,*/ 's', PR_FALSE, 0, PR_FALSE }, + { /* opt_Verbose, */ 'v', PR_FALSE, 0, PR_FALSE }, + { /* opt_KeepExpired, */ 'x', PR_FALSE, 0, PR_FALSE } + }; + +#define CERT_DB_FMT "%s/cert%s.db" + +static char * +dbck_certdb_name_cb(void *arg, int dbVersion) +{ + const char *configdir = (const char *)arg; + const char *dbver; + char *smpname = NULL; + char *dbname = NULL; + + switch (dbVersion) { + case 8: + dbver = "8"; + break; + case 7: + dbver = "7"; + break; + case 6: + dbver = "6"; + break; + case 5: + dbver = "5"; + break; + case 4: + default: + dbver = ""; + break; + } + + /* make sure we return something allocated with PORT_ so we have properly + * matched frees at the end */ + smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver); + if (smpname) { + dbname = PORT_Strdup(smpname); + PR_smprintf_free(smpname); + } + return dbname; +} + +int +main(int argc, char **argv) +{ + NSSLOWCERTCertDBHandle *certHandle; + + PRFileDesc *mailfile = NULL; + PRFileDesc *dumpfile = NULL; + + char *pathname = 0; + char *fullname = 0; + char *newdbname = 0; + + PRBool removeExpired, requireProfile, singleEntry; + SECStatus rv; + secuCommand dbck; + + dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag); + dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag); + dbck.commands = dbck_commands; + dbck.options = dbck_options; + + progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + rv = SECU_ParseCommandLine(argc, argv, progName, &dbck); + + if (rv != SECSuccess) + Usage(progName); + + if (dbck.commands[cmd_LongUsage].activated) + LongUsage(progName); + + if (!dbck.commands[cmd_Debug].activated && + !dbck.commands[cmd_Recover].activated) { + PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n"); + Usage(progName); + } + + removeExpired = !(dbck.options[opt_KeepAll].activated || + dbck.options[opt_KeepExpired].activated); + + requireProfile = !(dbck.options[opt_KeepAll].activated || + dbck.options[opt_KeepNoSMimeProfile].activated); + + singleEntry = !(dbck.options[opt_KeepAll].activated || + dbck.options[opt_KeepRedundant].activated); + + if (dbck.options[opt_OutputDB].activated) { + newdbname = PL_strdup(dbck.options[opt_OutputDB].arg); + } else { + newdbname = PL_strdup("new_cert8.db"); + } + + /* Create a generic graph of the database. */ + if (dbck.options[opt_Mailfile].activated) { + mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660); + if (!mailfile) { + fprintf(stderr, "Unable to create mailfile.\n"); + return -1; + } + } + + /* Dump all debugging info while running. */ + if (dbck.options[opt_Verbose].activated) { + if (dbck.options[opt_Dumpfile].activated) { + dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg, + PR_RDWR | PR_CREATE_FILE, 00660); + if (!dumpfile) { + fprintf(stderr, "Unable to create dumpfile.\n"); + return -1; + } + } else { + dumpfile = PR_STDOUT; + } + } + + /* Set the cert database directory. */ + if (dbck.options[opt_CertDir].activated) { + SECU_ConfigDirectory(dbck.options[opt_CertDir].arg); + } + + pathname = SECU_ConfigDirectory(NULL); + + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + rv = NSS_NoDB_Init(pathname); + if (rv != SECSuccess) { + fprintf(stderr, "NSS_NoDB_Init failed\n"); + return -1; + } + + certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle); + if (!certHandle) { + SECU_PrintError(progName, "unable to get database handle"); + return -1; + } + certHandle->ref = 1; + +#ifdef NOTYET + /* Open the possibly corrupt database. */ + if (dbck.options[opt_InputDB].activated) { + PRFileInfo fileInfo; + fullname = PR_smprintf("%s/%s", pathname, + dbck.options[opt_InputDB].arg); + if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) { + fprintf(stderr, "Unable to read file \"%s\".\n", fullname); + return -1; + } + rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE); + } else +#endif + { +/* Use the default. */ +#ifdef NOTYET + fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION); + if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) { + fprintf(stderr, "Unable to read file \"%s\".\n", fullname); + return -1; + } +#endif + rv = nsslowcert_OpenCertDB(certHandle, + PR_TRUE, /* readOnly */ + NULL, /* rdb appName */ + "", /* rdb prefix */ + dbck_certdb_name_cb, /* namecb */ + pathname, /* configDir */ + PR_FALSE); /* volatile */ + } + + if (rv) { + SECU_PrintError(progName, "unable to open cert database"); + return -1; + } + + if (dbck.commands[cmd_Debug].activated) { + DBCK_DebugDB(certHandle, dumpfile, mailfile); + return 0; + } + +#ifdef DORECOVER + if (dbck.commands[cmd_Recover].activated) { + DBCK_ReconstructDBFromCerts(certHandle, newdbname, + dumpfile, removeExpired, + requireProfile, singleEntry, + dbck.options[opt_Prompt].activated); + return 0; + } +#endif + + if (mailfile) + PR_Close(mailfile); + if (dumpfile) + PR_Close(dumpfile); + if (certHandle) { + nsslowcert_ClosePermCertDB(certHandle); + PORT_Free(certHandle); + } + return -1; +} |