security/nss/cmd/dbck/dbck.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial