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