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