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 +}