Back to index

lightning-sunbird  0.9+nobinonly
dbrecover.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 enum {
00038     dbInvalidCert = 0,
00039     dbNoSMimeProfile,
00040     dbOlderCert,
00041     dbBadCertificate,
00042     dbCertNotWrittenToDB
00043 };
00044 
00045 typedef struct dbRestoreInfoStr
00046 {
00047     NSSLOWCERTCertDBHandle *handle;
00048     PRBool verbose;
00049     PRFileDesc *out;
00050     int nCerts;
00051     int nOldCerts;
00052     int dbErrors[5];
00053     PRBool removeType[3];
00054     PRBool promptUser[3];
00055 } dbRestoreInfo;
00056 
00057 char *
00058 IsEmailCert(CERTCertificate *cert)
00059 {
00060     char *email, *tmp1, *tmp2;
00061     PRBool isCA;
00062     int len;
00063 
00064     if (!cert->subjectName) {
00065        return NULL;
00066     }
00067 
00068     tmp1 = PORT_Strstr(cert->subjectName, "E=");
00069     tmp2 = PORT_Strstr(cert->subjectName, "MAIL=");
00070     /* XXX Nelson has cert for KTrilli which does not have either
00071      * of above but is email cert (has cert->emailAddr). 
00072      */
00073     if (!tmp1 && !tmp2 && !(cert->emailAddr && cert->emailAddr[0])) {
00074        return NULL;
00075     }
00076 
00077     /*  Server or CA cert, not personal email.  */
00078     isCA = CERT_IsCACert(cert, NULL);
00079     if (isCA)
00080        return NULL;
00081 
00082     /*  XXX CERT_IsCACert advertises checking the key usage ext.,
00083        but doesn't appear to. */
00084     /*  Check the key usage extension.  */
00085     if (cert->keyUsagePresent) {
00086        /*  Must at least be able to sign or encrypt (not neccesarily
00087         *  both if it is one of a dual cert).  
00088         */
00089        if (!((cert->rawKeyUsage & KU_DIGITAL_SIGNATURE) || 
00090               (cert->rawKeyUsage & KU_KEY_ENCIPHERMENT)))
00091            return NULL;
00092 
00093        /*  CA cert, not personal email.  */
00094        if (cert->rawKeyUsage & (KU_KEY_CERT_SIGN | KU_CRL_SIGN))
00095            return NULL;
00096     }
00097 
00098     if (cert->emailAddr && cert->emailAddr[0]) {
00099        email = PORT_Strdup(cert->emailAddr);
00100     } else {
00101        if (tmp1)
00102            tmp1 += 2; /* "E="  */
00103        else
00104            tmp1 = tmp2 + 5; /* "MAIL=" */
00105        len = strcspn(tmp1, ", ");
00106        email = (char*)PORT_Alloc(len+1);
00107        PORT_Strncpy(email, tmp1, len);
00108        email[len] = '\0';
00109     }
00110 
00111     return email;
00112 }
00113 
00114 SECStatus
00115 deleteit(CERTCertificate *cert, void *arg)
00116 {
00117     return SEC_DeletePermCertificate(cert);
00118 }
00119 
00120 /*  Different than DeleteCertificate - has the added bonus of removing
00121  *  all certs with the same DN.  
00122  */
00123 SECStatus
00124 deleteAllEntriesForCert(NSSLOWCERTCertDBHandle *handle, CERTCertificate *cert,
00125                         PRFileDesc *outfile)
00126 {
00127 #if 0
00128     certDBEntrySubject *subjectEntry;
00129     certDBEntryNickname *nicknameEntry;
00130     certDBEntrySMime *smimeEntry;
00131     int i;
00132 #endif
00133 
00134     if (outfile) {
00135        PR_fprintf(outfile, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
00136        PR_fprintf(outfile, "Deleting redundant certificate:\n");
00137        dumpCertificate(cert, -1, outfile);
00138     }
00139 
00140     CERT_TraverseCertsForSubject(handle, cert->subjectList, deleteit, NULL);
00141 #if 0
00142     CERT_LockDB(handle);
00143     subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
00144     /*  It had better be there, or created a bad db.  */
00145     PORT_Assert(subjectEntry);
00146     for (i=0; i<subjectEntry->ncerts; i++) {
00147        DeleteDBCertEntry(handle, &subjectEntry->certKeys[i]);
00148     }
00149     DeleteDBSubjectEntry(handle, &cert->derSubject);
00150     if (subjectEntry->emailAddr && subjectEntry->emailAddr[0]) {
00151        smimeEntry = ReadDBSMimeEntry(handle, subjectEntry->emailAddr);
00152        if (smimeEntry) {
00153            if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
00154                                      &smimeEntry->subjectName))
00155               /*  Only delete it if it's for this subject!  */
00156               DeleteDBSMimeEntry(handle, subjectEntry->emailAddr);
00157            SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
00158        }
00159     }
00160     if (subjectEntry->nickname) {
00161        nicknameEntry = ReadDBNicknameEntry(handle, subjectEntry->nickname);
00162        if (nicknameEntry) {
00163            if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
00164                                      &nicknameEntry->subjectName))
00165               /*  Only delete it if it's for this subject!  */
00166               DeleteDBNicknameEntry(handle, subjectEntry->nickname);
00167            SEC_DestroyDBEntry((certDBEntry*)nicknameEntry);
00168        }
00169     }
00170     SEC_DestroyDBEntry((certDBEntry*)subjectEntry);
00171     CERT_UnlockDB(handle);
00172 #endif
00173     return SECSuccess;
00174 }
00175 
00176 void
00177 getCertsToDelete(char *numlist, int len, int *certNums, int nCerts)
00178 {
00179     int j, num;
00180     char *numstr, *numend, *end;
00181 
00182     numstr = numlist;
00183     end = numstr + len - 1;
00184     while (numstr != end) {
00185        numend = strpbrk(numstr, ", \n");
00186        *numend = '\0';
00187        if (PORT_Strlen(numstr) == 0)
00188            return;
00189        num = PORT_Atoi(numstr);
00190        if (numstr == numlist)
00191            certNums[0] = num;
00192        for (j=1; j<nCerts+1; j++) {
00193            if (num == certNums[j]) {
00194               certNums[j] = -1;
00195               break;
00196            }
00197        }
00198        if (numend == end)
00199            break;
00200        numstr = strpbrk(numend+1, "0123456789");
00201     }
00202 }
00203 
00204 PRBool
00205 userSaysDeleteCert(CERTCertificate **certs, int nCerts,
00206                    int errtype, dbRestoreInfo *info, int *certNums)
00207 {
00208     char response[32];
00209     int32 nb;
00210     int i;
00211     /*  User wants to remove cert without prompting.  */
00212     if (info->promptUser[errtype] == PR_FALSE)
00213        return (info->removeType[errtype]);
00214     switch (errtype) {
00215     case dbInvalidCert:
00216        PR_fprintf(PR_STDOUT, "********  Expired ********\n");
00217        PR_fprintf(PR_STDOUT, "Cert has expired.\n\n");
00218        dumpCertificate(certs[0], -1, PR_STDOUT);
00219        PR_fprintf(PR_STDOUT,
00220                   "Keep it? (y/n - this one, Y/N - all expired certs) [n] ");
00221        break;
00222     case dbNoSMimeProfile:
00223        PR_fprintf(PR_STDOUT, "********  No Profile ********\n");
00224        PR_fprintf(PR_STDOUT, "S/MIME cert has no profile.\n\n");
00225        dumpCertificate(certs[0], -1, PR_STDOUT);
00226        PR_fprintf(PR_STDOUT,
00227              "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] ");
00228        break;
00229     case dbOlderCert:
00230        PR_fprintf(PR_STDOUT, "*******  Redundant nickname/email *******\n\n");
00231        PR_fprintf(PR_STDOUT, "These certs have the same nickname/email:\n");
00232        for (i=0; i<nCerts; i++)
00233            dumpCertificate(certs[i], i, PR_STDOUT);
00234        PR_fprintf(PR_STDOUT, 
00235        "Enter the certs you would like to keep from those listed above.\n");
00236        PR_fprintf(PR_STDOUT, 
00237        "Use a comma-separated list of the cert numbers (ex. 0, 8, 12).\n");
00238        PR_fprintf(PR_STDOUT, 
00239        "The first cert in the list will be the primary cert\n");
00240        PR_fprintf(PR_STDOUT, 
00241        " accessed by the nickname/email handle.\n");
00242        PR_fprintf(PR_STDOUT, 
00243        "List cert numbers to keep here, or hit enter\n");
00244        PR_fprintf(PR_STDOUT, 
00245        " to always keep only the newest cert:  ");
00246        break;
00247     default:
00248     }
00249     nb = PR_Read(PR_STDIN, response, sizeof(response));
00250     PR_fprintf(PR_STDOUT, "\n\n");
00251     if (errtype == dbOlderCert) {
00252        if (!isdigit(response[0])) {
00253            info->promptUser[errtype] = PR_FALSE;
00254            info->removeType[errtype] = PR_TRUE;
00255            return PR_TRUE;
00256        }
00257        getCertsToDelete(response, nb, certNums, nCerts);
00258        return PR_TRUE;
00259     }
00260     /*  User doesn't want to be prompted for this type anymore.  */
00261     if (response[0] == 'Y') {
00262        info->promptUser[errtype] = PR_FALSE;
00263        info->removeType[errtype] = PR_FALSE;
00264        return PR_FALSE;
00265     } else if (response[0] == 'N') {
00266        info->promptUser[errtype] = PR_FALSE;
00267        info->removeType[errtype] = PR_TRUE;
00268        return PR_TRUE;
00269     }
00270     return (response[0] != 'y') ? PR_TRUE : PR_FALSE;
00271 }
00272 
00273 SECStatus
00274 addCertToDB(certDBEntryCert *certEntry, dbRestoreInfo *info, 
00275             NSSLOWCERTCertDBHandle *oldhandle)
00276 {
00277     SECStatus rv = SECSuccess;
00278     PRBool allowOverride;
00279     PRBool userCert;
00280     SECCertTimeValidity validity;
00281     CERTCertificate *oldCert = NULL;
00282     CERTCertificate *dbCert = NULL;
00283     CERTCertificate *newCert = NULL;
00284     CERTCertTrust *trust;
00285     certDBEntrySMime *smimeEntry = NULL;
00286     char *email = NULL;
00287     char *nickname = NULL;
00288     int nCertsForSubject = 1;
00289 
00290     oldCert = CERT_DecodeDERCertificate(&certEntry->derCert, PR_FALSE,
00291                                         certEntry->nickname);
00292     if (!oldCert) {
00293        info->dbErrors[dbBadCertificate]++;
00294        SEC_DestroyDBEntry((certDBEntry*)certEntry);
00295        return SECSuccess;
00296     }
00297 
00298     oldCert->dbEntry = certEntry;
00299     oldCert->trust = &certEntry->trust;
00300     oldCert->dbhandle = oldhandle;
00301 
00302     trust = oldCert->trust;
00303 
00304     info->nOldCerts++;
00305 
00306     if (info->verbose)
00307        PR_fprintf(info->out, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n");
00308 
00309     if (oldCert->nickname)
00310        nickname = PORT_Strdup(oldCert->nickname);
00311 
00312     /*  Always keep user certs.  Skip ahead.  */
00313     /*  XXX if someone sends themselves a signed message, it is possible
00314        for their cert to be imported as an "other" cert, not a user cert.
00315        this mucks with smime entries...  */
00316     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
00317                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
00318                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
00319     if (userCert)
00320        goto createcert;
00321 
00322     /*  If user chooses so, ignore expired certificates.  */
00323     allowOverride = (PRBool)((oldCert->keyUsage == certUsageSSLServer) ||
00324                          (oldCert->keyUsage == certUsageSSLServerWithStepUp));
00325     validity = CERT_CheckCertValidTimes(oldCert, PR_Now(), allowOverride);
00326     /*  If cert expired and user wants to delete it, ignore it. */
00327     if ((validity != secCertTimeValid) && 
00328         userSaysDeleteCert(&oldCert, 1, dbInvalidCert, info, 0)) {
00329        info->dbErrors[dbInvalidCert]++;
00330        if (info->verbose) {
00331            PR_fprintf(info->out, "Deleting expired certificate:\n");
00332            dumpCertificate(oldCert, -1, info->out);
00333        }
00334        goto cleanup;
00335     }
00336 
00337     /*  New database will already have default certs, don't attempt
00338        to overwrite them.  */
00339     dbCert = CERT_FindCertByDERCert(info->handle, &oldCert->derCert);
00340     if (dbCert) {
00341        info->nCerts++;
00342        if (info->verbose) {
00343            PR_fprintf(info->out, "Added certificate to database:\n");
00344            dumpCertificate(oldCert, -1, info->out);
00345        }
00346        goto cleanup;
00347     }
00348     
00349     /*  Determine if cert is S/MIME and get its email if so.  */
00350     email = IsEmailCert(oldCert);
00351 
00352     /*
00353        XXX  Just create empty profiles?
00354     if (email) {
00355        SECItem *profile = CERT_FindSMimeProfile(oldCert);
00356        if (!profile &&
00357            userSaysDeleteCert(&oldCert, 1, dbNoSMimeProfile, info, 0)) {
00358            info->dbErrors[dbNoSMimeProfile]++;
00359            if (info->verbose) {
00360               PR_fprintf(info->out, 
00361                          "Deleted cert missing S/MIME profile.\n");
00362               dumpCertificate(oldCert, -1, info->out);
00363            }
00364            goto cleanup;
00365        } else {
00366            SECITEM_FreeItem(profile);
00367        }
00368     }
00369     */
00370 
00371 createcert:
00372 
00373     /*  Sometimes happens... */
00374     if (!nickname && userCert)
00375        nickname = PORT_Strdup(oldCert->subjectName);
00376 
00377     /*  Create a new certificate, copy of the old one.  */
00378     newCert = CERT_NewTempCertificate(info->handle, &oldCert->derCert, 
00379                                       nickname, PR_FALSE, PR_TRUE);
00380     if (!newCert) {
00381        PR_fprintf(PR_STDERR, "Unable to create new certificate.\n");
00382        dumpCertificate(oldCert, -1, PR_STDERR);
00383        info->dbErrors[dbBadCertificate]++;
00384        goto cleanup;
00385     }
00386 
00387     /*  Add the cert to the new database.  */
00388     rv = CERT_AddTempCertToPerm(newCert, nickname, oldCert->trust);
00389     if (rv) {
00390        PR_fprintf(PR_STDERR, "Failed to write temp cert to perm database.\n");
00391        dumpCertificate(oldCert, -1, PR_STDERR);
00392        info->dbErrors[dbCertNotWrittenToDB]++;
00393        goto cleanup;
00394     }
00395 
00396     if (info->verbose) {
00397        PR_fprintf(info->out, "Added certificate to database:\n");
00398        dumpCertificate(oldCert, -1, info->out);
00399     }
00400 
00401     /*  If the cert is an S/MIME cert, and the first with it's subject,
00402      *  modify the subject entry to include the email address,
00403      *  CERT_AddTempCertToPerm does not do email addresses and S/MIME entries.
00404      */
00405     if (smimeEntry) { /*&& !userCert && nCertsForSubject == 1) { */
00406 #if 0
00407        UpdateSubjectWithEmailAddr(newCert, email);
00408 #endif
00409        SECItem emailProfile, profileTime;
00410        rv = CERT_FindFullSMimeProfile(oldCert, &emailProfile, &profileTime);
00411        /*  calls UpdateSubjectWithEmailAddr  */
00412        if (rv == SECSuccess)
00413            rv = CERT_SaveSMimeProfile(newCert, &emailProfile, &profileTime);
00414     }
00415 
00416     info->nCerts++;
00417 
00418 cleanup:
00419 
00420     if (nickname)
00421        PORT_Free(nickname);
00422     if (email)
00423        PORT_Free(email);
00424     if (oldCert)
00425        CERT_DestroyCertificate(oldCert);
00426     if (dbCert)
00427        CERT_DestroyCertificate(dbCert);
00428     if (newCert)
00429        CERT_DestroyCertificate(newCert);
00430     if (smimeEntry)
00431        SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
00432     return SECSuccess;
00433 }
00434 
00435 #if 0
00436 SECStatus
00437 copyDBEntry(SECItem *data, SECItem *key, certDBEntryType type, void *pdata)
00438 {
00439     SECStatus rv;
00440     NSSLOWCERTCertDBHandle *newdb = (NSSLOWCERTCertDBHandle *)pdata;
00441     certDBEntryCommon common;
00442     SECItem dbkey;
00443 
00444     common.type = type;
00445     common.version = CERT_DB_FILE_VERSION;
00446     common.flags = data->data[2];
00447     common.arena = NULL;
00448 
00449     dbkey.len = key->len + SEC_DB_KEY_HEADER_LEN;
00450     dbkey.data = (unsigned char *)PORT_Alloc(dbkey.len*sizeof(unsigned char));
00451     PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], key->data, key->len);
00452     dbkey.data[0] = type;
00453 
00454     rv = WriteDBEntry(newdb, &common, &dbkey, data);
00455 
00456     PORT_Free(dbkey.data);
00457     return rv;
00458 }
00459 #endif
00460 
00461 int
00462 certIsOlder(CERTCertificate **cert1, CERTCertificate** cert2)
00463 {
00464     return !CERT_IsNewer(*cert1, *cert2);
00465 }
00466 
00467 int
00468 findNewestSubjectForEmail(NSSLOWCERTCertDBHandle *handle, int subjectNum,
00469                           certDBArray *dbArray, dbRestoreInfo *info,
00470                           int *subjectWithSMime, int *smimeForSubject)
00471 {
00472     int newestSubject;
00473     int subjectsForEmail[50];
00474     int i, j, ns, sNum;
00475     certDBEntryListNode *subjects = &dbArray->subjects;
00476     certDBEntryListNode *smime = &dbArray->smime;
00477     certDBEntrySubject *subjectEntry1, *subjectEntry2;
00478     certDBEntrySMime *smimeEntry;
00479     CERTCertificate **certs;
00480     CERTCertificate *cert;
00481     CERTCertTrust *trust;
00482     PRBool userCert;
00483     int *certNums;
00484 
00485     ns = 0;
00486     subjectEntry1 = (certDBEntrySubject*)&subjects.entries[subjectNum];
00487     subjectsForEmail[ns++] = subjectNum;
00488 
00489     *subjectWithSMime = -1;
00490     *smimeForSubject = -1;
00491     newestSubject = subjectNum;
00492 
00493     cert = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
00494     if (cert) {
00495        trust = cert->trust;
00496        userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
00497                  (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
00498                 (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
00499        CERT_DestroyCertificate(cert);
00500     }
00501 
00502     /*
00503      * XXX Should we make sure that subjectEntry1->emailAddr is not
00504      * a null pointer or an empty string before going into the next
00505      * two for loops, which pass it to PORT_Strcmp?
00506      */
00507 
00508     /*  Loop over the remaining subjects.  */
00509     for (i=subjectNum+1; i<subjects.numEntries; i++) {
00510        subjectEntry2 = (certDBEntrySubject*)&subjects.entries[i];
00511        if (!subjectEntry2)
00512            continue;
00513        if (subjectEntry2->emailAddr && subjectEntry2->emailAddr[0] &&
00514             PORT_Strcmp(subjectEntry1->emailAddr, 
00515                         subjectEntry2->emailAddr) == 0) {
00516            /*  Found a subject using the same email address.  */
00517            subjectsForEmail[ns++] = i;
00518        }
00519     }
00520 
00521     /*  Find the S/MIME entry for this email address.  */
00522     for (i=0; i<smime.numEntries; i++) {
00523        smimeEntry = (certDBEntrySMime*)&smime.entries[i];
00524        if (smimeEntry->common.arena == NULL)
00525            continue;
00526        if (smimeEntry->emailAddr && smimeEntry->emailAddr[0] && 
00527            PORT_Strcmp(subjectEntry1->emailAddr, smimeEntry->emailAddr) == 0) {
00528            /*  Find which of the subjects uses this S/MIME entry.  */
00529            for (j=0; j<ns && *subjectWithSMime < 0; j++) {
00530               sNum = subjectsForEmail[j];
00531               subjectEntry2 = (certDBEntrySubject*)&subjects.entries[sNum];
00532               if (SECITEM_ItemsAreEqual(&smimeEntry->subjectName,
00533                                         &subjectEntry2->derSubject)) {
00534                   /*  Found the subject corresponding to the S/MIME entry. */
00535                   *subjectWithSMime = sNum;
00536                   *smimeForSubject = i;
00537               }
00538            }
00539            SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
00540            PORT_Memset(smimeEntry, 0, sizeof(certDBEntry));
00541            break;
00542        }
00543     }
00544 
00545     if (ns <= 1)
00546        return subjectNum;
00547 
00548     if (userCert)
00549        return *subjectWithSMime;
00550 
00551     /*  Now find which of the subjects has the newest cert.  */
00552     certs = (CERTCertificate**)PORT_Alloc(ns*sizeof(CERTCertificate*));
00553     certNums = (int*)PORT_Alloc((ns+1)*sizeof(int));
00554     certNums[0] = 0;
00555     for (i=0; i<ns; i++) {
00556        sNum = subjectsForEmail[i];
00557        subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum];
00558        certs[i] = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
00559        certNums[i+1] = i;
00560     }
00561     /*  Sort the array by validity.  */
00562     qsort(certs, ns, sizeof(CERTCertificate*), 
00563           (int (*)(const void *, const void *))certIsOlder);
00564     newestSubject = -1;
00565     for (i=0; i<ns; i++) {
00566        sNum = subjectsForEmail[i];
00567        subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum];
00568        if (SECITEM_ItemsAreEqual(&subjectEntry1->derSubject,
00569                                  &certs[0]->derSubject))
00570            newestSubject = sNum;
00571        else
00572            SEC_DestroyDBEntry((certDBEntry*)subjectEntry1);
00573     }
00574     if (info && userSaysDeleteCert(certs, ns, dbOlderCert, info, certNums)) {
00575        for (i=1; i<ns+1; i++) {
00576            if (certNums[i] >= 0 && certNums[i] != certNums[0]) {
00577               deleteAllEntriesForCert(handle, certs[certNums[i]], info->out);
00578               info->dbErrors[dbOlderCert]++;
00579            }
00580        }
00581     }
00582     CERT_DestroyCertArray(certs, ns);
00583     return newestSubject;
00584 }
00585 
00586 NSSLOWCERTCertDBHandle *
00587 DBCK_ReconstructDBFromCerts(NSSLOWCERTCertDBHandle *oldhandle, char *newdbname,
00588                             PRFileDesc *outfile, PRBool removeExpired,
00589                             PRBool requireProfile, PRBool singleEntry,
00590                             PRBool promptUser)
00591 {
00592     SECStatus rv;
00593     dbRestoreInfo info;
00594     certDBEntryContentVersion *oldContentVersion;
00595     certDBArray dbArray;
00596     int i;
00597 
00598     PORT_Memset(&dbArray, 0, sizeof(dbArray));
00599     PORT_Memset(&info, 0, sizeof(info));
00600     info.verbose = (outfile) ? PR_TRUE : PR_FALSE;
00601     info.out = (outfile) ? outfile : PR_STDOUT;
00602     info.removeType[dbInvalidCert] = removeExpired;
00603     info.removeType[dbNoSMimeProfile] = requireProfile;
00604     info.removeType[dbOlderCert] = singleEntry;
00605     info.promptUser[dbInvalidCert]  = promptUser;
00606     info.promptUser[dbNoSMimeProfile]  = promptUser;
00607     info.promptUser[dbOlderCert]  = promptUser;
00608 
00609     /*  Allocate a handle to fill with CERT_OpenCertDB below.  */
00610     info.handle = PORT_ZNew(NSSLOWCERTCertDBHandle);
00611     if (!info.handle) {
00612        fprintf(stderr, "unable to get database handle");
00613        return NULL;
00614     }
00615 
00616     /*  Create a certdb with the most recent set of roots.  */
00617     rv = CERT_OpenCertDBFilename(info.handle, newdbname, PR_FALSE);
00618 
00619     if (rv) {
00620        fprintf(stderr, "could not open certificate database");
00621        goto loser;
00622     }
00623 
00624     /*  Create certificate, subject, nickname, and email records.
00625      *  mcom_db seems to have a sequential access bug.  Though reads and writes
00626      *  should be allowed during traversal, they seem to screw up the sequence.
00627      *  So, stuff all the cert entries into an array, and loop over the array
00628      *  doing read/writes in the db.
00629      */
00630     fillDBEntryArray(oldhandle, certDBEntryTypeCert, &dbArray.certs);
00631     for (elem = PR_LIST_HEAD(&dbArray->certs.link);
00632          elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
00633        node = LISTNODE_CAST(elem);
00634        addCertToDB((certDBEntryCert*)&node->entry, &info, oldhandle);
00635        /* entries get destroyed in addCertToDB */
00636     }
00637 #if 0
00638     rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeSMimeProfile, 
00639                                copyDBEntry, info.handle);
00640 #endif
00641 
00642     /*  Fix up the pointers between (nickname|S/MIME) --> (subject).
00643      *  Create S/MIME entries for S/MIME certs.
00644      *  Have the S/MIME entry point to the last-expiring cert using
00645      *  an email address.
00646      */
00647 #if 0
00648     CERT_RedoHandlesForSubjects(info.handle, singleEntry, &info);
00649 #endif
00650 
00651     freeDBEntryList(&dbArray.certs.link);
00652 
00653     /*  Copy over the version record.  */
00654     /*  XXX Already exists - and _must_ be correct... */
00655     /*
00656     versionEntry = ReadDBVersionEntry(oldhandle);
00657     rv = WriteDBVersionEntry(info.handle, versionEntry);
00658     */
00659 
00660     /*  Copy over the content version record.  */
00661     /*  XXX Can probably get useful info from old content version?
00662      *      Was this db created before/after this tool?  etc.
00663      */
00664 #if 0
00665     oldContentVersion = ReadDBContentVersionEntry(oldhandle);
00666     CERT_SetDBContentVersion(oldContentVersion->contentVersion, info.handle); 
00667 #endif
00668 
00669 #if 0
00670     /*  Copy over the CRL & KRL records.  */
00671     rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeRevocation, 
00672                                copyDBEntry, info.handle);
00673     /*  XXX Only one KRL, just do db->get? */
00674     rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeKeyRevocation, 
00675                                copyDBEntry, info.handle);
00676 #endif
00677 
00678     PR_fprintf(info.out, "Database had %d certificates.\n", info.nOldCerts);
00679 
00680     PR_fprintf(info.out, "Reconstructed %d certificates.\n", info.nCerts);
00681     PR_fprintf(info.out, "(ax) Rejected %d expired certificates.\n", 
00682                        info.dbErrors[dbInvalidCert]);
00683     PR_fprintf(info.out, "(as) Rejected %d S/MIME certificates missing a profile.\n", 
00684                        info.dbErrors[dbNoSMimeProfile]);
00685     PR_fprintf(info.out, "(ar) Rejected %d certificates for which a newer certificate was found.\n", 
00686                        info.dbErrors[dbOlderCert]);
00687     PR_fprintf(info.out, "     Rejected %d corrupt certificates.\n", 
00688                        info.dbErrors[dbBadCertificate]);
00689     PR_fprintf(info.out, "     Rejected %d certificates which did not write to the DB.\n", 
00690                        info.dbErrors[dbCertNotWrittenToDB]);
00691 
00692     if (rv)
00693        goto loser;
00694 
00695     return info.handle;
00696 
00697 loser:
00698     if (info.handle) 
00699        PORT_Free(info.handle);
00700     return NULL;
00701 }
00702