Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
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 | } |