security/nss/cmd/dbck/dbck.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6 ** dbck.c
     7 **
     8 ** utility for fixing corrupt cert databases
     9 **
    10 */
    11 #include <stdio.h>
    12 #include <string.h>
    14 #include "secutil.h"
    15 #include "cdbhdl.h"
    16 #include "certdb.h"
    17 #include "cert.h"
    18 #include "nspr.h"
    19 #include "prtypes.h"
    20 #include "prtime.h"
    21 #include "prlong.h"
    22 #include "pcert.h"
    23 #include "nss.h"
    25 static char *progName;
    27 /* placeholders for pointer error types */
    28 static void *WrongEntry;
    29 static void *NoNickname;
    30 static void *NoSMime;
    32 typedef enum {
    33 /* 0*/ NoSubjectForCert = 0,
    34 /* 1*/ SubjectHasNoKeyForCert,
    35 /* 2*/ NoNicknameOrSMimeForSubject,
    36 /* 3*/ WrongNicknameForSubject,
    37 /* 4*/ NoNicknameEntry,
    38 /* 5*/ WrongSMimeForSubject,
    39 /* 6*/ NoSMimeEntry,
    40 /* 7*/ NoSubjectForNickname,
    41 /* 8*/ NoSubjectForSMime,
    42 /* 9*/ NicknameAndSMimeEntries,
    43     NUM_ERROR_TYPES
    44 } dbErrorType;
    46 static char *dbErrorString[NUM_ERROR_TYPES] = {
    47 /* 0*/ "<CERT ENTRY>\nDid not find a subject entry for this certificate.",
    48 /* 1*/ "<SUBJECT ENTRY>\nSubject has certKey which is not in db.",
    49 /* 2*/ "<SUBJECT ENTRY>\nSubject does not have a nickname or email address.",
    50 /* 3*/ "<SUBJECT ENTRY>\nUsing this subject's nickname, found a nickname entry for a different subject.",
    51 /* 4*/ "<SUBJECT ENTRY>\nDid not find a nickname entry for this subject.",
    52 /* 5*/ "<SUBJECT ENTRY>\nUsing this subject's email, found an S/MIME entry for a different subject.",
    53 /* 6*/ "<SUBJECT ENTRY>\nDid not find an S/MIME entry for this subject.",
    54 /* 7*/ "<NICKNAME ENTRY>\nDid not find a subject entry for this nickname.",
    55 /* 8*/ "<S/MIME ENTRY>\nDid not find a subject entry for this S/MIME profile.",
    56 };
    58 static char *errResult[NUM_ERROR_TYPES] = {
    59     "Certificate entries that had no subject entry.", 
    60     "Subject entries with no corresponding Certificate entries.", 
    61     "Subject entries that had no nickname or S/MIME entries.",
    62     "Redundant nicknames (subjects with the same nickname).",
    63     "Subject entries that had no nickname entry.",
    64     "Redundant email addresses (subjects with the same email address).",
    65     "Subject entries that had no S/MIME entry.",
    66     "Nickname entries that had no subject entry.", 
    67     "S/MIME entries that had no subject entry.",
    68     "Subject entries with BOTH nickname and S/MIME entries."
    69 };
    72 enum {
    73     GOBOTH = 0,
    74     GORIGHT,
    75     GOLEFT
    76 };
    78 typedef struct
    79 {
    80     PRBool verbose;
    81     PRBool dograph;
    82     PRFileDesc *out;
    83     PRFileDesc *graphfile;
    84     int dbErrors[NUM_ERROR_TYPES];
    85 } dbDebugInfo;
    87 struct certDBEntryListNodeStr {
    88     PRCList link;
    89     certDBEntry entry;
    90     void *appData;
    91 };
    92 typedef struct certDBEntryListNodeStr  certDBEntryListNode;
    94 /*
    95  * A list node for a cert db entry.  The index is a unique identifier
    96  * to use for creating generic maps of a db.  This struct handles
    97  * the cert, nickname, and smime db entry types, as all three have a
    98  * single handle to a subject entry.
    99  * This structure is pointed to by certDBEntryListNode->appData.
   100  */
   101 typedef struct 
   102 {
   103     PLArenaPool *arena;
   104     int index;
   105     certDBEntryListNode *pSubject;
   106 } certDBEntryMap;
   108 /*
   109  * Subject entry is special case, it has bidirectional handles.  One
   110  * subject entry can point to several certs (using the same DN), and
   111  * a nickname and/or smime entry.
   112  * This structure is pointed to by certDBEntryListNode->appData.
   113  */
   114 typedef struct
   115 {
   116     PLArenaPool *arena;
   117     int index;
   118     int numCerts;
   119     certDBEntryListNode **pCerts;
   120     certDBEntryListNode *pNickname;
   121     certDBEntryListNode *pSMime;
   122 } certDBSubjectEntryMap;
   124 /*
   125  * A map of a certdb.
   126  */
   127 typedef struct
   128 {
   129     int numCerts;
   130     int numSubjects;
   131     int numNicknames;
   132     int numSMime;
   133     int numRevocation;
   134     certDBEntryListNode certs;      /* pointer to head of cert list */
   135     certDBEntryListNode subjects;   /* pointer to head of subject list */
   136     certDBEntryListNode nicknames;  /* pointer to head of nickname list */
   137     certDBEntryListNode smime;      /* pointer to head of smime list */
   138     certDBEntryListNode revocation; /* pointer to head of revocation list */
   139 } certDBArray;
   141 /* Cast list to the base element, a certDBEntryListNode. */
   142 #define LISTNODE_CAST(node) \
   143     ((certDBEntryListNode *)(node))
   145 static void 
   146 Usage(char *progName)
   147 {
   148 #define FPS fprintf(stderr, 
   149     FPS "Type %s -H for more detailed descriptions\n", progName);
   150     FPS "Usage:  %s -D [-d certdir] [-m] [-v [-f dumpfile]]\n", 
   151 	progName);
   152 #ifdef DORECOVER
   153     FPS "        %s -R -o newdbname [-d certdir] [-aprsx] [-v [-f dumpfile]]\n", 
   154 	progName);
   155 #endif
   156     exit(-1);
   157 }
   159 static void
   160 LongUsage(char *progName)
   161 {
   162     FPS "%-15s Display this help message.\n",
   163 	"-H");
   164     FPS "%-15s Dump analysis.  No changes will be made to the database.\n",
   165 	"-D");
   166     FPS "%-15s Cert database directory (default is ~/.netscape)\n",
   167 	"   -d certdir");
   168     FPS "%-15s Put database graph in ./mailfile (default is stdout).\n",
   169 	"   -m");
   170     FPS "%-15s Verbose mode.  Dumps the entire contents of your cert8.db.\n",
   171 	"   -v");
   172     FPS "%-15s File to dump verbose output into. (default is stdout)\n",
   173 	"   -f dumpfile");
   174 #ifdef DORECOVER
   175     FPS "%-15s Repair the database.  The program will look for broken\n",
   176 	"-R");
   177     FPS "%-15s dependencies between subject entries and certificates,\n",
   178         "");
   179     FPS "%-15s between nickname entries and subjects, and between SMIME\n",
   180         "");
   181     FPS "%-15s profiles and subjects.  Any duplicate entries will be\n",
   182         "");
   183     FPS "%-15s removed, any missing entries will be created.\n",
   184         "");
   185     FPS "%-15s File to store new database in (default is new_cert8.db)\n",
   186 	"   -o newdbname");
   187     FPS "%-15s Cert database directory (default is ~/.netscape)\n",
   188 	"   -d certdir");
   189     FPS "%-15s Prompt before removing any certificates.\n",
   190         "   -p");
   191     FPS "%-15s Keep all possible certificates.  Only remove certificates\n",
   192 	"   -a");
   193     FPS "%-15s which prevent creation of a consistent database.  Thus any\n",
   194 	"");
   195     FPS "%-15s expired or redundant entries will be kept.\n",
   196 	"");
   197     FPS "%-15s Keep redundant nickname/email entries.  It is possible\n",
   198 	"   -r");
   199     FPS "%-15s only one such entry will be usable.\n",
   200 	"");
   201     FPS "%-15s Don't require an S/MIME profile in order to keep an S/MIME\n",
   202 	"   -s");
   203     FPS "%-15s cert.  An empty profile will be created.\n",
   204 	"");
   205     FPS "%-15s Keep expired certificates.\n",
   206 	"   -x");
   207     FPS "%-15s Verbose mode - report all activity while recovering db.\n",
   208 	"   -v");
   209     FPS "%-15s File to dump verbose output into.\n",
   210 	"   -f dumpfile");
   211     FPS "\n");
   212 #endif
   213     exit(-1);
   214 #undef FPS
   215 }
   217 /*******************************************************************
   218  *
   219  *  Functions for dbck.
   220  *
   221  ******************************************************************/
   223 void
   224 printHexString(PRFileDesc *out, SECItem *hexval)
   225 {
   226     unsigned int i;
   227     for (i = 0; i < hexval->len; i++) {
   228 	if (i != hexval->len - 1) {
   229 	    PR_fprintf(out, "%02x:", hexval->data[i]);
   230 	} else {
   231 	    PR_fprintf(out, "%02x", hexval->data[i]);
   232 	}
   233     }
   234     PR_fprintf(out, "\n");
   235 }
   238 SECStatus
   239 dumpCertificate(CERTCertificate *cert, int num, PRFileDesc *outfile)
   240 {
   241     int userCert = 0;
   242     CERTCertTrust *trust = cert->trust;
   243     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
   244                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
   245                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
   246     if (num >= 0) {
   247 	PR_fprintf(outfile, "Certificate: %3d\n", num);
   248     } else {
   249 	PR_fprintf(outfile, "Certificate:\n");
   250     }
   251     PR_fprintf(outfile, "----------------\n");
   252     if (userCert)
   253 	PR_fprintf(outfile, "(User Cert)\n");
   254     PR_fprintf(outfile, "## SUBJECT:  %s\n", cert->subjectName);
   255     PR_fprintf(outfile, "## ISSUER:  %s\n", cert->issuerName);
   256     PR_fprintf(outfile, "## SERIAL NUMBER:  ");
   257     printHexString(outfile, &cert->serialNumber);
   258     {  /*  XXX should be separate function.  */
   259 	PRTime timeBefore, timeAfter;
   260 	PRExplodedTime beforePrintable, afterPrintable;
   261 	char *beforestr, *afterstr;
   262 	DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
   263 	DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
   264 	PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
   265 	PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
   266 	beforestr = PORT_Alloc(100);
   267 	afterstr = PORT_Alloc(100);
   268 	PR_FormatTime(beforestr, 100, "%a %b %d %H:%M:%S %Y", &beforePrintable);
   269 	PR_FormatTime(afterstr, 100, "%a %b %d %H:%M:%S %Y", &afterPrintable);
   270 	PR_fprintf(outfile, "## VALIDITY:  %s to %s\n", beforestr, afterstr);
   271     }
   272     PR_fprintf(outfile, "\n");
   273     return SECSuccess;
   274 }
   276 SECStatus
   277 dumpCertEntry(certDBEntryCert *entry, int num, PRFileDesc *outfile)
   278 {
   279 #if 0
   280     NSSLOWCERTCertificate *cert;
   281     /* should we check for existing duplicates? */
   282     cert = nsslowcert_DecodeDERCertificate(&entry->cert.derCert, 
   283 					    entry->cert.nickname);
   284 #else
   285     CERTCertificate *cert;
   286     cert = CERT_DecodeDERCertificate(&entry->derCert, PR_FALSE, NULL);
   287 #endif
   288     if (!cert) {
   289 	fprintf(stderr, "Failed to decode certificate.\n");
   290 	return SECFailure;
   291     }
   292     cert->trust = (CERTCertTrust *)&entry->trust;
   293     dumpCertificate(cert, num, outfile);
   294     CERT_DestroyCertificate(cert);
   295     return SECSuccess;
   296 }
   298 SECStatus
   299 dumpSubjectEntry(certDBEntrySubject *entry, int num, PRFileDesc *outfile)
   300 {
   301     char *subjectName = CERT_DerNameToAscii(&entry->derSubject);
   303     PR_fprintf(outfile, "Subject: %3d\n", num);
   304     PR_fprintf(outfile, "------------\n");
   305     PR_fprintf(outfile, "## %s\n", subjectName);
   306     if (entry->nickname)
   307 	PR_fprintf(outfile, "## Subject nickname:  %s\n", entry->nickname);
   308     if (entry->emailAddrs) {
   309 	unsigned int n;
   310 	for (n = 0; n < entry->nemailAddrs && entry->emailAddrs[n]; ++n) {
   311 	    char * emailAddr = entry->emailAddrs[n];
   312 	    if (emailAddr[0]) {
   313 		PR_fprintf(outfile, "## Subject email address:  %s\n", 
   314 	           emailAddr);
   315 	    }
   316 	}
   317     }
   318     PR_fprintf(outfile, "## This subject has %d cert(s).\n", entry->ncerts);
   319     PR_fprintf(outfile, "\n");
   320     PORT_Free(subjectName);
   321     return SECSuccess;
   322 }
   324 SECStatus
   325 dumpNicknameEntry(certDBEntryNickname *entry, int num, PRFileDesc *outfile)
   326 {
   327     PR_fprintf(outfile, "Nickname: %3d\n", num);
   328     PR_fprintf(outfile, "-------------\n");
   329     PR_fprintf(outfile, "##  \"%s\"\n\n", entry->nickname);
   330     return SECSuccess;
   331 }
   333 SECStatus
   334 dumpSMimeEntry(certDBEntrySMime *entry, int num, PRFileDesc *outfile)
   335 {
   336     PR_fprintf(outfile, "S/MIME Profile: %3d\n", num);
   337     PR_fprintf(outfile, "-------------------\n");
   338     PR_fprintf(outfile, "##  \"%s\"\n", entry->emailAddr);
   339 #ifdef OLDWAY
   340     PR_fprintf(outfile, "##  OPTIONS:  ");
   341     printHexString(outfile, &entry->smimeOptions);
   342     PR_fprintf(outfile, "##  TIMESTAMP:  ");
   343     printHexString(outfile, &entry->optionsDate);
   344 #else
   345     SECU_PrintAny(stdout, &entry->smimeOptions, "##  OPTIONS  ", 0);
   346     fflush(stdout);
   347     if (entry->optionsDate.len && entry->optionsDate.data)
   348 	PR_fprintf(outfile, "##  TIMESTAMP: %.*s\n", 
   349 	           entry->optionsDate.len, entry->optionsDate.data);
   350 #endif
   351     PR_fprintf(outfile, "\n");
   352     return SECSuccess;
   353 }
   355 SECStatus
   356 mapCertEntries(certDBArray *dbArray)
   357 {
   358     certDBEntryCert *certEntry;
   359     certDBEntrySubject *subjectEntry;
   360     certDBEntryListNode *certNode, *subjNode;
   361     certDBSubjectEntryMap *smap;
   362     certDBEntryMap *map;
   363     PLArenaPool *tmparena;
   364     SECItem derSubject;
   365     SECItem certKey;
   366     PRCList *cElem, *sElem;
   368     /* Arena for decoded entries */
   369     tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   370     if (tmparena == NULL) {
   371 	PORT_SetError(SEC_ERROR_NO_MEMORY);
   372 	return SECFailure;
   373     }
   375     /* Iterate over cert entries and map them to subject entries. 
   376      * NOTE: mapSubjectEntries must be called first to alloc memory
   377      * for array of subject->cert map.
   378      */
   379     for (cElem = PR_LIST_HEAD(&dbArray->certs.link); 
   380          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
   381 	certNode = LISTNODE_CAST(cElem);
   382 	certEntry = (certDBEntryCert *)&certNode->entry;
   383 	map = (certDBEntryMap *)certNode->appData;
   384 	CERT_NameFromDERCert(&certEntry->derCert, &derSubject);
   385 	CERT_KeyFromDERCert(tmparena, &certEntry->derCert, &certKey);
   386 	/*  Loop over found subjects for cert's DN.  */
   387 	for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   388 	     sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   389 	    subjNode = LISTNODE_CAST(sElem);
   390 	    subjectEntry = (certDBEntrySubject *)&subjNode->entry;
   391 	    if (SECITEM_ItemsAreEqual(&derSubject, &subjectEntry->derSubject)) {
   392 		unsigned int i;
   393 		/*  Found matching subject name, create link.  */
   394 		map->pSubject = subjNode;
   395 		/*  Make sure subject entry has cert's key.  */
   396 		for (i=0; i<subjectEntry->ncerts; i++) {
   397 		    if (SECITEM_ItemsAreEqual(&certKey,
   398 		                              &subjectEntry->certKeys[i])) {
   399 			/*  Found matching cert key.  */
   400 			smap = (certDBSubjectEntryMap *)subjNode->appData;
   401 			smap->pCerts[i] = certNode;
   402 			break;
   403 		    }
   404 		}
   405 	    }
   406 	}
   407     }
   408     PORT_FreeArena(tmparena, PR_FALSE);
   409     return SECSuccess;
   410 }
   412 SECStatus
   413 mapSubjectEntries(certDBArray *dbArray)
   414 {
   415     certDBEntrySubject *subjectEntry;
   416     certDBEntryListNode *subjNode;
   417     certDBSubjectEntryMap *subjMap;
   418     PRCList *sElem;
   420     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   421          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   422 	/* Iterate over subject entries and map subjects to nickname
   423 	 * and smime entries.  The cert<->subject map will be handled
   424 	 * by a subsequent call to mapCertEntries.
   425 	 */
   426 	subjNode = LISTNODE_CAST(sElem);
   427 	subjectEntry = (certDBEntrySubject *)&subjNode->entry;
   428 	subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   429 	/* need to alloc memory here for array of matching certs. */
   430 	subjMap->pCerts = PORT_ArenaAlloc(subjMap->arena, 
   431 	                                  subjectEntry->ncerts*sizeof(int));
   432 	subjMap->numCerts = subjectEntry->ncerts;
   433 	subjMap->pNickname = NoNickname;
   434 	subjMap->pSMime = NoSMime;
   436 	if (subjectEntry->nickname) {
   437 	    /* Subject should have a nickname entry, so create a link. */
   438 	    PRCList *nElem;
   439 	    for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
   440 	         nElem != &dbArray->nicknames.link; 
   441 	         nElem = PR_NEXT_LINK(nElem)) {
   442 		certDBEntryListNode *nickNode;
   443 		certDBEntryNickname *nicknameEntry;
   444 		/*  Look for subject's nickname in nickname entries.  */
   445 		nickNode = LISTNODE_CAST(nElem);
   446 		nicknameEntry = (certDBEntryNickname *)&nickNode->entry;
   447 		if (PL_strcmp(subjectEntry->nickname, 
   448 		              nicknameEntry->nickname) == 0) {
   449 		    /*  Found a nickname entry for subject's nickname.  */
   450 		    if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
   451 		                              &nicknameEntry->subjectName)) {
   452 			certDBEntryMap *nickMap;
   453 			nickMap = (certDBEntryMap *)nickNode->appData;
   454 			/*  Nickname and subject match.  */
   455 			subjMap->pNickname = nickNode;
   456 			nickMap->pSubject = subjNode;
   457 		    } else if (subjMap->pNickname == NoNickname) {
   458 			/*  Nickname entry found is for diff. subject.  */
   459 			subjMap->pNickname = WrongEntry;
   460 		    }
   461 		}
   462 	    }
   463 	}
   464 	if (subjectEntry->emailAddrs) {
   465 	    unsigned int n;
   466 	    for (n = 0; n < subjectEntry->nemailAddrs && 
   467 	                subjectEntry->emailAddrs[n]; ++n) {
   468 		char * emailAddr = subjectEntry->emailAddrs[n];
   469 		if (emailAddr[0]) {
   470 		    PRCList *mElem;
   471 		    /* Subject should have an smime entry, so create a link. */
   472 		    for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
   473 			 mElem != &dbArray->smime.link; 
   474 			 mElem = PR_NEXT_LINK(mElem)) {
   475 			certDBEntryListNode *smimeNode;
   476 			certDBEntrySMime *smimeEntry;
   477 			/*  Look for subject's email in S/MIME entries.  */
   478 			smimeNode = LISTNODE_CAST(mElem);
   479 			smimeEntry = (certDBEntrySMime *)&smimeNode->entry;
   480 			if (PL_strcmp(emailAddr, 
   481 				      smimeEntry->emailAddr) == 0) {
   482 			    /*  Found a S/MIME entry for subject's email.  */
   483 			    if (SECITEM_ItemsAreEqual(
   484 			    		&subjectEntry->derSubject,
   485 				        &smimeEntry->subjectName)) {
   486 				certDBEntryMap *smimeMap;
   487 				/*  S/MIME entry and subject match.  */
   488 				subjMap->pSMime = smimeNode;
   489 				smimeMap = (certDBEntryMap *)smimeNode->appData;
   490 				smimeMap->pSubject = subjNode;
   491 			    } else if (subjMap->pSMime == NoSMime) {
   492 				/*  S/MIME entry found is for diff. subject.  */
   493 				subjMap->pSMime = WrongEntry;
   494 			    }
   495 			}
   496 		    }   /* end for */
   497 		}   /* endif (emailAddr[0]) */
   498 	    }   /* end for */
   499 	}   /* endif (subjectEntry->emailAddrs) */
   500     }
   501     return SECSuccess;
   502 }
   504 void
   505 printnode(dbDebugInfo *info, const char *str, int num)
   506 {
   507     if (!info->dograph)
   508 	return;
   509     if (num < 0) {
   510 	PR_fprintf(info->graphfile, str);
   511     } else {
   512 	PR_fprintf(info->graphfile, str, num);
   513     }
   514 }
   516 PRBool
   517 map_handle_is_ok(dbDebugInfo *info, void *mapPtr, int indent)
   518 {
   519     if (mapPtr == NULL) {
   520 	if (indent > 0)
   521 	    printnode(info, "                ", -1);
   522 	if (indent >= 0)
   523 	    printnode(info, "******************* ", -1);
   524 	return PR_FALSE;
   525     } else if (mapPtr == WrongEntry) {
   526 	if (indent > 0)
   527 	    printnode(info, "                  ", -1);
   528 	if (indent >= 0)
   529 	    printnode(info, "??????????????????? ", -1);
   530 	return PR_FALSE;
   531     } else {
   532 	return PR_TRUE;
   533     }
   534 }
   536 /* these call each other */
   537 void print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, 
   538                        int direction);
   539 void print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, 
   540                           int direction);
   541 void print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
   542                          int direction, int optindex, int opttype);
   543 void print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, 
   544                       int direction);
   546 /* Given an smime entry, print its unique identifier.  If GOLEFT is 
   547  * specified, print the cert<-subject<-smime map, else just print
   548  * the smime entry.
   549  */
   550 void
   551 print_smime_graph(dbDebugInfo *info, certDBEntryMap *smimeMap, int direction)
   552 {
   553     certDBSubjectEntryMap *subjMap;
   554     certDBEntryListNode *subjNode;
   555     if (direction == GOLEFT) {
   556 	/* Need to output subject and cert first, see print_subject_graph */
   557 	subjNode = smimeMap->pSubject;
   558 	if (map_handle_is_ok(info, (void *)subjNode, 1)) {
   559 	    subjMap = (certDBSubjectEntryMap *)subjNode->appData; 
   560 	    print_subject_graph(info, subjMap, GOLEFT,
   561 	                        smimeMap->index, certDBEntryTypeSMimeProfile);
   562 	} else {
   563 	    printnode(info, "<---- S/MIME   %5d   ", smimeMap->index);
   564 	    info->dbErrors[NoSubjectForSMime]++;
   565 	}
   566     } else {
   567 	printnode(info, "S/MIME   %5d   ", smimeMap->index);
   568     }
   569 }
   571 /* Given a nickname entry, print its unique identifier.  If GOLEFT is 
   572  * specified, print the cert<-subject<-nickname map, else just print
   573  * the nickname entry.
   574  */
   575 void
   576 print_nickname_graph(dbDebugInfo *info, certDBEntryMap *nickMap, int direction)
   577 {
   578     certDBSubjectEntryMap *subjMap;
   579     certDBEntryListNode *subjNode;
   580     if (direction == GOLEFT) {
   581 	/* Need to output subject and cert first, see print_subject_graph */
   582 	subjNode = nickMap->pSubject;
   583 	if (map_handle_is_ok(info, (void *)subjNode, 1)) {
   584 	    subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   585 	    print_subject_graph(info, subjMap, GOLEFT,
   586 	                        nickMap->index, certDBEntryTypeNickname);
   587 	} else {
   588 	    printnode(info, "<---- Nickname %5d   ", nickMap->index);
   589 	    info->dbErrors[NoSubjectForNickname]++;
   590 	}
   591     } else {
   592 	printnode(info, "Nickname %5d   ", nickMap->index);
   593     }
   594 }
   596 /* Given a subject entry, if going right print the graph of the nickname|smime
   597  * that it maps to (by its unique identifier); and if going left
   598  * print the list of certs that it points to.
   599  */
   600 void
   601 print_subject_graph(dbDebugInfo *info, certDBSubjectEntryMap *subjMap, 
   602                     int direction, int optindex, int opttype)
   603 {
   604     certDBEntryMap *map;
   605     certDBEntryListNode *node;
   606     int i;
   607     /* The first line of output always contains the cert id, subject id,
   608      * and nickname|smime id.  Subsequent lines may contain additional
   609      * cert id's for the subject if going left or both directions.
   610      * Ex. of printing the graph for a subject entry:
   611      * Cert 3 <- Subject 5 -> Nickname 32
   612      * Cert 8 /
   613      * Cert 9 /
   614      * means subject 5 has 3 certs, 3, 8, and 9, and corresponds
   615      * to nickname entry 32.
   616      * To accomplish the above, it is required to dump the entire first
   617      * line left-to-right, regardless of the input direction, and then
   618      * finish up any remaining cert entries.  Hence the code is uglier
   619      * than one may expect.
   620      */
   621     if (direction == GOLEFT || direction == GOBOTH) {
   622 	/* In this case, nothing should be output until the first cert is
   623 	 * located and output (cert 3 in the above example).
   624 	 */
   625 	if (subjMap->numCerts == 0 || subjMap->pCerts == NULL)
   626 	    /* XXX uh-oh */
   627 	    return;
   628 	/* get the first cert and dump it. */
   629 	node = subjMap->pCerts[0];
   630 	if (map_handle_is_ok(info, (void *)node, 0)) {
   631 	    map = (certDBEntryMap *)node->appData;
   632 	    /* going left here stops. */
   633 	    print_cert_graph(info, map, GOLEFT); 
   634 	} else {
   635 	    info->dbErrors[SubjectHasNoKeyForCert]++;
   636 	}
   637 	/* Now it is safe to output the subject id. */
   638 	if (direction == GOLEFT)
   639 	    printnode(info, "Subject  %5d <---- ", subjMap->index);
   640 	else /* direction == GOBOTH */
   641 	    printnode(info, "Subject  %5d ----> ", subjMap->index);
   642     }
   643     if (direction == GORIGHT || direction == GOBOTH) { 
   644 	/* Okay, now output the nickname|smime for this subject. */
   645 	if (direction != GOBOTH) /* handled above */
   646 	   printnode(info, "Subject  %5d ----> ", subjMap->index);
   647 	if (subjMap->pNickname) {
   648 	    node = subjMap->pNickname;
   649 	    if (map_handle_is_ok(info, (void *)node, 0)) {
   650 		map = (certDBEntryMap *)node->appData;
   651 		/* going right here stops. */
   652 		print_nickname_graph(info, map, GORIGHT);
   653 	    }
   654 	}
   655 	if (subjMap->pSMime) {
   656 	    node = subjMap->pSMime;
   657 	    if (map_handle_is_ok(info, (void *)node, 0)) {
   658 		map = (certDBEntryMap *)node->appData;
   659 		/* going right here stops. */
   660 		print_smime_graph(info, map, GORIGHT); 
   661 	    }
   662 	}
   663 	if (!subjMap->pNickname && !subjMap->pSMime) {
   664 	    printnode(info, "******************* ", -1);
   665 	    info->dbErrors[NoNicknameOrSMimeForSubject]++;
   666 	}
   667 	if (subjMap->pNickname && subjMap->pSMime) {
   668 	    info->dbErrors[NicknameAndSMimeEntries]++;
   669 	}
   670     }
   671     if (direction != GORIGHT) { /* going right has only one cert */
   672 	if (opttype == certDBEntryTypeNickname)
   673 	    printnode(info, "Nickname %5d   ", optindex);
   674 	else if (opttype == certDBEntryTypeSMimeProfile)
   675 	    printnode(info, "S/MIME   %5d   ", optindex);
   676 	for (i=1 /* 1st one already done */; i<subjMap->numCerts; i++) {
   677 	    printnode(info, "\n", -1); /* start a new line */
   678 	    node = subjMap->pCerts[i];
   679 	    if (map_handle_is_ok(info, (void *)node, 0)) {
   680 		map = (certDBEntryMap *)node->appData;
   681 		/* going left here stops. */
   682 		print_cert_graph(info, map, GOLEFT); 
   683 		printnode(info, "/", -1);
   684 	    }
   685 	}
   686     }
   687 }
   689 /* Given a cert entry, print its unique identifer.  If GORIGHT is specified,
   690  * print the cert->subject->nickname|smime map, else just print
   691  * the cert entry.
   692  */
   693 void
   694 print_cert_graph(dbDebugInfo *info, certDBEntryMap *certMap, int direction)
   695 {
   696     certDBSubjectEntryMap *subjMap;
   697     certDBEntryListNode *subjNode;
   698     if (direction == GOLEFT) {
   699 	printnode(info, "Cert     %5d <---- ", certMap->index);
   700 	/* only want cert entry, terminate here. */
   701 	return;
   702     }
   703     /* Keep going right then. */
   704     printnode(info, "Cert     %5d ----> ", certMap->index);
   705     subjNode = certMap->pSubject;
   706     if (map_handle_is_ok(info, (void *)subjNode, 0)) {
   707 	subjMap = (certDBSubjectEntryMap *)subjNode->appData;
   708 	print_subject_graph(info, subjMap, GORIGHT, -1, -1);
   709     } else {
   710 	info->dbErrors[NoSubjectForCert]++;
   711     }
   712 }
   714 SECStatus
   715 computeDBGraph(certDBArray *dbArray, dbDebugInfo *info)
   716 {
   717     PRCList *cElem, *sElem, *nElem, *mElem;
   718     certDBEntryListNode *node;
   719     certDBEntryMap *map;
   720     certDBSubjectEntryMap *subjMap;
   722     /* Graph is of this form:
   723      *
   724      * certs:
   725      * cert ---> subject ---> (nickname|smime)
   726      *
   727      * subjects:
   728      * cert <--- subject ---> (nickname|smime)
   729      *
   730      * nicknames and smime:
   731      * cert <--- subject <--- (nickname|smime)
   732      */
   734     /* Print cert graph. */
   735     for (cElem = PR_LIST_HEAD(&dbArray->certs.link);
   736          cElem != &dbArray->certs.link; cElem = PR_NEXT_LINK(cElem)) {
   737 	/* Print graph of everything to right of cert entry. */
   738 	node = LISTNODE_CAST(cElem);
   739 	map = (certDBEntryMap *)node->appData;
   740 	print_cert_graph(info, map, GORIGHT);
   741 	printnode(info, "\n", -1);
   742     }
   743     printnode(info, "\n", -1);
   745     /* Print subject graph. */
   746     for (sElem = PR_LIST_HEAD(&dbArray->subjects.link);
   747          sElem != &dbArray->subjects.link; sElem = PR_NEXT_LINK(sElem)) {
   748 	/* Print graph of everything to both sides of subject entry. */
   749 	node = LISTNODE_CAST(sElem);
   750 	subjMap = (certDBSubjectEntryMap *)node->appData;
   751 	print_subject_graph(info, subjMap, GOBOTH, -1, -1);
   752 	printnode(info, "\n", -1);
   753     }
   754     printnode(info, "\n", -1);
   756     /* Print nickname graph. */
   757     for (nElem = PR_LIST_HEAD(&dbArray->nicknames.link);
   758          nElem != &dbArray->nicknames.link; nElem = PR_NEXT_LINK(nElem)) {
   759 	/* Print graph of everything to left of nickname entry. */
   760 	node = LISTNODE_CAST(nElem);
   761 	map = (certDBEntryMap *)node->appData;
   762 	print_nickname_graph(info, map, GOLEFT);
   763 	printnode(info, "\n", -1);
   764     }
   765     printnode(info, "\n", -1);
   767     /* Print smime graph. */
   768     for (mElem = PR_LIST_HEAD(&dbArray->smime.link);
   769          mElem != &dbArray->smime.link; mElem = PR_NEXT_LINK(mElem)) {
   770 	/* Print graph of everything to left of smime entry. */
   771 	node = LISTNODE_CAST(mElem);
   772 	if (node == NULL) break;
   773 	map = (certDBEntryMap *)node->appData;
   774 	print_smime_graph(info, map, GOLEFT);
   775 	printnode(info, "\n", -1);
   776     }
   777     printnode(info, "\n", -1);
   779     return SECSuccess;
   780 }
   782 /*
   783  * List the entries in the db, showing handles between entry types.
   784  */
   785 void
   786 verboseOutput(certDBArray *dbArray, dbDebugInfo *info)
   787 {
   788     int i, ref;
   789     PRCList *elem;
   790     certDBEntryListNode *node;
   791     certDBEntryMap *map;
   792     certDBSubjectEntryMap *smap;
   793     certDBEntrySubject *subjectEntry;
   795     /* List certs */
   796     for (elem = PR_LIST_HEAD(&dbArray->certs.link);
   797          elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
   798 	node = LISTNODE_CAST(elem);
   799 	map = (certDBEntryMap *)node->appData;
   800 	dumpCertEntry((certDBEntryCert*)&node->entry, map->index, info->out);
   801 	/* walk the cert handle to it's subject entry */
   802 	if (map_handle_is_ok(info, map->pSubject, -1)) {
   803 	    smap = (certDBSubjectEntryMap *)map->pSubject->appData;
   804 	    ref = smap->index;
   805 	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   806 	} else {
   807 	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   808 	}
   809     }
   810     /* List subjects */
   811     for (elem = PR_LIST_HEAD(&dbArray->subjects.link);
   812          elem != &dbArray->subjects.link; elem = PR_NEXT_LINK(elem)) {
   813 	int refs = 0;
   814 	node = LISTNODE_CAST(elem);
   815 	subjectEntry = (certDBEntrySubject *)&node->entry;
   816 	smap = (certDBSubjectEntryMap *)node->appData;
   817 	dumpSubjectEntry(subjectEntry, smap->index, info->out);
   818 	/* iterate over subject's certs */
   819 	for (i=0; i<smap->numCerts; i++) {
   820 	    /* walk each subject handle to it's cert entries */
   821 	    if (map_handle_is_ok(info, smap->pCerts[i], -1)) {
   822 		ref = ((certDBEntryMap *)smap->pCerts[i]->appData)->index;
   823 		PR_fprintf(info->out, "-->(%d. certificate %d)\n", i, ref);
   824 	    } else {
   825 		PR_fprintf(info->out, "-->(%d. MISSING CERT ENTRY)\n", i);
   826 	    }
   827 	}
   828 	if (subjectEntry->nickname) {
   829 	    ++refs;
   830 	    /* walk each subject handle to it's nickname entry */
   831 	    if (map_handle_is_ok(info, smap->pNickname, -1)) {
   832 		ref = ((certDBEntryMap *)smap->pNickname->appData)->index;
   833 		PR_fprintf(info->out, "-->(nickname %d)\n", ref);
   834 	    } else {
   835 		PR_fprintf(info->out, "-->(MISSING NICKNAME ENTRY)\n");
   836 	    }
   837 	}
   838 	if (subjectEntry->nemailAddrs && 
   839 	    subjectEntry->emailAddrs &&
   840 	    subjectEntry->emailAddrs[0] &&
   841 	    subjectEntry->emailAddrs[0][0]) {
   842 	    ++refs;
   843 	    /* walk each subject handle to it's smime entry */
   844 	    if (map_handle_is_ok(info, smap->pSMime, -1)) {
   845 		ref = ((certDBEntryMap *)smap->pSMime->appData)->index;
   846 		PR_fprintf(info->out, "-->(s/mime %d)\n", ref);
   847 	    } else {
   848 		PR_fprintf(info->out, "-->(MISSING S/MIME ENTRY)\n");
   849 	    }
   850 	}
   851 	if (!refs) {
   852 	    PR_fprintf(info->out, "-->(NO NICKNAME+S/MIME ENTRY)\n");
   853 	}
   854 	PR_fprintf(info->out, "\n\n");
   855     }
   856     for (elem = PR_LIST_HEAD(&dbArray->nicknames.link);
   857          elem != &dbArray->nicknames.link; elem = PR_NEXT_LINK(elem)) {
   858 	node = LISTNODE_CAST(elem);
   859 	map = (certDBEntryMap *)node->appData;
   860 	dumpNicknameEntry((certDBEntryNickname*)&node->entry, map->index, 
   861 	                  info->out);
   862 	if (map_handle_is_ok(info, map->pSubject, -1)) {
   863 	    ref = ((certDBEntryMap *)map->pSubject->appData)->index;
   864 	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   865 	} else {
   866 	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   867 	}
   868     }
   869     for (elem = PR_LIST_HEAD(&dbArray->smime.link);
   870          elem != &dbArray->smime.link; elem = PR_NEXT_LINK(elem)) {
   871 	node = LISTNODE_CAST(elem);
   872 	map = (certDBEntryMap *)node->appData;
   873 	dumpSMimeEntry((certDBEntrySMime*)&node->entry, map->index, info->out);
   874 	if (map_handle_is_ok(info, map->pSubject, -1)) {
   875 	    ref = ((certDBEntryMap *)map->pSubject->appData)->index;
   876 	    PR_fprintf(info->out, "-->(subject %d)\n\n\n", ref);
   877 	} else {
   878 	    PR_fprintf(info->out, "-->(MISSING SUBJECT ENTRY)\n\n\n");
   879 	}
   880     }
   881     PR_fprintf(info->out, "\n\n");
   882 }
   885 /* A callback function, intended to be called from nsslowcert_TraverseDBEntries
   886  * Builds a PRCList of DB entries of the specified type.
   887  */
   888 SECStatus 
   889 SEC_GetCertDBEntryList(SECItem *dbdata, SECItem *dbkey, 
   890                        certDBEntryType entryType, void *pdata)
   891 {
   892     certDBEntry         * entry;
   893     certDBEntryListNode * node;
   894     PRCList             * list = (PRCList *)pdata;
   896     if (!dbdata || !dbkey || !pdata || !dbdata->data || !dbkey->data) {
   897     	PORT_SetError(SEC_ERROR_INVALID_ARGS);
   898 	return SECFailure;
   899     }
   900     entry = nsslowcert_DecodeAnyDBEntry(dbdata, dbkey, entryType, NULL);
   901     if (!entry) {
   902     	return SECSuccess; /* skip it */
   903     }
   904     node = PORT_ArenaZNew(entry->common.arena, certDBEntryListNode);
   905     if (!node) {
   906     	/* DestroyDBEntry(entry); */
   907 	PLArenaPool *arena = entry->common.arena;
   908 	PORT_Memset(&entry->common, 0, sizeof entry->common);
   909 	PORT_FreeArena(arena, PR_FALSE);
   910 	return SECFailure;
   911     }
   912     node->entry = *entry;  		/* crude but effective. */
   913     PR_INIT_CLIST(&node->link);
   914     PR_INSERT_BEFORE(&node->link, list);
   915     return SECSuccess;
   916 }
   919 int
   920 fillDBEntryArray(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, 
   921                  certDBEntryListNode *list)
   922 {
   923     PRCList *elem;
   924     certDBEntryListNode *node;
   925     certDBEntryMap *mnode;
   926     certDBSubjectEntryMap *smnode;
   927     PLArenaPool *arena;
   928     int count = 0;
   930     /* Initialize a dummy entry in the list.  The list head will be the
   931      * next element, so this element is skipped by for loops.
   932      */
   933     PR_INIT_CLIST((PRCList *)list);
   934     /* Collect all of the cert db entries for this type into a list. */
   935     nsslowcert_TraverseDBEntries(handle, type, SEC_GetCertDBEntryList, list);
   937     for (elem = PR_LIST_HEAD(&list->link); 
   938          elem != &list->link; elem = PR_NEXT_LINK(elem)) {
   939 	/* Iterate over the entries and ... */
   940 	node = (certDBEntryListNode *)elem;
   941 	if (type != certDBEntryTypeSubject) {
   942 	    arena = PORT_NewArena(sizeof(*mnode));
   943 	    mnode = PORT_ArenaZNew(arena, certDBEntryMap);
   944 	    mnode->arena = arena;
   945 	    /* ... assign a unique index number to each node, and ... */
   946 	    mnode->index = count;
   947 	    /* ... set the map pointer for the node. */
   948 	    node->appData = (void *)mnode;
   949 	} else {
   950 	    /* allocate some room for the cert pointers also */
   951 	    arena = PORT_NewArena(sizeof(*smnode) + 20*sizeof(void *));
   952 	    smnode = PORT_ArenaZNew(arena, certDBSubjectEntryMap);
   953 	    smnode->arena = arena;
   954 	    smnode->index = count;
   955 	    node->appData = (void *)smnode;
   956 	}
   957 	count++;
   958     }
   959     return count;
   960 }
   962 void
   963 freeDBEntryList(PRCList *list)
   964 {
   965     PRCList *next, *elem;
   966     certDBEntryListNode *node;
   967     certDBEntryMap *map;
   969     for (elem = PR_LIST_HEAD(list); elem != list;) { 
   970 	next = PR_NEXT_LINK(elem);
   971 	node = (certDBEntryListNode *)elem;
   972 	map = (certDBEntryMap *)node->appData;
   973 	PR_REMOVE_LINK(&node->link);
   974 	PORT_FreeArena(map->arena, PR_TRUE);
   975 	PORT_FreeArena(node->entry.common.arena, PR_TRUE);
   976 	elem = next;
   977     }
   978 }
   980 void
   981 DBCK_DebugDB(NSSLOWCERTCertDBHandle *handle, PRFileDesc *out, 
   982 	     PRFileDesc *mailfile)
   983 {
   984     int i, nCertsFound, nSubjFound, nErr;
   985     int nCerts, nSubjects, nSubjCerts, nNicknames, nSMime, nRevocation;
   986     PRCList *elem;
   987     char c;
   988     dbDebugInfo info;
   989     certDBArray dbArray;
   991     PORT_Memset(&dbArray, 0, sizeof(dbArray));
   992     PORT_Memset(&info, 0, sizeof(info));
   993     info.verbose = (PRBool)(out != NULL);
   994     info.dograph = info.verbose;
   995     info.out       = (out)    ? out      : PR_STDOUT;
   996     info.graphfile = mailfile ? mailfile : PR_STDOUT;
   998     /*  Fill the array structure with cert/subject/nickname/smime entries.  */
   999     dbArray.numCerts     = fillDBEntryArray(handle, certDBEntryTypeCert, 
  1000                                             &dbArray.certs);
  1001     dbArray.numSubjects  = fillDBEntryArray(handle, certDBEntryTypeSubject, 
  1002                                             &dbArray.subjects);
  1003     dbArray.numNicknames = fillDBEntryArray(handle, certDBEntryTypeNickname, 
  1004                                             &dbArray.nicknames);
  1005     dbArray.numSMime     = fillDBEntryArray(handle, certDBEntryTypeSMimeProfile, 
  1006                                             &dbArray.smime);
  1007     dbArray.numRevocation= fillDBEntryArray(handle, certDBEntryTypeRevocation, 
  1008                                             &dbArray.revocation);
  1010     /*  Compute the map between the database entries.  */
  1011     mapSubjectEntries(&dbArray);
  1012     mapCertEntries(&dbArray);
  1013     computeDBGraph(&dbArray, &info);
  1015     /*  Store the totals for later reference.  */
  1016     nCerts     = dbArray.numCerts;
  1017     nSubjects  = dbArray.numSubjects;
  1018     nNicknames = dbArray.numNicknames;
  1019     nSMime     = dbArray.numSMime;
  1020     nRevocation= dbArray.numRevocation;
  1021     nSubjCerts = 0;
  1022     for (elem = PR_LIST_HEAD(&dbArray.subjects.link);
  1023          elem != &dbArray.subjects.link; elem = PR_NEXT_LINK(elem)) {
  1024 	certDBSubjectEntryMap *smap;
  1025 	smap = (certDBSubjectEntryMap *)LISTNODE_CAST(elem)->appData;
  1026 	nSubjCerts += smap->numCerts;
  1029     if (info.verbose) {
  1030 	/*  Dump the database contents.  */
  1031 	verboseOutput(&dbArray, &info);
  1034     freeDBEntryList(&dbArray.certs.link);
  1035     freeDBEntryList(&dbArray.subjects.link);
  1036     freeDBEntryList(&dbArray.nicknames.link);
  1037     freeDBEntryList(&dbArray.smime.link);
  1038     freeDBEntryList(&dbArray.revocation.link);
  1040     PR_fprintf(info.out, "\n");
  1041     PR_fprintf(info.out, "Database statistics:\n");
  1042     PR_fprintf(info.out, "N0: Found %4d Certificate entries.\n", 
  1043                           nCerts);
  1044     PR_fprintf(info.out, "N1: Found %4d Subject entries (unique DN's).\n", 
  1045                           nSubjects);
  1046     PR_fprintf(info.out, "N2: Found %4d Cert keys within Subject entries.\n", 
  1047                           nSubjCerts);
  1048     PR_fprintf(info.out, "N3: Found %4d Nickname entries.\n", 
  1049                           nNicknames);
  1050     PR_fprintf(info.out, "N4: Found %4d S/MIME entries.\n", 
  1051                           nSMime);
  1052     PR_fprintf(info.out, "N5: Found %4d CRL entries.\n", 
  1053                           nRevocation);
  1054     PR_fprintf(info.out, "\n");
  1056     nErr = 0;
  1057     for (i=0; i < NUM_ERROR_TYPES; i++) {
  1058 	PR_fprintf(info.out, "E%d: Found %4d %s\n", 
  1059 	           i, info.dbErrors[i], errResult[i]);
  1060 	nErr += info.dbErrors[i];
  1062     PR_fprintf(info.out, "--------------\n    Found %4d errors in database.\n", 
  1063                nErr);
  1065     PR_fprintf(info.out, "\nCertificates:\n");
  1066     PR_fprintf(info.out, "N0 == N2 + E%d + E%d\n", NoSubjectForCert, 
  1067                                                    SubjectHasNoKeyForCert);
  1068     nCertsFound = nSubjCerts +
  1069                   info.dbErrors[NoSubjectForCert] +
  1070                   info.dbErrors[SubjectHasNoKeyForCert];
  1071     c = (nCertsFound == nCerts) ? '=' : '!';
  1072     PR_fprintf(info.out, "%d %c= %d + %d + %d\n", nCerts, c, nSubjCerts, 
  1073                   info.dbErrors[NoSubjectForCert],
  1074                   info.dbErrors[SubjectHasNoKeyForCert]);
  1075     PR_fprintf(info.out, "\nSubjects:\n");
  1076     PR_fprintf(info.out, 
  1077     "N1 == N3 + N4 + E%d + E%d + E%d + E%d + E%d - E%d - E%d - E%d\n",
  1078                   NoNicknameOrSMimeForSubject, 
  1079 		  WrongNicknameForSubject,
  1080 		  NoNicknameEntry, 
  1081 		  WrongSMimeForSubject, 
  1082 		  NoSMimeEntry,
  1083 		  NoSubjectForNickname, 
  1084 		  NoSubjectForSMime,
  1085 		  NicknameAndSMimeEntries);
  1086     nSubjFound = nNicknames + nSMime + 
  1087                  info.dbErrors[NoNicknameOrSMimeForSubject] +
  1088 		 info.dbErrors[WrongNicknameForSubject] +
  1089 		 info.dbErrors[NoNicknameEntry] +
  1090 		 info.dbErrors[WrongSMimeForSubject] +
  1091                  info.dbErrors[NoSMimeEntry] -
  1092 		 info.dbErrors[NoSubjectForNickname] -
  1093 		 info.dbErrors[NoSubjectForSMime] -
  1094 		 info.dbErrors[NicknameAndSMimeEntries];
  1095     c = (nSubjFound == nSubjects) ? '=' : '!';
  1096     PR_fprintf(info.out, 
  1097     "%2d %c= %2d + %2d + %2d + %2d + %2d + %2d + %2d - %2d - %2d - %2d\n",
  1098                   nSubjects, c, nNicknames, nSMime,
  1099                   info.dbErrors[NoNicknameOrSMimeForSubject],
  1100 		  info.dbErrors[WrongNicknameForSubject],
  1101 		  info.dbErrors[NoNicknameEntry],
  1102 		  info.dbErrors[WrongSMimeForSubject],
  1103                   info.dbErrors[NoSMimeEntry],
  1104 		  info.dbErrors[NoSubjectForNickname],
  1105 		  info.dbErrors[NoSubjectForSMime],
  1106 		  info.dbErrors[NicknameAndSMimeEntries]);
  1107     PR_fprintf(info.out, "\n");
  1110 #ifdef DORECOVER
  1111 #include "dbrecover.c"
  1112 #endif /* DORECOVER */
  1114 enum {
  1115     cmd_Debug = 0,
  1116     cmd_LongUsage,
  1117     cmd_Recover
  1118 };
  1120 enum {
  1121     opt_KeepAll = 0,
  1122     opt_CertDir,
  1123     opt_Dumpfile,
  1124     opt_InputDB,
  1125     opt_OutputDB,
  1126     opt_Mailfile,
  1127     opt_Prompt,
  1128     opt_KeepRedundant,
  1129     opt_KeepNoSMimeProfile,
  1130     opt_Verbose,
  1131     opt_KeepExpired
  1132 };
  1134 static secuCommandFlag dbck_commands[] =
  1136     { /* cmd_Debug,    */  'D', PR_FALSE, 0, PR_FALSE },
  1137     { /* cmd_LongUsage,*/  'H', PR_FALSE, 0, PR_FALSE },
  1138     { /* cmd_Recover,  */  'R', PR_FALSE, 0, PR_FALSE }
  1139 };
  1141 static secuCommandFlag dbck_options[] =
  1143     { /* opt_KeepAll,           */  'a', PR_FALSE, 0, PR_FALSE },
  1144     { /* opt_CertDir,           */  'd', PR_TRUE,  0, PR_FALSE },
  1145     { /* opt_Dumpfile,          */  'f', PR_TRUE,  0, PR_FALSE },
  1146     { /* opt_InputDB,           */  'i', PR_TRUE,  0, PR_FALSE },
  1147     { /* opt_OutputDB,          */  'o', PR_TRUE,  0, PR_FALSE },
  1148     { /* opt_Mailfile,          */  'm', PR_FALSE, 0, PR_FALSE },
  1149     { /* opt_Prompt,            */  'p', PR_FALSE, 0, PR_FALSE },
  1150     { /* opt_KeepRedundant,     */  'r', PR_FALSE, 0, PR_FALSE },
  1151     { /* opt_KeepNoSMimeProfile,*/  's', PR_FALSE, 0, PR_FALSE },
  1152     { /* opt_Verbose,           */  'v', PR_FALSE, 0, PR_FALSE },
  1153     { /* opt_KeepExpired,       */  'x', PR_FALSE, 0, PR_FALSE }
  1154 };
  1156 #define CERT_DB_FMT "%s/cert%s.db"
  1158 static char *
  1159 dbck_certdb_name_cb(void *arg, int dbVersion)
  1161     const char *configdir = (const char *)arg;
  1162     const char *dbver;
  1163     char *smpname = NULL;
  1164     char *dbname = NULL;
  1166     switch (dbVersion) {
  1167       case 8:
  1168 	dbver = "8";
  1169 	break;
  1170       case 7:
  1171 	dbver = "7";
  1172 	break;
  1173       case 6:
  1174 	dbver = "6";
  1175 	break;
  1176       case 5:
  1177 	dbver = "5";
  1178 	break;
  1179       case 4:
  1180       default:
  1181 	dbver = "";
  1182 	break;
  1185     /* make sure we return something allocated with PORT_ so we have properly
  1186      * matched frees at the end */
  1187     smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
  1188     if (smpname) {
  1189 	dbname = PORT_Strdup(smpname);
  1190 	PR_smprintf_free(smpname);
  1192     return dbname;
  1196 int 
  1197 main(int argc, char **argv)
  1199     NSSLOWCERTCertDBHandle *certHandle;
  1201     PRFileDesc *mailfile = NULL;
  1202     PRFileDesc *dumpfile = NULL;
  1204     char * pathname     = 0;
  1205     char * fullname     = 0;
  1206     char * newdbname    = 0;
  1208     PRBool removeExpired, requireProfile, singleEntry;
  1209     SECStatus   rv;
  1210     secuCommand dbck;
  1212     dbck.numCommands = sizeof(dbck_commands) / sizeof(secuCommandFlag);
  1213     dbck.numOptions = sizeof(dbck_options) / sizeof(secuCommandFlag);
  1214     dbck.commands = dbck_commands;
  1215     dbck.options = dbck_options;
  1217     progName = strrchr(argv[0], '/');
  1218     progName = progName ? progName+1 : argv[0];
  1220     rv = SECU_ParseCommandLine(argc, argv, progName, &dbck);
  1222     if (rv != SECSuccess)
  1223 	Usage(progName);
  1225     if (dbck.commands[cmd_LongUsage].activated)
  1226 	LongUsage(progName);
  1228     if (!dbck.commands[cmd_Debug].activated &&
  1229         !dbck.commands[cmd_Recover].activated) {
  1230 	PR_fprintf(PR_STDERR, "Please specify -H, -D or -R.\n");
  1231 	Usage(progName);
  1234     removeExpired = !(dbck.options[opt_KeepAll].activated ||
  1235                       dbck.options[opt_KeepExpired].activated);
  1237     requireProfile = !(dbck.options[opt_KeepAll].activated ||
  1238                     dbck.options[opt_KeepNoSMimeProfile].activated);
  1240     singleEntry = !(dbck.options[opt_KeepAll].activated ||
  1241                     dbck.options[opt_KeepRedundant].activated);
  1243     if (dbck.options[opt_OutputDB].activated) {
  1244 	newdbname = PL_strdup(dbck.options[opt_OutputDB].arg);
  1245     } else {
  1246 	newdbname = PL_strdup("new_cert8.db");
  1249     /*  Create a generic graph of the database.  */
  1250     if (dbck.options[opt_Mailfile].activated) {
  1251 	mailfile = PR_Open("./mailfile", PR_RDWR | PR_CREATE_FILE, 00660);
  1252 	if (!mailfile) {
  1253 	    fprintf(stderr, "Unable to create mailfile.\n");
  1254 	    return -1;
  1258     /*  Dump all debugging info while running.  */
  1259     if (dbck.options[opt_Verbose].activated) {
  1260 	if (dbck.options[opt_Dumpfile].activated) {
  1261 	    dumpfile = PR_Open(dbck.options[opt_Dumpfile].arg,
  1262 	                       PR_RDWR | PR_CREATE_FILE, 00660);
  1263 	    if (!dumpfile) {
  1264 		fprintf(stderr, "Unable to create dumpfile.\n");
  1265 		return -1;
  1267 	} else {
  1268 	    dumpfile = PR_STDOUT;
  1272     /*  Set the cert database directory.  */
  1273     if (dbck.options[opt_CertDir].activated) {
  1274 	SECU_ConfigDirectory(dbck.options[opt_CertDir].arg);
  1277     pathname = SECU_ConfigDirectory(NULL);
  1279     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
  1280     rv = NSS_NoDB_Init(pathname);
  1281     if (rv != SECSuccess) {
  1282 	fprintf(stderr, "NSS_NoDB_Init failed\n");
  1283 	return -1;
  1286     certHandle = PORT_ZNew(NSSLOWCERTCertDBHandle);
  1287     if (!certHandle) {
  1288 	SECU_PrintError(progName, "unable to get database handle");
  1289 	return -1;
  1291     certHandle->ref = 1;
  1293 #ifdef NOTYET
  1294     /*  Open the possibly corrupt database.  */
  1295     if (dbck.options[opt_InputDB].activated) {
  1296 	PRFileInfo fileInfo;
  1297 	fullname = PR_smprintf("%s/%s", pathname, 
  1298 	                                dbck.options[opt_InputDB].arg);
  1299 	if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1300 	    fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
  1301 	    return -1;
  1303 	rv = CERT_OpenCertDBFilename(certHandle, fullname, PR_TRUE);
  1304     } else 
  1305 #endif
  1307 	/*  Use the default.  */
  1308 #ifdef NOTYET
  1309 	fullname = SECU_CertDBNameCallback(NULL, CERT_DB_FILE_VERSION);
  1310 	if (PR_GetFileInfo(fullname, &fileInfo) != PR_SUCCESS) {
  1311 	    fprintf(stderr, "Unable to read file \"%s\".\n", fullname);
  1312 	    return -1;
  1314 #endif
  1315 	rv = nsslowcert_OpenCertDB(certHandle, 
  1316 	                           PR_TRUE, 		    /* readOnly */
  1317 				   NULL,                    /* rdb appName */
  1318 				   "",                      /* rdb prefix */
  1319 	                           dbck_certdb_name_cb,     /* namecb */
  1320 				   pathname, 		    /* configDir */
  1321 				   PR_FALSE);		    /* volatile */
  1324     if (rv) {
  1325 	SECU_PrintError(progName, "unable to open cert database");
  1326 	return -1;
  1329     if (dbck.commands[cmd_Debug].activated) {
  1330 	DBCK_DebugDB(certHandle, dumpfile, mailfile);
  1331 	return 0;
  1334 #ifdef DORECOVER
  1335     if (dbck.commands[cmd_Recover].activated) {
  1336 	DBCK_ReconstructDBFromCerts(certHandle, newdbname,
  1337 	                            dumpfile, removeExpired, 
  1338 	                            requireProfile, singleEntry, 
  1339 	                            dbck.options[opt_Prompt].activated);
  1340 	return 0;
  1342 #endif
  1344     if (mailfile)
  1345 	PR_Close(mailfile);
  1346     if (dumpfile)
  1347 	PR_Close(dumpfile);
  1348     if (certHandle) {
  1349 	nsslowcert_ClosePermCertDB(certHandle);
  1350 	PORT_Free(certHandle);
  1352     return -1;

mercurial