1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
45 typedef struct dbRestoreInfoStr
47 NSSLOWCERTCertDBHandle
*handle
;
58 IsEmailCert(CERTCertificate
*cert
)
60 char *email
, *tmp1
, *tmp2
;
64 if (!cert
->subjectName
) {
68 tmp1
= PORT_Strstr(cert
->subjectName
, "E=");
69 tmp2
= PORT_Strstr(cert
->subjectName
, "MAIL=");
70 /* XXX Nelson has cert for KTrilli which does not have either
71 * of above but is email cert (has cert->emailAddr).
73 if (!tmp1
&& !tmp2
&& !(cert
->emailAddr
&& cert
->emailAddr
[0])) {
77 /* Server or CA cert, not personal email. */
78 isCA
= CERT_IsCACert(cert
, NULL
);
82 /* XXX CERT_IsCACert advertises checking the key usage ext.,
83 but doesn't appear to. */
84 /* Check the key usage extension. */
85 if (cert
->keyUsagePresent
) {
86 /* Must at least be able to sign or encrypt (not neccesarily
87 * both if it is one of a dual cert).
89 if (!((cert
->rawKeyUsage
& KU_DIGITAL_SIGNATURE
) ||
90 (cert
->rawKeyUsage
& KU_KEY_ENCIPHERMENT
)))
93 /* CA cert, not personal email. */
94 if (cert
->rawKeyUsage
& (KU_KEY_CERT_SIGN
| KU_CRL_SIGN
))
98 if (cert
->emailAddr
&& cert
->emailAddr
[0]) {
99 email
= PORT_Strdup(cert
->emailAddr
);
102 tmp1
+= 2; /* "E=" */
104 tmp1
= tmp2
+ 5; /* "MAIL=" */
105 len
= strcspn(tmp1
, ", ");
106 email
= (char*)PORT_Alloc(len
+1);
107 PORT_Strncpy(email
, tmp1
, len
);
115 deleteit(CERTCertificate
*cert
, void *arg
)
117 return SEC_DeletePermCertificate(cert
);
120 /* Different than DeleteCertificate - has the added bonus of removing
121 * all certs with the same DN.
124 deleteAllEntriesForCert(NSSLOWCERTCertDBHandle
*handle
, CERTCertificate
*cert
,
128 certDBEntrySubject
*subjectEntry
;
129 certDBEntryNickname
*nicknameEntry
;
130 certDBEntrySMime
*smimeEntry
;
135 PR_fprintf(outfile
, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
136 PR_fprintf(outfile
, "Deleting redundant certificate:\n");
137 dumpCertificate(cert
, -1, outfile
);
140 CERT_TraverseCertsForSubject(handle
, cert
->subjectList
, deleteit
, NULL
);
143 subjectEntry
= ReadDBSubjectEntry(handle
, &cert
->derSubject
);
144 /* It had better be there, or created a bad db. */
145 PORT_Assert(subjectEntry
);
146 for (i
=0; i
<subjectEntry
->ncerts
; i
++) {
147 DeleteDBCertEntry(handle
, &subjectEntry
->certKeys
[i
]);
149 DeleteDBSubjectEntry(handle
, &cert
->derSubject
);
150 if (subjectEntry
->emailAddr
&& subjectEntry
->emailAddr
[0]) {
151 smimeEntry
= ReadDBSMimeEntry(handle
, subjectEntry
->emailAddr
);
153 if (SECITEM_ItemsAreEqual(&subjectEntry
->derSubject
,
154 &smimeEntry
->subjectName
))
155 /* Only delete it if it's for this subject! */
156 DeleteDBSMimeEntry(handle
, subjectEntry
->emailAddr
);
157 SEC_DestroyDBEntry((certDBEntry
*)smimeEntry
);
160 if (subjectEntry
->nickname
) {
161 nicknameEntry
= ReadDBNicknameEntry(handle
, subjectEntry
->nickname
);
163 if (SECITEM_ItemsAreEqual(&subjectEntry
->derSubject
,
164 &nicknameEntry
->subjectName
))
165 /* Only delete it if it's for this subject! */
166 DeleteDBNicknameEntry(handle
, subjectEntry
->nickname
);
167 SEC_DestroyDBEntry((certDBEntry
*)nicknameEntry
);
170 SEC_DestroyDBEntry((certDBEntry
*)subjectEntry
);
171 CERT_UnlockDB(handle
);
177 getCertsToDelete(char *numlist
, int len
, int *certNums
, int nCerts
)
180 char *numstr
, *numend
, *end
;
183 end
= numstr
+ len
- 1;
184 while (numstr
!= end
) {
185 numend
= strpbrk(numstr
, ", \n");
187 if (PORT_Strlen(numstr
) == 0)
189 num
= PORT_Atoi(numstr
);
190 if (numstr
== numlist
)
192 for (j
=1; j
<nCerts
+1; j
++) {
193 if (num
== certNums
[j
]) {
200 numstr
= strpbrk(numend
+1, "0123456789");
205 userSaysDeleteCert(CERTCertificate
**certs
, int nCerts
,
206 int errtype
, dbRestoreInfo
*info
, int *certNums
)
211 /* User wants to remove cert without prompting. */
212 if (info
->promptUser
[errtype
] == PR_FALSE
)
213 return (info
->removeType
[errtype
]);
216 PR_fprintf(PR_STDOUT
, "******** Expired ********\n");
217 PR_fprintf(PR_STDOUT
, "Cert has expired.\n\n");
218 dumpCertificate(certs
[0], -1, PR_STDOUT
);
219 PR_fprintf(PR_STDOUT
,
220 "Keep it? (y/n - this one, Y/N - all expired certs) [n] ");
222 case dbNoSMimeProfile
:
223 PR_fprintf(PR_STDOUT
, "******** No Profile ********\n");
224 PR_fprintf(PR_STDOUT
, "S/MIME cert has no profile.\n\n");
225 dumpCertificate(certs
[0], -1, PR_STDOUT
);
226 PR_fprintf(PR_STDOUT
,
227 "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] ");
230 PR_fprintf(PR_STDOUT
, "******* Redundant nickname/email *******\n\n");
231 PR_fprintf(PR_STDOUT
, "These certs have the same nickname/email:\n");
232 for (i
=0; i
<nCerts
; i
++)
233 dumpCertificate(certs
[i
], i
, PR_STDOUT
);
234 PR_fprintf(PR_STDOUT
,
235 "Enter the certs you would like to keep from those listed above.\n");
236 PR_fprintf(PR_STDOUT
,
237 "Use a comma-separated list of the cert numbers (ex. 0, 8, 12).\n");
238 PR_fprintf(PR_STDOUT
,
239 "The first cert in the list will be the primary cert\n");
240 PR_fprintf(PR_STDOUT
,
241 " accessed by the nickname/email handle.\n");
242 PR_fprintf(PR_STDOUT
,
243 "List cert numbers to keep here, or hit enter\n");
244 PR_fprintf(PR_STDOUT
,
245 " to always keep only the newest cert: ");
249 nb
= PR_Read(PR_STDIN
, response
, sizeof(response
));
250 PR_fprintf(PR_STDOUT
, "\n\n");
251 if (errtype
== dbOlderCert
) {
252 if (!isdigit(response
[0])) {
253 info
->promptUser
[errtype
] = PR_FALSE
;
254 info
->removeType
[errtype
] = PR_TRUE
;
257 getCertsToDelete(response
, nb
, certNums
, nCerts
);
260 /* User doesn't want to be prompted for this type anymore. */
261 if (response
[0] == 'Y') {
262 info
->promptUser
[errtype
] = PR_FALSE
;
263 info
->removeType
[errtype
] = PR_FALSE
;
265 } else if (response
[0] == 'N') {
266 info
->promptUser
[errtype
] = PR_FALSE
;
267 info
->removeType
[errtype
] = PR_TRUE
;
270 return (response
[0] != 'y') ? PR_TRUE
: PR_FALSE
;
274 addCertToDB(certDBEntryCert
*certEntry
, dbRestoreInfo
*info
,
275 NSSLOWCERTCertDBHandle
*oldhandle
)
277 SECStatus rv
= SECSuccess
;
278 PRBool allowOverride
;
280 SECCertTimeValidity validity
;
281 CERTCertificate
*oldCert
= NULL
;
282 CERTCertificate
*dbCert
= NULL
;
283 CERTCertificate
*newCert
= NULL
;
284 CERTCertTrust
*trust
;
285 certDBEntrySMime
*smimeEntry
= NULL
;
287 char *nickname
= NULL
;
288 int nCertsForSubject
= 1;
290 oldCert
= CERT_DecodeDERCertificate(&certEntry
->derCert
, PR_FALSE
,
291 certEntry
->nickname
);
293 info
->dbErrors
[dbBadCertificate
]++;
294 SEC_DestroyDBEntry((certDBEntry
*)certEntry
);
298 oldCert
->dbEntry
= certEntry
;
299 oldCert
->trust
= &certEntry
->trust
;
300 oldCert
->dbhandle
= oldhandle
;
302 trust
= oldCert
->trust
;
307 PR_fprintf(info
->out
, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n");
309 if (oldCert
->nickname
)
310 nickname
= PORT_Strdup(oldCert
->nickname
);
312 /* Always keep user certs. Skip ahead. */
313 /* XXX if someone sends themselves a signed message, it is possible
314 for their cert to be imported as an "other" cert, not a user cert.
315 this mucks with smime entries... */
316 userCert
= (SEC_GET_TRUST_FLAGS(trust
, trustSSL
) & CERTDB_USER
) ||
317 (SEC_GET_TRUST_FLAGS(trust
, trustEmail
) & CERTDB_USER
) ||
318 (SEC_GET_TRUST_FLAGS(trust
, trustObjectSigning
) & CERTDB_USER
);
322 /* If user chooses so, ignore expired certificates. */
323 allowOverride
= (PRBool
)((oldCert
->keyUsage
== certUsageSSLServer
) ||
324 (oldCert
->keyUsage
== certUsageSSLServerWithStepUp
));
325 validity
= CERT_CheckCertValidTimes(oldCert
, PR_Now(), allowOverride
);
326 /* If cert expired and user wants to delete it, ignore it. */
327 if ((validity
!= secCertTimeValid
) &&
328 userSaysDeleteCert(&oldCert
, 1, dbInvalidCert
, info
, 0)) {
329 info
->dbErrors
[dbInvalidCert
]++;
331 PR_fprintf(info
->out
, "Deleting expired certificate:\n");
332 dumpCertificate(oldCert
, -1, info
->out
);
337 /* New database will already have default certs, don't attempt
338 to overwrite them. */
339 dbCert
= CERT_FindCertByDERCert(info
->handle
, &oldCert
->derCert
);
343 PR_fprintf(info
->out
, "Added certificate to database:\n");
344 dumpCertificate(oldCert
, -1, info
->out
);
349 /* Determine if cert is S/MIME and get its email if so. */
350 email
= IsEmailCert(oldCert
);
353 XXX Just create empty profiles?
355 SECItem *profile = CERT_FindSMimeProfile(oldCert);
357 userSaysDeleteCert(&oldCert, 1, dbNoSMimeProfile, info, 0)) {
358 info->dbErrors[dbNoSMimeProfile]++;
360 PR_fprintf(info->out,
361 "Deleted cert missing S/MIME profile.\n");
362 dumpCertificate(oldCert, -1, info->out);
366 SECITEM_FreeItem(profile);
373 /* Sometimes happens... */
374 if (!nickname
&& userCert
)
375 nickname
= PORT_Strdup(oldCert
->subjectName
);
377 /* Create a new certificate, copy of the old one. */
378 newCert
= CERT_NewTempCertificate(info
->handle
, &oldCert
->derCert
,
379 nickname
, PR_FALSE
, PR_TRUE
);
381 PR_fprintf(PR_STDERR
, "Unable to create new certificate.\n");
382 dumpCertificate(oldCert
, -1, PR_STDERR
);
383 info
->dbErrors
[dbBadCertificate
]++;
387 /* Add the cert to the new database. */
388 rv
= CERT_AddTempCertToPerm(newCert
, nickname
, oldCert
->trust
);
390 PR_fprintf(PR_STDERR
, "Failed to write temp cert to perm database.\n");
391 dumpCertificate(oldCert
, -1, PR_STDERR
);
392 info
->dbErrors
[dbCertNotWrittenToDB
]++;
397 PR_fprintf(info
->out
, "Added certificate to database:\n");
398 dumpCertificate(oldCert
, -1, info
->out
);
401 /* If the cert is an S/MIME cert, and the first with it's subject,
402 * modify the subject entry to include the email address,
403 * CERT_AddTempCertToPerm does not do email addresses and S/MIME entries.
405 if (smimeEntry
) { /*&& !userCert && nCertsForSubject == 1) { */
407 UpdateSubjectWithEmailAddr(newCert
, email
);
409 SECItem emailProfile
, profileTime
;
410 rv
= CERT_FindFullSMimeProfile(oldCert
, &emailProfile
, &profileTime
);
411 /* calls UpdateSubjectWithEmailAddr */
412 if (rv
== SECSuccess
)
413 rv
= CERT_SaveSMimeProfile(newCert
, &emailProfile
, &profileTime
);
425 CERT_DestroyCertificate(oldCert
);
427 CERT_DestroyCertificate(dbCert
);
429 CERT_DestroyCertificate(newCert
);
431 SEC_DestroyDBEntry((certDBEntry
*)smimeEntry
);
437 copyDBEntry(SECItem
*data
, SECItem
*key
, certDBEntryType type
, void *pdata
)
440 NSSLOWCERTCertDBHandle
*newdb
= (NSSLOWCERTCertDBHandle
*)pdata
;
441 certDBEntryCommon common
;
445 common
.version
= CERT_DB_FILE_VERSION
;
446 common
.flags
= data
->data
[2];
449 dbkey
.len
= key
->len
+ SEC_DB_KEY_HEADER_LEN
;
450 dbkey
.data
= (unsigned char *)PORT_Alloc(dbkey
.len
*sizeof(unsigned char));
451 PORT_Memcpy(&dbkey
.data
[SEC_DB_KEY_HEADER_LEN
], key
->data
, key
->len
);
452 dbkey
.data
[0] = type
;
454 rv
= WriteDBEntry(newdb
, &common
, &dbkey
, data
);
456 PORT_Free(dbkey
.data
);
462 certIsOlder(CERTCertificate
**cert1
, CERTCertificate
** cert2
)
464 return !CERT_IsNewer(*cert1
, *cert2
);
468 findNewestSubjectForEmail(NSSLOWCERTCertDBHandle
*handle
, int subjectNum
,
469 certDBArray
*dbArray
, dbRestoreInfo
*info
,
470 int *subjectWithSMime
, int *smimeForSubject
)
473 int subjectsForEmail
[50];
475 certDBEntryListNode
*subjects
= &dbArray
->subjects
;
476 certDBEntryListNode
*smime
= &dbArray
->smime
;
477 certDBEntrySubject
*subjectEntry1
, *subjectEntry2
;
478 certDBEntrySMime
*smimeEntry
;
479 CERTCertificate
**certs
;
480 CERTCertificate
*cert
;
481 CERTCertTrust
*trust
;
486 subjectEntry1
= (certDBEntrySubject
*)&subjects
.entries
[subjectNum
];
487 subjectsForEmail
[ns
++] = subjectNum
;
489 *subjectWithSMime
= -1;
490 *smimeForSubject
= -1;
491 newestSubject
= subjectNum
;
493 cert
= CERT_FindCertByKey(handle
, &subjectEntry1
->certKeys
[0]);
496 userCert
= (SEC_GET_TRUST_FLAGS(trust
, trustSSL
) & CERTDB_USER
) ||
497 (SEC_GET_TRUST_FLAGS(trust
, trustEmail
) & CERTDB_USER
) ||
498 (SEC_GET_TRUST_FLAGS(trust
, trustObjectSigning
) & CERTDB_USER
);
499 CERT_DestroyCertificate(cert
);
503 * XXX Should we make sure that subjectEntry1->emailAddr is not
504 * a null pointer or an empty string before going into the next
505 * two for loops, which pass it to PORT_Strcmp?
508 /* Loop over the remaining subjects. */
509 for (i
=subjectNum
+1; i
<subjects
.numEntries
; i
++) {
510 subjectEntry2
= (certDBEntrySubject
*)&subjects
.entries
[i
];
513 if (subjectEntry2
->emailAddr
&& subjectEntry2
->emailAddr
[0] &&
514 PORT_Strcmp(subjectEntry1
->emailAddr
,
515 subjectEntry2
->emailAddr
) == 0) {
516 /* Found a subject using the same email address. */
517 subjectsForEmail
[ns
++] = i
;
521 /* Find the S/MIME entry for this email address. */
522 for (i
=0; i
<smime
.numEntries
; i
++) {
523 smimeEntry
= (certDBEntrySMime
*)&smime
.entries
[i
];
524 if (smimeEntry
->common
.arena
== NULL
)
526 if (smimeEntry
->emailAddr
&& smimeEntry
->emailAddr
[0] &&
527 PORT_Strcmp(subjectEntry1
->emailAddr
, smimeEntry
->emailAddr
) == 0) {
528 /* Find which of the subjects uses this S/MIME entry. */
529 for (j
=0; j
<ns
&& *subjectWithSMime
< 0; j
++) {
530 sNum
= subjectsForEmail
[j
];
531 subjectEntry2
= (certDBEntrySubject
*)&subjects
.entries
[sNum
];
532 if (SECITEM_ItemsAreEqual(&smimeEntry
->subjectName
,
533 &subjectEntry2
->derSubject
)) {
534 /* Found the subject corresponding to the S/MIME entry. */
535 *subjectWithSMime
= sNum
;
536 *smimeForSubject
= i
;
539 SEC_DestroyDBEntry((certDBEntry
*)smimeEntry
);
540 PORT_Memset(smimeEntry
, 0, sizeof(certDBEntry
));
549 return *subjectWithSMime
;
551 /* Now find which of the subjects has the newest cert. */
552 certs
= (CERTCertificate
**)PORT_Alloc(ns
*sizeof(CERTCertificate
*));
553 certNums
= (int*)PORT_Alloc((ns
+1)*sizeof(int));
555 for (i
=0; i
<ns
; i
++) {
556 sNum
= subjectsForEmail
[i
];
557 subjectEntry1
= (certDBEntrySubject
*)&subjects
.entries
[sNum
];
558 certs
[i
] = CERT_FindCertByKey(handle
, &subjectEntry1
->certKeys
[0]);
561 /* Sort the array by validity. */
562 qsort(certs
, ns
, sizeof(CERTCertificate
*),
563 (int (*)(const void *, const void *))certIsOlder
);
565 for (i
=0; i
<ns
; i
++) {
566 sNum
= subjectsForEmail
[i
];
567 subjectEntry1
= (certDBEntrySubject
*)&subjects
.entries
[sNum
];
568 if (SECITEM_ItemsAreEqual(&subjectEntry1
->derSubject
,
569 &certs
[0]->derSubject
))
570 newestSubject
= sNum
;
572 SEC_DestroyDBEntry((certDBEntry
*)subjectEntry1
);
574 if (info
&& userSaysDeleteCert(certs
, ns
, dbOlderCert
, info
, certNums
)) {
575 for (i
=1; i
<ns
+1; i
++) {
576 if (certNums
[i
] >= 0 && certNums
[i
] != certNums
[0]) {
577 deleteAllEntriesForCert(handle
, certs
[certNums
[i
]], info
->out
);
578 info
->dbErrors
[dbOlderCert
]++;
582 CERT_DestroyCertArray(certs
, ns
);
583 return newestSubject
;
586 NSSLOWCERTCertDBHandle
*
587 DBCK_ReconstructDBFromCerts(NSSLOWCERTCertDBHandle
*oldhandle
, char *newdbname
,
588 PRFileDesc
*outfile
, PRBool removeExpired
,
589 PRBool requireProfile
, PRBool singleEntry
,
594 certDBEntryContentVersion
*oldContentVersion
;
598 PORT_Memset(&dbArray
, 0, sizeof(dbArray
));
599 PORT_Memset(&info
, 0, sizeof(info
));
600 info
.verbose
= (outfile
) ? PR_TRUE
: PR_FALSE
;
601 info
.out
= (outfile
) ? outfile
: PR_STDOUT
;
602 info
.removeType
[dbInvalidCert
] = removeExpired
;
603 info
.removeType
[dbNoSMimeProfile
] = requireProfile
;
604 info
.removeType
[dbOlderCert
] = singleEntry
;
605 info
.promptUser
[dbInvalidCert
] = promptUser
;
606 info
.promptUser
[dbNoSMimeProfile
] = promptUser
;
607 info
.promptUser
[dbOlderCert
] = promptUser
;
609 /* Allocate a handle to fill with CERT_OpenCertDB below. */
610 info
.handle
= PORT_ZNew(NSSLOWCERTCertDBHandle
);
612 fprintf(stderr
, "unable to get database handle");
616 /* Create a certdb with the most recent set of roots. */
617 rv
= CERT_OpenCertDBFilename(info
.handle
, newdbname
, PR_FALSE
);
620 fprintf(stderr
, "could not open certificate database");
624 /* Create certificate, subject, nickname, and email records.
625 * mcom_db seems to have a sequential access bug. Though reads and writes
626 * should be allowed during traversal, they seem to screw up the sequence.
627 * So, stuff all the cert entries into an array, and loop over the array
628 * doing read/writes in the db.
630 fillDBEntryArray(oldhandle
, certDBEntryTypeCert
, &dbArray
.certs
);
631 for (elem
= PR_LIST_HEAD(&dbArray
->certs
.link
);
632 elem
!= &dbArray
->certs
.link
; elem
= PR_NEXT_LINK(elem
)) {
633 node
= LISTNODE_CAST(elem
);
634 addCertToDB((certDBEntryCert
*)&node
->entry
, &info
, oldhandle
);
635 /* entries get destroyed in addCertToDB */
638 rv
= nsslowcert_TraverseDBEntries(oldhandle
, certDBEntryTypeSMimeProfile
,
639 copyDBEntry
, info
.handle
);
642 /* Fix up the pointers between (nickname|S/MIME) --> (subject).
643 * Create S/MIME entries for S/MIME certs.
644 * Have the S/MIME entry point to the last-expiring cert using
648 CERT_RedoHandlesForSubjects(info
.handle
, singleEntry
, &info
);
651 freeDBEntryList(&dbArray
.certs
.link
);
653 /* Copy over the version record. */
654 /* XXX Already exists - and _must_ be correct... */
656 versionEntry = ReadDBVersionEntry(oldhandle);
657 rv = WriteDBVersionEntry(info.handle, versionEntry);
660 /* Copy over the content version record. */
661 /* XXX Can probably get useful info from old content version?
662 * Was this db created before/after this tool? etc.
665 oldContentVersion
= ReadDBContentVersionEntry(oldhandle
);
666 CERT_SetDBContentVersion(oldContentVersion
->contentVersion
, info
.handle
);
670 /* Copy over the CRL & KRL records. */
671 rv
= nsslowcert_TraverseDBEntries(oldhandle
, certDBEntryTypeRevocation
,
672 copyDBEntry
, info
.handle
);
673 /* XXX Only one KRL, just do db->get? */
674 rv
= nsslowcert_TraverseDBEntries(oldhandle
, certDBEntryTypeKeyRevocation
,
675 copyDBEntry
, info
.handle
);
678 PR_fprintf(info
.out
, "Database had %d certificates.\n", info
.nOldCerts
);
680 PR_fprintf(info
.out
, "Reconstructed %d certificates.\n", info
.nCerts
);
681 PR_fprintf(info
.out
, "(ax) Rejected %d expired certificates.\n",
682 info
.dbErrors
[dbInvalidCert
]);
683 PR_fprintf(info
.out
, "(as) Rejected %d S/MIME certificates missing a profile.\n",
684 info
.dbErrors
[dbNoSMimeProfile
]);
685 PR_fprintf(info
.out
, "(ar) Rejected %d certificates for which a newer certificate was found.\n",
686 info
.dbErrors
[dbOlderCert
]);
687 PR_fprintf(info
.out
, " Rejected %d corrupt certificates.\n",
688 info
.dbErrors
[dbBadCertificate
]);
689 PR_fprintf(info
.out
, " Rejected %d certificates which did not write to the DB.\n",
690 info
.dbErrors
[dbCertNotWrittenToDB
]);
699 PORT_Free(info
.handle
);