security/nss/cmd/dbck/dbck.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/cmd/dbck/dbck.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1353 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 +** dbck.c
    1.10 +**
    1.11 +** utility for fixing corrupt cert databases
    1.12 +**
    1.13 +*/
    1.14 +#include <stdio.h>
    1.15 +#include <string.h>
    1.16 +
    1.17 +#include "secutil.h"
    1.18 +#include "cdbhdl.h"
    1.19 +#include "certdb.h"
    1.20 +#include "cert.h"
    1.21 +#include "nspr.h"
    1.22 +#include "prtypes.h"
    1.23 +#include "prtime.h"
    1.24 +#include "prlong.h"
    1.25 +#include "pcert.h"
    1.26 +#include "nss.h"
    1.27 +
    1.28 +static char *progName;
    1.29 +
    1.30 +/* placeholders for pointer error types */
    1.31 +static void *WrongEntry;
    1.32 +static void *NoNickname;
    1.33 +static void *NoSMime;
    1.34 +
    1.35 +typedef enum {
    1.36 +/* 0*/ NoSubjectForCert = 0,
    1.37 +/* 1*/ SubjectHasNoKeyForCert,
    1.38 +/* 2*/ NoNicknameOrSMimeForSubject,
    1.39 +/* 3*/ WrongNicknameForSubject,
    1.40 +/* 4*/ NoNicknameEntry,
    1.41 +/* 5*/ WrongSMimeForSubject,
    1.42 +/* 6*/ NoSMimeEntry,
    1.43 +/* 7*/ NoSubjectForNickname,
    1.44 +/* 8*/ NoSubjectForSMime,
    1.45 +/* 9*/ NicknameAndSMimeEntries,
    1.46 +    NUM_ERROR_TYPES
    1.47 +} dbErrorType;
    1.48 +
    1.49 +static char *dbErrorString[NUM_ERROR_TYPES] = {
    1.50 +/* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
    1.51 +/* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
    1.52 +/* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
    1.53 +/* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
    1.54 +/* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
    1.55 +/* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
    1.56 +/* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
    1.57 +/* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
    1.58 +/* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
    1.59 +};
    1.60 +
    1.61 +static char *errResult[NUM_ERROR_TYPES] = {
    1.62 +    "Certificate entries that had no subject entry.", 
    1.63 +    "Subject entries with no corresponding Certificate entries.", 
    1.64 +    "Subject entries that had no nickname or S/MIME entries.",
    1.65 +    "Redundant nicknames (subjects with the same nickname).",
    1.66 +    "Subject entries that had no nickname entry.",
    1.67 +    "Redundant email addresses (subjects with the same email address).",
    1.68 +    "Subject entries that had no S/MIME entry.",
    1.69 +    "Nickname entries that had no subject entry.", 
    1.70 +    "S/MIME entries that had no subject entry.",
    1.71 +    "Subject entries with BOTH nickname and S/MIME entries."
    1.72 +};
    1.73 +
    1.74 +
    1.75 +enum {
    1.76 +    GOBOTH = 0,
    1.77 +    GORIGHT,
    1.78 +    GOLEFT
    1.79 +};
    1.80 +
    1.81 +typedef struct
    1.82 +{
    1.83 +    PRBool verbose;
    1.84 +    PRBool dograph;
    1.85 +    PRFileDesc *out;
    1.86 +    PRFileDesc *graphfile;
    1.87 +    int dbErrors[NUM_ERROR_TYPES];
    1.88 +} dbDebugInfo;
    1.89 +
    1.90 +struct certDBEntryListNodeStr {
    1.91 +    PRCList link;
    1.92 +    certDBEntry entry;
    1.93 +    void *appData;
    1.94 +};
    1.95 +typedef struct certDBEntryListNodeStr  certDBEntryListNode;
    1.96 +
    1.97 +/*
    1.98 + * A list node for a cert db entry.  The index is a unique identifier
    1.99 + * to use for creating generic maps of a db.  This struct handles
   1.100 + * the cert, nickname, and smime db entry types, as all three have a
   1.101 + * single handle to a subject entry.
   1.102 + * This structure is pointed to by certDBEntryListNode->appData.
   1.103 + */
   1.104 +typedef struct 
   1.105 +{
   1.106 +    PLArenaPool *arena;
   1.107 +    int index;
   1.108 +    certDBEntryListNode *pSubject;
   1.109 +} certDBEntryMap;
   1.110 +
   1.111 +/*
   1.112 + * Subject entry is special case, it has bidirectional handles.  One
   1.113 + * subject entry can point to several certs (using the same DN), and
   1.114 + * a nickname and/or smime entry.
   1.115 + * This structure is pointed to by certDBEntryListNode->appData.
   1.116 + */
   1.117 +typedef struct
   1.118 +{
   1.119 +    PLArenaPool *arena;
   1.120 +    int index;
   1.121 +    int numCerts;
   1.122 +    certDBEntryListNode **pCerts;
   1.123 +    certDBEntryListNode *pNickname;
   1.124 +    certDBEntryListNode *pSMime;
   1.125 +} certDBSubjectEntryMap;
   1.126 +
   1.127 +/*
   1.128 + * A map of a certdb.
   1.129 + */
   1.130 +typedef struct
   1.131 +{
   1.132 +    int numCerts;
   1.133 +    int numSubjects;
   1.134 +    int numNicknames;
   1.135 +    int numSMime;
   1.136 +    int numRevocation;
   1.137 +    certDBEntryListNode certs;      /* pointer to head of cert list */
   1.138 +    certDBEntryListNode subjects;   /* pointer to head of subject list */
   1.139 +    certDBEntryListNode nicknames;  /* pointer to head of nickname list */
   1.140 +    certDBEntryListNode smime;      /* pointer to head of smime list */
   1.141 +    certDBEntryListNode revocation; /* pointer to head of revocation list */
   1.142 +} certDBArray;
   1.143 +
   1.144 +/* Cast list to the base element, a certDBEntryListNode. */
   1.145 +#define LISTNODE_CAST(node) \
   1.146 +    ((certDBEntryListNode *)(node))
   1.147 +
   1.148 +static void 
   1.149 +Usage(char *progName)
   1.150 +{
   1.151 +#define FPS fprintf(stderr, 
   1.152 +    FPS "Type %s -H for more detailed descriptions\n", progName);
   1.153 +    FPS "Usage:  %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", 
   1.154 +	progName);
   1.155 +#ifdef DORECOVER
   1.156 +    FPS "        %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", 
   1.157 +	progName);
   1.158 +#endif
   1.159 +    exit(-1);
   1.160 +}
   1.161 +
   1.162 +static void
   1.163 +LongUsage(char *progName)
   1.164 +{
   1.165 +    FPS "%-15s Display this help message.\n",
   1.166 +	"-H");
   1.167 +    FPS "%-15s Dump analysis.  No changes will be made to the database.\n",
   1.168 +	"-D");
   1.169 +    FPS "%-15s Cert database directory (default is ~/.netscape)\n",
   1.170 +	"   -d certdir");
   1.171 +    FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
   1.172 +	"   -m");
   1.173 +    FPS "%-15s Verbose mode.  Dumps the entire contents of your cert8.db.\n",
   1.174 +	"   -v");
   1.175 +    FPS "%-15s File to dump verbose output into. (default is stdout)\n",
   1.176 +	"   -f dumpfile");
   1.177 +#ifdef DORECOVER
   1.178 +    FPS "%-15s Repair the database.  The program will look for broken\n",
   1.179 +	"-R");
   1.180 +    FPS "%-15s dependencies between subject entries and certificates,\n",
   1.181 +        "");
   1.182 +    FPS "%-15s between nickname entries and subjects, and between SMIME\n",
   1.183 +        "");
   1.184 +    FPS "%-15s profiles and subjects.  Any duplicate entries will be\n",
   1.185 +        "");
   1.186 +    FPS "%-15s removed, any missing entries will be created.\n",
   1.187 +        "");
   1.188 +    FPS "%-15s File to store new database in (default is new_cert8.db)\n",
   1.189 +	"   -o newdbname");
   1.190 +    FPS "%-15s Cert database directory (default is ~/.netscape)\n",
   1.191 +	"   -d certdir");
   1.192 +    FPS "%-15s Prompt before removing any certificates.\n",
   1.193 +        "   -p");
   1.194 +    FPS "%-15s Keep all possible certificates.  Only remove certificates\n",
   1.195 +	"   -a");
   1.196 +    FPS "%-15s which prevent creation of a consistent database.  Thus any\n",
   1.197 +	"");
   1.198 +    FPS "%-15s expired or redundant entries will be kept.\n",
   1.199 +	"");
   1.200 +    FPS "%-15s Keep redundant nickname/email entries.  It is possible\n",
   1.201 +	"   -r");
   1.202 +    FPS "%-15s only one such entry will be usable.\n",
   1.203 +	"");
   1.204 +    FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
   1.205 +	"   -s");
   1.206 +    FPS "%-15s cert.  An empty profile will be created.\n",
   1.207 +	"");
   1.208 +    FPS "%-15s Keep expired certificates.\n",
   1.209 +	"   -x");
   1.210 +    FPS "%-15s Verbose mode - report all activity while recovering db.\n",
   1.211 +	"   -v");
   1.212 +    FPS "%-15s File to dump verbose output into.\n",
   1.213 +	"   -f dumpfile");
   1.214 +    FPS "\n");
   1.215 +#endif
   1.216 +    exit(-1);
   1.217 +#undef FPS
   1.218 +}
   1.219 +
   1.220 +/*******************************************************************
   1.221 + *
   1.222 + *  Functions for dbck.
   1.223 + *
   1.224 + ******************************************************************/
   1.225 +
   1.226 +void
   1.227 +printHexString(PRFileDesc *out, SECItem *hexval)
   1.228 +{
   1.229 +    unsigned int i;
   1.230 +    for (i = 0; i < hexval->len; i++) {
   1.231 +	if (i != hexval->len - 1) {
   1.232 +	    PR_fprintf(out, "%02x:", hexval->data[i]);
   1.233 +	} else {
   1.234 +	    PR_fprintf(out, "%02x", hexval->data[i]);
   1.235 +	}
   1.236 +    }
   1.237 +    PR_fprintf(out, "\n");
   1.238 +}
   1.239 +
   1.240 +
   1.241 +SECStatus
   1.242 +dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
   1.243 +{
   1.244 +    int userCert = 0;
   1.245 +    CERTCertTrust *trust = cert->trust;
   1.246 +    userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
   1.247 +               (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
   1.248 +               (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
   1.249 +    if (num >= 0) {
   1.250 +	PR_fprintf(outfile, "Certificate: %3d\n", num);
   1.251 +    } else {
   1.252 +	PR_fprintf(outfile, "Certificate:\n");
   1.253 +    }
   1.254 +    PR_fprintf(outfile, "----------------\n");
   1.255 +    if (userCert)
   1.256 +	PR_fprintf(outfile, "(User Cert)\n");
   1.257 +    PR_fprintf(outfile, "## SUBJECT:  %s\n", cert->subjectName);
   1.258 +    PR_fprintf(outfile, "## ISSUER:  %s\n", cert->issuerName);
   1.259 +    PR_fprintf(outfile, "## SERIAL NUMBER:  ");
   1.260 +    printHexString(outfile, &cert->serialNumber);
   1.261 +    {  /*  XXX should be separate function.  */
   1.262 +	PRTime timeBefore, timeAfter;
   1.263 +	PRExplodedTime beforePrintable, afterPrintable;
   1.264 +	char *beforestr, *afterstr;
   1.265 +	DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
   1.266 +	DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
   1.267 +	PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
   1.268 +	PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
   1.269 +	beforestr = PORT_Alloc(100);
   1.270 +	afterstr = PORT_Alloc(100);
   1.271 +	PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
   1.272 +	PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
   1.273 +	PR_fprintf(outfile, "## VALIDITY:  %s to %s\n", beforestr, afterstr);
   1.274 +    }
   1.275 +    PR_fprintf(outfile, "\n");
   1.276 +    return SECSuccess;
   1.277 +}
   1.278 +
   1.279 +SECStatus
   1.280 +dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
   1.281 +{
   1.282 +#if 0
   1.283 +    NSSLOWCERTCertificate *cert;
   1.284 +    /* should we check for existing duplicates? */
   1.285 +    cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, 
   1.286 +					    entry->cert.nickname);
   1.287 +#else
   1.288 +    CERTCertificate *cert;
   1.289 +    cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
   1.290 +#endif
   1.291 +    if (!cert) {
   1.292 +	fprintf(stderr, "Failed to decode certificate.\n");
   1.293 +	return SECFailure;
   1.294 +    }
   1.295 +    cert->trust = (CERTCertTrust *)&entry->trust;
   1.296 +    dumpCertificate(cert, num, outfile);
   1.297 +    CERT_DestroyCertificate(cert);
   1.298 +    return SECSuccess;
   1.299 +}
   1.300 +
   1.301 +SECStatus
   1.302 +dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
   1.303 +{
   1.304 +    char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
   1.305 +
   1.306 +    PR_fprintf(outfile, "Subject: %3d\n", num);
   1.307 +    PR_fprintf(outfile, "------------\n");
   1.308 +    PR_fprintf(outfile, "## %s\n", subjectName);
   1.309 +    if (entry->nickname)
   1.310 +	PR_fprintf(outfile, "## Subject nickname:  %s\n", entry->nickname);
   1.311 +    if (entry->emailAddrs) {
   1.312 +	unsigned int n;
   1.313 +	for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
   1.314 +	    char * emailAddr = entry->emailAddrs[n];
   1.315 +	    if (emailAddr[0]) {
   1.316 +		PR_fprintf(outfile, "## Subject email address:  %s\n", 
   1.317 +	           emailAddr);
   1.318 +	    }
   1.319 +	}
   1.320 +    }
   1.321 +    PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
   1.322 +    PR_fprintf(outfile, "\n");
   1.323 +    PORT_Free(subjectName);
   1.324 +    return SECSuccess;
   1.325 +}
   1.326 +
   1.327 +SECStatus
   1.328 +dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
   1.329 +{
   1.330 +    PR_fprintf(outfile, "Nickname: %3d\n", num);
   1.331 +    PR_fprintf(outfile, "-------------\n");
   1.332 +    PR_fprintf(outfile, "##  \"%s\"\n\n", entry->nickname);
   1.333 +    return SECSuccess;
   1.334 +}
   1.335 +
   1.336 +SECStatus
   1.337 +dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
   1.338 +{
   1.339 +    PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
   1.340 +    PR_fprintf(outfile, "-------------------\n");
   1.341 +    PR_fprintf(outfile, "##  \"%s\"\n", entry->emailAddr);
   1.342 +#ifdef OLDWAY
   1.343 +    PR_fprintf(outfile, "##  OPTIONS:  ");
   1.344 +    printHexString(outfile, &entry->smimeOptions);
   1.345 +    PR_fprintf(outfile, "##  TIMESTAMP:  ");
   1.346 +    printHexString(outfile, &entry->optionsDate);
   1.347 +#else
   1.348 +    SECU_PrintAny(stdout, &entry->smimeOptions, "##  OPTIONS  ", 0);
   1.349 +    fflush(stdout);
   1.350 +    if (entry->optionsDate.len && entry->optionsDate.data)
   1.351 +	PR_fprintf(outfile, "##  TIMESTAMP: %.*s\n", 
   1.352 +	           entry->optionsDate.len, entry->optionsDate.data);
   1.353 +#endif
   1.354 +    PR_fprintf(outfile, "\n");
   1.355 +    return SECSuccess;
   1.356 +}
   1.357 +
   1.358 +SECStatus
   1.359 +mapCertEntries(certDBArray *dbArray)
   1.360 +{
   1.361 +    certDBEntryCert *certEntry;
   1.362 +    certDBEntrySubject *subjectEntry;
   1.363 +    certDBEntryListNode *certNode, *subjNode;
   1.364 +    certDBSubjectEntryMap *smap;
   1.365 +    certDBEntryMap *map;
   1.366 +    PLArenaPool *tmparena;
   1.367 +    SECItem derSubject;
   1.368 +    SECItem certKey;
   1.369 +    PRCList *cElem, *sElem;
   1.370 +
   1.371 +    /* Arena for decoded entries */
   1.372 +    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1.373 +    if (tmparena == NULL) {
   1.374 +	PORT_SetError(SEC_ERROR_NO_MEMORY);
   1.375 +	return SECFailure;
   1.376 +    }
   1.377 +
   1.378 +    /* Iterate over cert entries and map them to subject entries. 
   1.379 +     * NOTE: mapSubjectEntries must be called first to alloc memory
   1.380 +     * for array of subject->cert map.
   1.381 +     */
   1.382 +    for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 
   1.383 +         cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
   1.384 +	certNode = LISTNODE_CAST(cElem);
   1.385 +	certEntry = (certDBEntryCert *)&certNode->entry;
   1.386 +	map = (certDBEntryMap *)certNode->appData;
   1.387 +	CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
   1.388 +	CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
   1.389 +	/*  Loop over found subjects for cert's DN.  */
   1.390 +	for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   1.391 +	     sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   1.392 +	    subjNode = LISTNODE_CAST(sElem);
   1.393 +	    subjectEntry = (certDBEntrySubject *)&subjNode->entry;
   1.394 +	    if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
   1.395 +		unsigned int i;
   1.396 +		/*  Found matching subject name, create link.  */
   1.397 +		map->pSubject = subjNode;
   1.398 +		/*  Make sure subject entry has cert's key.  */
   1.399 +		for (i=0; i<subjectEntry->ncerts; i++) {
   1.400 +		    if (SECITEM_ItemsAreEqual(&certKey,
   1.401 +		                              &subjectEntry->certKeys[i])) {
   1.402 +			/*  Found matching cert key.  */
   1.403 +			smap = (certDBSubjectEntryMap *)subjNode->appData;
   1.404 +			smap->pCerts[i] = certNode;
   1.405 +			break;
   1.406 +		    }
   1.407 +		}
   1.408 +	    }
   1.409 +	}
   1.410 +    }
   1.411 +    PORT_FreeArena(tmparena, PR_FALSE);
   1.412 +    return SECSuccess;
   1.413 +}
   1.414 +
   1.415 +SECStatus
   1.416 +mapSubjectEntries(certDBArray *dbArray)
   1.417 +{
   1.418 +    certDBEntrySubject *subjectEntry;
   1.419 +    certDBEntryListNode *subjNode;
   1.420 +    certDBSubjectEntryMap *subjMap;
   1.421 +    PRCList *sElem;
   1.422 +
   1.423 +    for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   1.424 +         sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   1.425 +	/* Iterate over subject entries and map subjects to nickname
   1.426 +	 * and smime entries.  The cert<->subject map will be handled
   1.427 +	 * by a subsequent call to mapCertEntries.
   1.428 +	 */
   1.429 +	subjNode = LISTNODE_CAST(sElem);
   1.430 +	subjectEntry = (certDBEntrySubject *)&subjNode->entry;
   1.431 +	subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   1.432 +	/* need to alloc memory here for array of matching certs. */
   1.433 +	subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 
   1.434 +	                                  subjectEntry->ncerts*sizeof(int));
   1.435 +	subjMap->numCerts = subjectEntry->ncerts;
   1.436 +	subjMap->pNickname = NoNickname;
   1.437 +	subjMap->pSMime = NoSMime;
   1.438 +
   1.439 +	if (subjectEntry->nickname) {
   1.440 +	    /* Subject should have a nickname entry, so create a link. */
   1.441 +	    PRCList *nElem;
   1.442 +	    for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
   1.443 +	         nElem != &dbArray->nicknames.link; 
   1.444 +	         nElem = PR_NEXT_LINK(nElem)) {
   1.445 +		certDBEntryListNode *nickNode;
   1.446 +		certDBEntryNickname *nicknameEntry;
   1.447 +		/*  Look for subject's nickname in nickname entries.  */
   1.448 +		nickNode = LISTNODE_CAST(nElem);
   1.449 +		nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
   1.450 +		if (PL_strcmp(subjectEntry->nickname, 
   1.451 +		              nicknameEntry->nickname) == 0) {
   1.452 +		    /*  Found a nickname entry for subject's nickname.  */
   1.453 +		    if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
   1.454 +		                              &nicknameEntry->subjectName)) {
   1.455 +			certDBEntryMap *nickMap;
   1.456 +			nickMap = (certDBEntryMap *)nickNode->appData;
   1.457 +			/*  Nickname and subject match.  */
   1.458 +			subjMap->pNickname = nickNode;
   1.459 +			nickMap->pSubject = subjNode;
   1.460 +		    } else if (subjMap->pNickname == NoNickname) {
   1.461 +			/*  Nickname entry found is for diff. subject.  */
   1.462 +			subjMap->pNickname = WrongEntry;
   1.463 +		    }
   1.464 +		}
   1.465 +	    }
   1.466 +	}
   1.467 +	if (subjectEntry->emailAddrs) {
   1.468 +	    unsigned int n;
   1.469 +	    for (n = 0; n < subjectEntry->nemailAddrs && 
   1.470 +	                subjectEntry->emailAddrs[n]; ++n) {
   1.471 +		char * emailAddr = subjectEntry->emailAddrs[n];
   1.472 +		if (emailAddr[0]) {
   1.473 +		    PRCList *mElem;
   1.474 +		    /* Subject should have an smime entry, so create a link. */
   1.475 +		    for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
   1.476 +			 mElem != &dbArray->smime.link; 
   1.477 +			 mElem = PR_NEXT_LINK(mElem)) {
   1.478 +			certDBEntryListNode *smimeNode;
   1.479 +			certDBEntrySMime *smimeEntry;
   1.480 +			/*  Look for subject's email in S/MIME entries.  */
   1.481 +			smimeNode = LISTNODE_CAST(mElem);
   1.482 +			smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
   1.483 +			if (PL_strcmp(emailAddr, 
   1.484 +				      smimeEntry->emailAddr) == 0) {
   1.485 +			    /*  Found a S/MIME entry for subject's email.  */
   1.486 +			    if (SECITEM_ItemsAreEqual(
   1.487 +			    		&subjectEntry->derSubject,
   1.488 +				        &smimeEntry->subjectName)) {
   1.489 +				certDBEntryMap *smimeMap;
   1.490 +				/*  S/MIME entry and subject match.  */
   1.491 +				subjMap->pSMime = smimeNode;
   1.492 +				smimeMap = (certDBEntryMap *)smimeNode->appData;
   1.493 +				smimeMap->pSubject = subjNode;
   1.494 +			    } else if (subjMap->pSMime == NoSMime) {
   1.495 +				/*  S/MIME entry found is for diff. subject.  */
   1.496 +				subjMap->pSMime = WrongEntry;
   1.497 +			    }
   1.498 +			}
   1.499 +		    }   /* end for */
   1.500 +		}   /* endif (emailAddr[0]) */
   1.501 +	    }   /* end for */
   1.502 +	}   /* endif (subjectEntry->emailAddrs) */
   1.503 +    }
   1.504 +    return SECSuccess;
   1.505 +}
   1.506 +
   1.507 +void
   1.508 +printnode(dbDebugInfo *info, const char *str, int num)
   1.509 +{
   1.510 +    if (!info->dograph)
   1.511 +	return;
   1.512 +    if (num < 0) {
   1.513 +	PR_fprintf(info->graphfile, str);
   1.514 +    } else {
   1.515 +	PR_fprintf(info->graphfile, str, num);
   1.516 +    }
   1.517 +}
   1.518 +
   1.519 +PRBool
   1.520 +map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
   1.521 +{
   1.522 +    if (mapPtr == NULL) {
   1.523 +	if (indent > 0)
   1.524 +	    printnode(info, "                ", -1);
   1.525 +	if (indent >= 0)
   1.526 +	    printnode(info, "******************* ", -1);
   1.527 +	return PR_FALSE;
   1.528 +    } else if (mapPtr == WrongEntry) {
   1.529 +	if (indent > 0)
   1.530 +	    printnode(info, "                  ", -1);
   1.531 +	if (indent >= 0)
   1.532 +	    printnode(info, "??????????????????? ", -1);
   1.533 +	return PR_FALSE;
   1.534 +    } else {
   1.535 +	return PR_TRUE;
   1.536 +    }
   1.537 +}
   1.538 +
   1.539 +/* these call each other */
   1.540 +void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 
   1.541 +                       int direction);
   1.542 +void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 
   1.543 +                          int direction);
   1.544 +void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
   1.545 +                         int direction, int optindex, int opttype);
   1.546 +void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 
   1.547 +                      int direction);
   1.548 +
   1.549 +/* Given an smime entry, print its unique identifier.  If GOLEFT is 
   1.550 + * specified, print the cert<-subject<-smime map, else just print
   1.551 + * the smime entry.
   1.552 + */
   1.553 +void
   1.554 +print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
   1.555 +{
   1.556 +    certDBSubjectEntryMap *subjMap;
   1.557 +    certDBEntryListNode *subjNode;
   1.558 +    if (direction == GOLEFT) {
   1.559 +	/* Need to output subject and cert first, see print_subject_graph */
   1.560 +	subjNode = smimeMap->pSubject;
   1.561 +	if (map_handle_is_ok(info, (void *)subjNode, 1)) {
   1.562 +	    subjMap = (certDBSubjectEntryMap *)subjNode->appData; 
   1.563 +	    print_subject_graph(info, subjMap, GOLEFT,
   1.564 +	                        smimeMap->index, certDBEntryTypeSMimeProfile);
   1.565 +	} else {
   1.566 +	    printnode(info, "<---- S/MIME   %5d   ", smimeMap->index);
   1.567 +	    info->dbErrors[NoSubjectForSMime]++;
   1.568 +	}
   1.569 +    } else {
   1.570 +	printnode(info, "S/MIME   %5d   ", smimeMap->index);
   1.571 +    }
   1.572 +}
   1.573 +
   1.574 +/* Given a nickname entry, print its unique identifier.  If GOLEFT is 
   1.575 + * specified, print the cert<-subject<-nickname map, else just print
   1.576 + * the nickname entry.
   1.577 + */
   1.578 +void
   1.579 +print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
   1.580 +{
   1.581 +    certDBSubjectEntryMap *subjMap;
   1.582 +    certDBEntryListNode *subjNode;
   1.583 +    if (direction == GOLEFT) {
   1.584 +	/* Need to output subject and cert first, see print_subject_graph */
   1.585 +	subjNode = nickMap->pSubject;
   1.586 +	if (map_handle_is_ok(info, (void *)subjNode, 1)) {
   1.587 +	    subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   1.588 +	    print_subject_graph(info, subjMap, GOLEFT,
   1.589 +	                        nickMap->index, certDBEntryTypeNickname);
   1.590 +	} else {
   1.591 +	    printnode(info, "<---- Nickname %5d   ", nickMap->index);
   1.592 +	    info->dbErrors[NoSubjectForNickname]++;
   1.593 +	}
   1.594 +    } else {
   1.595 +	printnode(info, "Nickname %5d   ", nickMap->index);
   1.596 +    }
   1.597 +}
   1.598 +
   1.599 +/* Given a subject entry, if going right print the graph of the nickname|smime
   1.600 + * that it maps to (by its unique identifier); and if going left
   1.601 + * print the list of certs that it points to.
   1.602 + */
   1.603 +void
   1.604 +print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
   1.605 +                    int direction, int optindex, int opttype)
   1.606 +{
   1.607 +    certDBEntryMap *map;
   1.608 +    certDBEntryListNode *node;
   1.609 +    int i;
   1.610 +    /* The first line of output always contains the cert id, subject id,
   1.611 +     * and nickname|smime id.  Subsequent lines may contain additional
   1.612 +     * cert id's for the subject if going left or both directions.
   1.613 +     * Ex. of printing the graph for a subject entry:
   1.614 +     * Cert 3 <- Subject 5 -> Nickname 32
   1.615 +     * Cert 8 /
   1.616 +     * Cert 9 /
   1.617 +     * means subject 5 has 3 certs, 3, 8, and 9, and corresponds
   1.618 +     * to nickname entry 32.
   1.619 +     * To accomplish the above, it is required to dump the entire first
   1.620 +     * line left-to-right, regardless of the input direction, and then
   1.621 +     * finish up any remaining cert entries.  Hence the code is uglier
   1.622 +     * than one may expect.
   1.623 +     */
   1.624 +    if (direction == GOLEFT || direction == GOBOTH) {
   1.625 +	/* In this case, nothing should be output until the first cert is
   1.626 +	 * located and output (cert 3 in the above example).
   1.627 +	 */
   1.628 +	if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
   1.629 +	    /* XXX uh-oh */
   1.630 +	    return;
   1.631 +	/* get the first cert and dump it. */
   1.632 +	node = subjMap->pCerts[0];
   1.633 +	if (map_handle_is_ok(info, (void *)node, 0)) {
   1.634 +	    map = (certDBEntryMap *)node->appData;
   1.635 +	    /* going left here stops. */
   1.636 +	    print_cert_graph(info, map, GOLEFT); 
   1.637 +	} else {
   1.638 +	    info->dbErrors[SubjectHasNoKeyForCert]++;
   1.639 +	}
   1.640 +	/* Now it is safe to output the subject id. */
   1.641 +	if (direction == GOLEFT)
   1.642 +	    printnode(info, "Subject  %5d <---- ", subjMap->index);
   1.643 +	else /* direction == GOBOTH */
   1.644 +	    printnode(info, "Subject  %5d ----> ", subjMap->index);
   1.645 +    }
   1.646 +    if (direction == GORIGHT || direction == GOBOTH) { 
   1.647 +	/* Okay, now output the nickname|smime for this subject. */
   1.648 +	if (direction != GOBOTH) /* handled above */
   1.649 +	   printnode(info, "Subject  %5d ----> ", subjMap->index);
   1.650 +	if (subjMap->pNickname) {
   1.651 +	    node = subjMap->pNickname;
   1.652 +	    if (map_handle_is_ok(info, (void *)node, 0)) {
   1.653 +		map = (certDBEntryMap *)node->appData;
   1.654 +		/* going right here stops. */
   1.655 +		print_nickname_graph(info, map, GORIGHT);
   1.656 +	    }
   1.657 +	}
   1.658 +	if (subjMap->pSMime) {
   1.659 +	    node = subjMap->pSMime;
   1.660 +	    if (map_handle_is_ok(info, (void *)node, 0)) {
   1.661 +		map = (certDBEntryMap *)node->appData;
   1.662 +		/* going right here stops. */
   1.663 +		print_smime_graph(info, map, GORIGHT); 
   1.664 +	    }
   1.665 +	}
   1.666 +	if (!subjMap->pNickname && !subjMap->pSMime) {
   1.667 +	    printnode(info, "******************* ", -1);
   1.668 +	    info->dbErrors[NoNicknameOrSMimeForSubject]++;
   1.669 +	}
   1.670 +	if (subjMap->pNickname && subjMap->pSMime) {
   1.671 +	    info->dbErrors[NicknameAndSMimeEntries]++;
   1.672 +	}
   1.673 +    }
   1.674 +    if (direction != GORIGHT) { /* going right has only one cert */
   1.675 +	if (opttype == certDBEntryTypeNickname)
   1.676 +	    printnode(info, "Nickname %5d   ", optindex);
   1.677 +	else if (opttype == certDBEntryTypeSMimeProfile)
   1.678 +	    printnode(info, "S/MIME   %5d   ", optindex);
   1.679 +	for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
   1.680 +	    printnode(info, "\n", -1); /* start a new line */
   1.681 +	    node = subjMap->pCerts[i];
   1.682 +	    if (map_handle_is_ok(info, (void *)node, 0)) {
   1.683 +		map = (certDBEntryMap *)node->appData;
   1.684 +		/* going left here stops. */
   1.685 +		print_cert_graph(info, map, GOLEFT); 
   1.686 +		printnode(info, "/", -1);
   1.687 +	    }
   1.688 +	}
   1.689 +    }
   1.690 +}
   1.691 +
   1.692 +/* Given a cert entry, print its unique identifer.  If GORIGHT is specified,
   1.693 + * print the cert->subject->nickname|smime map, else just print
   1.694 + * the cert entry.
   1.695 + */
   1.696 +void
   1.697 +print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
   1.698 +{
   1.699 +    certDBSubjectEntryMap *subjMap;
   1.700 +    certDBEntryListNode *subjNode;
   1.701 +    if (direction == GOLEFT) {
   1.702 +	printnode(info, "Cert     %5d <---- ", certMap->index);
   1.703 +	/* only want cert entry, terminate here. */
   1.704 +	return;
   1.705 +    }
   1.706 +    /* Keep going right then. */
   1.707 +    printnode(info, "Cert     %5d ----> ", certMap->index);
   1.708 +    subjNode = certMap->pSubject;
   1.709 +    if (map_handle_is_ok(info, (void *)subjNode, 0)) {
   1.710 +	subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   1.711 +	print_subject_graph(info, subjMap, GORIGHT, -1, -1);
   1.712 +    } else {
   1.713 +	info->dbErrors[NoSubjectForCert]++;
   1.714 +    }
   1.715 +}
   1.716 +
   1.717 +SECStatus
   1.718 +computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
   1.719 +{
   1.720 +    PRCList *cElem, *sElem, *nElem, *mElem;
   1.721 +    certDBEntryListNode *node;
   1.722 +    certDBEntryMap *map;
   1.723 +    certDBSubjectEntryMap *subjMap;
   1.724 +
   1.725 +    /* Graph is of this form:
   1.726 +     *
   1.727 +     * certs:
   1.728 +     * cert ---> subject ---> (nickname|smime)
   1.729 +     *
   1.730 +     * subjects:
   1.731 +     * cert <--- subject ---> (nickname|smime)
   1.732 +     *
   1.733 +     * nicknames and smime:
   1.734 +     * cert <--- subject <--- (nickname|smime)
   1.735 +     */
   1.736 +
   1.737 +    /* Print cert graph. */
   1.738 +    for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
   1.739 +         cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
   1.740 +	/* Print graph of everything to right of cert entry. */
   1.741 +	node = LISTNODE_CAST(cElem);
   1.742 +	map = (certDBEntryMap *)node->appData;
   1.743 +	print_cert_graph(info, map, GORIGHT);
   1.744 +	printnode(info, "\n", -1);
   1.745 +    }
   1.746 +    printnode(info, "\n", -1);
   1.747 +
   1.748 +    /* Print subject graph. */
   1.749 +    for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   1.750 +         sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   1.751 +	/* Print graph of everything to both sides of subject entry. */
   1.752 +	node = LISTNODE_CAST(sElem);
   1.753 +	subjMap = (certDBSubjectEntryMap *)node->appData;
   1.754 +	print_subject_graph(info, subjMap, GOBOTH, -1, -1);
   1.755 +	printnode(info, "\n", -1);
   1.756 +    }
   1.757 +    printnode(info, "\n", -1);
   1.758 +
   1.759 +    /* Print nickname graph. */
   1.760 +    for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
   1.761 +         nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
   1.762 +	/* Print graph of everything to left of nickname entry. */
   1.763 +	node = LISTNODE_CAST(nElem);
   1.764 +	map = (certDBEntryMap *)node->appData;
   1.765 +	print_nickname_graph(info, map, GOLEFT);
   1.766 +	printnode(info, "\n", -1);
   1.767 +    }
   1.768 +    printnode(info, "\n", -1);
   1.769 +
   1.770 +    /* Print smime graph. */
   1.771 +    for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
   1.772 +         mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
   1.773 +	/* Print graph of everything to left of smime entry. */
   1.774 +	node = LISTNODE_CAST(mElem);
   1.775 +	if (node == NULL) break;
   1.776 +	map = (certDBEntryMap *)node->appData;
   1.777 +	print_smime_graph(info, map, GOLEFT);
   1.778 +	printnode(info, "\n", -1);
   1.779 +    }
   1.780 +    printnode(info, "\n", -1);
   1.781 +
   1.782 +    return SECSuccess;
   1.783 +}
   1.784 +
   1.785 +/*
   1.786 + * List the entries in the db, showing handles between entry types.
   1.787 + */
   1.788 +void
   1.789 +verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
   1.790 +{
   1.791 +    int i, ref;
   1.792 +    PRCList *elem;
   1.793 +    certDBEntryListNode *node;
   1.794 +    certDBEntryMap *map;
   1.795 +    certDBSubjectEntryMap *smap;
   1.796 +    certDBEntrySubject *subjectEntry;
   1.797 +
   1.798 +    /* List certs */
   1.799 +    for (elem = PR_LIST_HEAD(&dbArray->certs.link);
   1.800 +         elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
   1.801 +	node = LISTNODE_CAST(elem);
   1.802 +	map = (certDBEntryMap *)node->appData;
   1.803 +	dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
   1.804 +	/* walk the cert handle to it's subject entry */
   1.805 +	if (map_handle_is_ok(info, map->pSubject, -1)) {
   1.806 +	    smap = (certDBSubjectEntryMap *)map->pSubject->appData;
   1.807 +	    ref = smap->index;
   1.808 +	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   1.809 +	} else {
   1.810 +	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   1.811 +	}
   1.812 +    }
   1.813 +    /* List subjects */
   1.814 +    for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
   1.815 +         elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
   1.816 +	int refs = 0;
   1.817 +	node = LISTNODE_CAST(elem);
   1.818 +	subjectEntry = (certDBEntrySubject *)&node->entry;
   1.819 +	smap = (certDBSubjectEntryMap *)node->appData;
   1.820 +	dumpSubjectEntry(subjectEntry, smap->index, info->out);
   1.821 +	/* iterate over subject's certs */
   1.822 +	for (i=0; i<smap->numCerts; i++) {
   1.823 +	    /* walk each subject handle to it's cert entries */
   1.824 +	    if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
   1.825 +		ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
   1.826 +		PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
   1.827 +	    } else {
   1.828 +		PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
   1.829 +	    }
   1.830 +	}
   1.831 +	if (subjectEntry->nickname) {
   1.832 +	    ++refs;
   1.833 +	    /* walk each subject handle to it's nickname entry */
   1.834 +	    if (map_handle_is_ok(info, smap->pNickname, -1)) {
   1.835 +		ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
   1.836 +		PR_fprintf(info->out, "-->(nickname %d)\n", ref);
   1.837 +	    } else {
   1.838 +		PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
   1.839 +	    }
   1.840 +	}
   1.841 +	if (subjectEntry->nemailAddrs && 
   1.842 +	    subjectEntry->emailAddrs &&
   1.843 +	    subjectEntry->emailAddrs[0] &&
   1.844 +	    subjectEntry->emailAddrs[0][0]) {
   1.845 +	    ++refs;
   1.846 +	    /* walk each subject handle to it's smime entry */
   1.847 +	    if (map_handle_is_ok(info, smap->pSMime, -1)) {
   1.848 +		ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
   1.849 +		PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
   1.850 +	    } else {
   1.851 +		PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
   1.852 +	    }
   1.853 +	}
   1.854 +	if (!refs) {
   1.855 +	    PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
   1.856 +	}
   1.857 +	PR_fprintf(info->out, "\n\n");
   1.858 +    }
   1.859 +    for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
   1.860 +         elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
   1.861 +	node = LISTNODE_CAST(elem);
   1.862 +	map = (certDBEntryMap *)node->appData;
   1.863 +	dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index, 
   1.864 +	                  info->out);
   1.865 +	if (map_handle_is_ok(info, map->pSubject, -1)) {
   1.866 +	    ref = ((certDBEntryMap *)map->pSubject->appData)->index;
   1.867 +	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   1.868 +	} else {
   1.869 +	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   1.870 +	}
   1.871 +    }
   1.872 +    for (elem = PR_LIST_HEAD(&dbArray->smime.link);
   1.873 +         elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
   1.874 +	node = LISTNODE_CAST(elem);
   1.875 +	map = (certDBEntryMap *)node->appData;
   1.876 +	dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
   1.877 +	if (map_handle_is_ok(info, map->pSubject, -1)) {
   1.878 +	    ref = ((certDBEntryMap *)map->pSubject->appData)->index;
   1.879 +	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   1.880 +	} else {
   1.881 +	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   1.882 +	}
   1.883 +    }
   1.884 +    PR_fprintf(info->out, "\n\n");
   1.885 +}
   1.886 +
   1.887 +
   1.888 +/* A callback function, intended to be called from nsslowcert_TraverseDBEntries
   1.889 + * Builds a PRCList of DB entries of the specified type.
   1.890 + */
   1.891 +SECStatus 
   1.892 +SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, 
   1.893 +                       certDBEntryType entryType, void *pdata)
   1.894 +{
   1.895 +    certDBEntry         * entry;
   1.896 +    certDBEntryListNode * node;
   1.897 +    PRCList             * list = (PRCList *)pdata;
   1.898 +
   1.899 +    if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
   1.900 +    	PORT_SetError(SEC_ERROR_INVALID_ARGS);
   1.901 +	return SECFailure;
   1.902 +    }
   1.903 +    entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
   1.904 +    if (!entry) {
   1.905 +    	return SECSuccess; /* skip it */
   1.906 +    }
   1.907 +    node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
   1.908 +    if (!node) {
   1.909 +    	/* DestroyDBEntry(entry); */
   1.910 +	PLArenaPool *arena = entry->common.arena;
   1.911 +	PORT_Memset(&entry->common, 0, sizeof entry->common);
   1.912 +	PORT_FreeArena(arena, PR_FALSE);
   1.913 +	return SECFailure;
   1.914 +    }
   1.915 +    node->entry = *entry;  		/* crude but effective. */
   1.916 +    PR_INIT_CLIST(&node->link);
   1.917 +    PR_INSERT_BEFORE(&node->link, list);
   1.918 +    return SECSuccess;
   1.919 +}
   1.920 +
   1.921 +
   1.922 +int
   1.923 +fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, 
   1.924 +                 certDBEntryListNode *list)
   1.925 +{
   1.926 +    PRCList *elem;
   1.927 +    certDBEntryListNode *node;
   1.928 +    certDBEntryMap *mnode;
   1.929 +    certDBSubjectEntryMap *smnode;
   1.930 +    PLArenaPool *arena;
   1.931 +    int count = 0;
   1.932 +
   1.933 +    /* Initialize a dummy entry in the list.  The list head will be the
   1.934 +     * next element, so this element is skipped by for loops.
   1.935 +     */
   1.936 +    PR_INIT_CLIST((PRCList *)list);
   1.937 +    /* Collect all of the cert db entries for this type into a list. */
   1.938 +    nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
   1.939 +
   1.940 +    for (elem = PR_LIST_HEAD(&list->link); 
   1.941 +         elem != &list->link; elem = PR_NEXT_LINK(elem)) {
   1.942 +	/* Iterate over the entries and ... */
   1.943 +	node = (certDBEntryListNode *)elem;
   1.944 +	if (type != certDBEntryTypeSubject) {
   1.945 +	    arena = PORT_NewArena(sizeof(*mnode));
   1.946 +	    mnode = PORT_ArenaZNew(arena, certDBEntryMap);
   1.947 +	    mnode->arena = arena;
   1.948 +	    /* ... assign a unique index number to each node, and ... */
   1.949 +	    mnode->index = count;
   1.950 +	    /* ... set the map pointer for the node. */
   1.951 +	    node->appData = (void *)mnode;
   1.952 +	} else {
   1.953 +	    /* allocate some room for the cert pointers also */
   1.954 +	    arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
   1.955 +	    smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
   1.956 +	    smnode->arena = arena;
   1.957 +	    smnode->index = count;
   1.958 +	    node->appData = (void *)smnode;
   1.959 +	}
   1.960 +	count++;
   1.961 +    }
   1.962 +    return count;
   1.963 +}
   1.964 +
   1.965 +void
   1.966 +freeDBEntryList(PRCList *list)
   1.967 +{
   1.968 +    PRCList *next, *elem;
   1.969 +    certDBEntryListNode *node;
   1.970 +    certDBEntryMap *map;
   1.971 +
   1.972 +    for (elem = PR_LIST_HEAD(list); elem != list;) { 
   1.973 +	next = PR_NEXT_LINK(elem);
   1.974 +	node = (certDBEntryListNode *)elem;
   1.975 +	map = (certDBEntryMap *)node->appData;
   1.976 +	PR_REMOVE_LINK(&node->link);
   1.977 +	PORT_FreeArena(map->arena, PR_TRUE);
   1.978 +	PORT_FreeArena(node->entry.common.arena, PR_TRUE);
   1.979 +	elem = next;
   1.980 +    }
   1.981 +}
   1.982 +
   1.983 +void
   1.984 +DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, 
   1.985 +	     PRFileDesc *mailfile)
   1.986 +{
   1.987 +    int i, nCertsFound, nSubjFound, nErr;
   1.988 +    int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
   1.989 +    PRCList *elem;
   1.990 +    char c;
   1.991 +    dbDebugInfo info;
   1.992 +    certDBArray dbArray;
   1.993 +
   1.994 +    PORT_Memset(&dbArray, 0, sizeof(dbArray));
   1.995 +    PORT_Memset(&info, 0, sizeof(info));
   1.996 +    info.verbose = (PRBool)(out != NULL);
   1.997 +    info.dograph = info.verbose;
   1.998 +    info.out       = (out)    ? out      : PR_STDOUT;
   1.999 +    info.graphfile = mailfile ? mailfile : PR_STDOUT;
  1.1000 +
  1.1001 +    /*  Fill the array structure with cert/subject/nickname/smime entries.  */
  1.1002 +    dbArray.numCerts     = fillDBEntryArray(handle, certDBEntryTypeCert, 
  1.1003 +                                            &dbArray.certs);
  1.1004 +    dbArray.numSubjects  = fillDBEntryArray(handle, certDBEntryTypeSubject, 
  1.1005 +                                            &dbArray.subjects);
  1.1006 +    dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 
  1.1007 +                                            &dbArray.nicknames);
  1.1008 +    dbArray.numSMime     = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 
  1.1009 +                                            &dbArray.smime);
  1.1010 +    dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation, 
  1.1011 +                                            &dbArray.revocation);
  1.1012 +
  1.1013 +    /*  Compute the map between the database entries.  */
  1.1014 +    mapSubjectEntries(&dbArray);
  1.1015 +    mapCertEntries(&dbArray);
  1.1016 +    computeDBGraph(&dbArray, &info);
  1.1017 +
  1.1018 +    /*  Store the totals for later reference.  */
  1.1019 +    nCerts     = dbArray.numCerts;
  1.1020 +    nSubjects  = dbArray.numSubjects;
  1.1021 +    nNicknames = dbArray.numNicknames;
  1.1022 +    nSMime     = dbArray.numSMime;
  1.1023 +    nRevocation= dbArray.numRevocation;
  1.1024 +    nSubjCerts = 0;
  1.1025 +    for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
  1.1026 +         elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
  1.1027 +	certDBSubjectEntryMap *smap;
  1.1028 +	smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
  1.1029 +	nSubjCerts += smap->numCerts;
  1.1030 +    }
  1.1031 +
  1.1032 +    if (info.verbose) {
  1.1033 +	/*  Dump the database contents.  */
  1.1034 +	verboseOutput(&dbArray, &info);
  1.1035 +    }
  1.1036 +
  1.1037 +    freeDBEntryList(&dbArray.certs.link);
  1.1038 +    freeDBEntryList(&dbArray.subjects.link);
  1.1039 +    freeDBEntryList(&dbArray.nicknames.link);
  1.1040 +    freeDBEntryList(&dbArray.smime.link);
  1.1041 +    freeDBEntryList(&dbArray.revocation.link);
  1.1042 +
  1.1043 +    PR_fprintf(info.out, "\n");
  1.1044 +    PR_fprintf(info.out, "Database statistics:\n");
  1.1045 +    PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", 
  1.1046 +                          nCerts);
  1.1047 +    PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", 
  1.1048 +                          nSubjects);
  1.1049 +    PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", 
  1.1050 +                          nSubjCerts);
  1.1051 +    PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", 
  1.1052 +                          nNicknames);
  1.1053 +    PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", 
  1.1054 +                          nSMime);
  1.1055 +    PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", 
  1.1056 +                          nRevocation);
  1.1057 +    PR_fprintf(info.out, "\n");
  1.1058 +
  1.1059 +    nErr = 0;
  1.1060 +    for (i=0; i < NUM_ERROR_TYPES; i++) {
  1.1061 +	PR_fprintf(info.out, "E%d: Found %4d %s\n", 
  1.1062 +	           i, info.dbErrors[i], errResult[i]);
  1.1063 +	nErr += info.dbErrors[i];
  1.1064 +    }
  1.1065 +    PR_fprintf(info.out, "--------------\n    Found %4d errors in database.\n", 
  1.1066 +               nErr);
  1.1067 +
  1.1068 +    PR_fprintf(info.out, "\nCertificates:\n");
  1.1069 +    PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, 
  1.1070 +                                                   SubjectHasNoKeyForCert);
  1.1071 +    nCertsFound = nSubjCerts +
  1.1072 +                  info.dbErrors[NoSubjectForCert] +
  1.1073 +                  info.dbErrors[SubjectHasNoKeyForCert];
  1.1074 +    c = (nCertsFound == nCerts) ? '=' : '!';
  1.1075 +    PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, 
  1.1076 +                  info.dbErrors[NoSubjectForCert],
  1.1077 +                  info.dbErrors[SubjectHasNoKeyForCert]);
  1.1078 +    PR_fprintf(info.out, "\nSubjects:\n");
  1.1079 +    PR_fprintf(info.out, 
  1.1080 +    "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
  1.1081 +                  NoNicknameOrSMimeForSubject, 
  1.1082 +		  WrongNicknameForSubject,
  1.1083 +		  NoNicknameEntry, 
  1.1084 +		  WrongSMimeForSubject, 
  1.1085 +		  NoSMimeEntry,
  1.1086 +		  NoSubjectForNickname, 
  1.1087 +		  NoSubjectForSMime,
  1.1088 +		  NicknameAndSMimeEntries);
  1.1089 +    nSubjFound = nNicknames + nSMime + 
  1.1090 +                 info.dbErrors[NoNicknameOrSMimeForSubject] +
  1.1091 +		 info.dbErrors[WrongNicknameForSubject] +
  1.1092 +		 info.dbErrors[NoNicknameEntry] +
  1.1093 +		 info.dbErrors[WrongSMimeForSubject] +
  1.1094 +                 info.dbErrors[NoSMimeEntry] -
  1.1095 +		 info.dbErrors[NoSubjectForNickname] -
  1.1096 +		 info.dbErrors[NoSubjectForSMime] -
  1.1097 +		 info.dbErrors[NicknameAndSMimeEntries];
  1.1098 +    c = (nSubjFound == nSubjects) ? '=' : '!';
  1.1099 +    PR_fprintf(info.out, 
  1.1100 +    "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
  1.1101 +                  nSubjects, c, nNicknames, nSMime,
  1.1102 +                  info.dbErrors[NoNicknameOrSMimeForSubject],
  1.1103 +		  info.dbErrors[WrongNicknameForSubject],
  1.1104 +		  info.dbErrors[NoNicknameEntry],
  1.1105 +		  info.dbErrors[WrongSMimeForSubject],
  1.1106 +                  info.dbErrors[NoSMimeEntry],
  1.1107 +		  info.dbErrors[NoSubjectForNickname],
  1.1108 +		  info.dbErrors[NoSubjectForSMime],
  1.1109 +		  info.dbErrors[NicknameAndSMimeEntries]);
  1.1110 +    PR_fprintf(info.out, "\n");
  1.1111 +}
  1.1112 +
  1.1113 +#ifdef DORECOVER
  1.1114 +#include "dbrecover.c"
  1.1115 +#endif /* DORECOVER */
  1.1116 +
  1.1117 +enum {
  1.1118 +    cmd_Debug = 0,
  1.1119 +    cmd_LongUsage,
  1.1120 +    cmd_Recover
  1.1121 +};
  1.1122 +
  1.1123 +enum {
  1.1124 +    opt_KeepAll = 0,
  1.1125 +    opt_CertDir,
  1.1126 +    opt_Dumpfile,
  1.1127 +    opt_InputDB,
  1.1128 +    opt_OutputDB,
  1.1129 +    opt_Mailfile,
  1.1130 +    opt_Prompt,
  1.1131 +    opt_KeepRedundant,
  1.1132 +    opt_KeepNoSMimeProfile,
  1.1133 +    opt_Verbose,
  1.1134 +    opt_KeepExpired
  1.1135 +};
  1.1136 +
  1.1137 +static secuCommandFlag dbck_commands[] =
  1.1138 +{
  1.1139 +    { /* cmd_Debug,    */  'D', PR_FALSE, 0, PR_FALSE },
  1.1140 +    { /* cmd_LongUsage,*/  'H', PR_FALSE, 0, PR_FALSE },
  1.1141 +    { /* cmd_Recover,  */  'R', PR_FALSE, 0, PR_FALSE }
  1.1142 +};
  1.1143 +
  1.1144 +static secuCommandFlag dbck_options[] =
  1.1145 +{
  1.1146 +    { /* opt_KeepAll,           */  'a', PR_FALSE, 0, PR_FALSE },
  1.1147 +    { /* opt_CertDir,           */  'd', PR_TRUE,  0, PR_FALSE },
  1.1148 +    { /* opt_Dumpfile,          */  'f', PR_TRUE,  0, PR_FALSE },
  1.1149 +    { /* opt_InputDB,           */  'i', PR_TRUE,  0, PR_FALSE },
  1.1150 +    { /* opt_OutputDB,          */  'o', PR_TRUE,  0, PR_FALSE },
  1.1151 +    { /* opt_Mailfile,          */  'm', PR_FALSE, 0, PR_FALSE },
  1.1152 +    { /* opt_Prompt,            */  'p', PR_FALSE, 0, PR_FALSE },
  1.1153 +    { /* opt_KeepRedundant,     */  'r', PR_FALSE, 0, PR_FALSE },
  1.1154 +    { /* opt_KeepNoSMimeProfile,*/  's', PR_FALSE, 0, PR_FALSE },
  1.1155 +    { /* opt_Verbose,           */  'v', PR_FALSE, 0, PR_FALSE },
  1.1156 +    { /* opt_KeepExpired,       */  'x', PR_FALSE, 0, PR_FALSE }
  1.1157 +};
  1.1158 +
  1.1159 +#define CERT_DB_FMT "%s/cert%s.db"
  1.1160 +
  1.1161 +static char *
  1.1162 +dbck_certdb_name_cb(void *arg, int dbVersion)
  1.1163 +{
  1.1164 +    const char *configdir = (const char *)arg;
  1.1165 +    const char *dbver;
  1.1166 +    char *smpname = NULL;
  1.1167 +    char *dbname = NULL;
  1.1168 +
  1.1169 +    switch (dbVersion) {
  1.1170 +      case 8:
  1.1171 +	dbver = "8";
  1.1172 +	break;
  1.1173 +      case 7:
  1.1174 +	dbver = "7";
  1.1175 +	break;
  1.1176 +      case 6:
  1.1177 +	dbver = "6";
  1.1178 +	break;
  1.1179 +      case 5:
  1.1180 +	dbver = "5";
  1.1181 +	break;
  1.1182 +      case 4:
  1.1183 +      default:
  1.1184 +	dbver = "";
  1.1185 +	break;
  1.1186 +    }
  1.1187 +
  1.1188 +    /* make sure we return something allocated with PORT_ so we have properly
  1.1189 +     * matched frees at the end */
  1.1190 +    smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
  1.1191 +    if (smpname) {
  1.1192 +	dbname = PORT_Strdup(smpname);
  1.1193 +	PR_smprintf_free(smpname);
  1.1194 +    }
  1.1195 +    return dbname;
  1.1196 +}
  1.1197 +    
  1.1198 +
  1.1199 +int 
  1.1200 +main(int argc, char **argv)
  1.1201 +{
  1.1202 +    NSSLOWCERTCertDBHandle *certHandle;
  1.1203 +
  1.1204 +    PRFileDesc *mailfile = NULL;
  1.1205 +    PRFileDesc *dumpfile = NULL;
  1.1206 +
  1.1207 +    char * pathname     = 0;
  1.1208 +    char * fullname     = 0;
  1.1209 +    char * newdbname    = 0;
  1.1210 +
  1.1211 +    PRBool removeExpired, requireProfile, singleEntry;
  1.1212 +    SECStatus   rv;
  1.1213 +    secuCommand dbck;
  1.1214 +
  1.1215 +    dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
  1.1216 +    dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
  1.1217 +    dbck.commands = dbck_commands;
  1.1218 +    dbck.options = dbck_options;
  1.1219 +
  1.1220 +    progName = strrchr(argv[0], '/');
  1.1221 +    progName = progName ? progName+1 : argv[0];
  1.1222 +
  1.1223 +    rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
  1.1224 +
  1.1225 +    if (rv != SECSuccess)
  1.1226 +	Usage(progName);
  1.1227 +
  1.1228 +    if (dbck.commands[cmd_LongUsage].activated)
  1.1229 +	LongUsage(progName);
  1.1230 +
  1.1231 +    if (!dbck.commands[cmd_Debug].activated &&
  1.1232 +        !dbck.commands[cmd_Recover].activated) {
  1.1233 +	PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
  1.1234 +	Usage(progName);
  1.1235 +    }
  1.1236 +
  1.1237 +    removeExpired = !(dbck.options[opt_KeepAll].activated ||
  1.1238 +                      dbck.options[opt_KeepExpired].activated);
  1.1239 +
  1.1240 +    requireProfile = !(dbck.options[opt_KeepAll].activated ||
  1.1241 +                    dbck.options[opt_KeepNoSMimeProfile].activated);
  1.1242 +
  1.1243 +    singleEntry = !(dbck.options[opt_KeepAll].activated ||
  1.1244 +                    dbck.options[opt_KeepRedundant].activated);
  1.1245 +
  1.1246 +    if (dbck.options[opt_OutputDB].activated) {
  1.1247 +	newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
  1.1248 +    } else {
  1.1249 +	newdbname = PL_strdup("new_cert8.db");
  1.1250 +    }
  1.1251 +
  1.1252 +    /*  Create a generic graph of the database.  */
  1.1253 +    if (dbck.options[opt_Mailfile].activated) {
  1.1254 +	mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
  1.1255 +	if (!mailfile) {
  1.1256 +	    fprintf(stderr, "Unable to create mailfile.\n");
  1.1257 +	    return -1;
  1.1258 +	}
  1.1259 +    }
  1.1260 +
  1.1261 +    /*  Dump all debugging info while running.  */
  1.1262 +    if (dbck.options[opt_Verbose].activated) {
  1.1263 +	if (dbck.options[opt_Dumpfile].activated) {
  1.1264 +	    dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
  1.1265 +	                       PR_RDWR | PR_CREATE_FILE, 00660);
  1.1266 +	    if (!dumpfile) {
  1.1267 +		fprintf(stderr, "Unable to create dumpfile.\n");
  1.1268 +		return -1;
  1.1269 +	    }
  1.1270 +	} else {
  1.1271 +	    dumpfile = PR_STDOUT;
  1.1272 +	}
  1.1273 +    }
  1.1274 +
  1.1275 +    /*  Set the cert database directory.  */
  1.1276 +    if (dbck.options[opt_CertDir].activated) {
  1.1277 +	SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
  1.1278 +    }
  1.1279 +
  1.1280 +    pathname = SECU_ConfigDirectory(NULL);
  1.1281 +
  1.1282 +    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
  1.1283 +    rv = NSS_NoDB_Init(pathname);
  1.1284 +    if (rv != SECSuccess) {
  1.1285 +	fprintf(stderr, "NSS_NoDB_Init failed\n");
  1.1286 +	return -1;
  1.1287 +    }
  1.1288 +
  1.1289 +    certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
  1.1290 +    if (!certHandle) {
  1.1291 +	SECU_PrintError(progName, "unable to get database handle");
  1.1292 +	return -1;
  1.1293 +    }
  1.1294 +    certHandle->ref = 1;
  1.1295 +
  1.1296 +#ifdef NOTYET
  1.1297 +    /*  Open the possibly corrupt database.  */
  1.1298 +    if (dbck.options[opt_InputDB].activated) {
  1.1299 +	PRFileInfo fileInfo;
  1.1300 +	fullname = PR_smprintf("%s/%s", pathname, 
  1.1301 +	                                dbck.options[opt_InputDB].arg);
  1.1302 +	if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1.1303 +	    fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
  1.1304 +	    return -1;
  1.1305 +	}
  1.1306 +	rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
  1.1307 +    } else 
  1.1308 +#endif
  1.1309 +    {
  1.1310 +	/*  Use the default.  */
  1.1311 +#ifdef NOTYET
  1.1312 +	fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
  1.1313 +	if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1.1314 +	    fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
  1.1315 +	    return -1;
  1.1316 +	}
  1.1317 +#endif
  1.1318 +	rv = nsslowcert_OpenCertDB(certHandle, 
  1.1319 +	                           PR_TRUE, 		    /* readOnly */
  1.1320 +				   NULL,                    /* rdb appName */
  1.1321 +				   "",                      /* rdb prefix */
  1.1322 +	                           dbck_certdb_name_cb,     /* namecb */
  1.1323 +				   pathname, 		    /* configDir */
  1.1324 +				   PR_FALSE);		    /* volatile */
  1.1325 +    }
  1.1326 +
  1.1327 +    if (rv) {
  1.1328 +	SECU_PrintError(progName, "unable to open cert database");
  1.1329 +	return -1;
  1.1330 +    }
  1.1331 +
  1.1332 +    if (dbck.commands[cmd_Debug].activated) {
  1.1333 +	DBCK_DebugDB(certHandle, dumpfile, mailfile);
  1.1334 +	return 0;
  1.1335 +    }
  1.1336 +
  1.1337 +#ifdef DORECOVER
  1.1338 +    if (dbck.commands[cmd_Recover].activated) {
  1.1339 +	DBCK_ReconstructDBFromCerts(certHandle, newdbname,
  1.1340 +	                            dumpfile, removeExpired, 
  1.1341 +	                            requireProfile, singleEntry, 
  1.1342 +	                            dbck.options[opt_Prompt].activated);
  1.1343 +	return 0;
  1.1344 +    }
  1.1345 +#endif
  1.1346 +
  1.1347 +    if (mailfile)
  1.1348 +	PR_Close(mailfile);
  1.1349 +    if (dumpfile)
  1.1350 +	PR_Close(dumpfile);
  1.1351 +    if (certHandle) {
  1.1352 +	nsslowcert_ClosePermCertDB(certHandle);
  1.1353 +	PORT_Free(certHandle);
  1.1354 +    }
  1.1355 +    return -1;
  1.1356 +}

mercurial