tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dbrecover.c (23369B)


      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 enum {
      6    dbInvalidCert = 0,
      7    dbNoSMimeProfile,
      8    dbOlderCert,
      9    dbBadCertificate,
     10    dbCertNotWrittenToDB
     11 };
     12 
     13 typedef struct dbRestoreInfoStr {
     14    NSSLOWCERTCertDBHandle *handle;
     15    PRBool verbose;
     16    PRFileDesc *out;
     17    int nCerts;
     18    int nOldCerts;
     19    int dbErrors[5];
     20    PRBool removeType[3];
     21    PRBool promptUser[3];
     22 } dbRestoreInfo;
     23 
     24 char *
     25 IsEmailCert(CERTCertificate *cert)
     26 {
     27    char *email, *tmp1, *tmp2;
     28    PRBool isCA;
     29    int len;
     30 
     31    if (!cert->subjectName) {
     32        return NULL;
     33    }
     34 
     35    tmp1 = PORT_Strstr(cert->subjectName, "E=");
     36    tmp2 = PORT_Strstr(cert->subjectName, "MAIL=");
     37    /* XXX Nelson has cert for KTrilli which does not have either
     38     * of above but is email cert (has cert->emailAddr).
     39     */
     40    if (!tmp1 && !tmp2 && !(cert->emailAddr && cert->emailAddr[0])) {
     41        return NULL;
     42    }
     43 
     44    /*  Server or CA cert, not personal email.  */
     45    isCA = CERT_IsCACert(cert, NULL);
     46    if (isCA)
     47        return NULL;
     48 
     49    /*  XXX CERT_IsCACert advertises checking the key usage ext.,
     50        but doesn't appear to. */
     51    /*  Check the key usage extension.  */
     52    if (cert->keyUsagePresent) {
     53        /*  Must at least be able to sign or encrypt (not neccesarily
     54         *  both if it is one of a dual cert).
     55         */
     56        if (!((cert->rawKeyUsage & KU_DIGITAL_SIGNATURE) ||
     57              (cert->rawKeyUsage & KU_KEY_ENCIPHERMENT)))
     58            return NULL;
     59 
     60        /*  CA cert, not personal email.  */
     61        if (cert->rawKeyUsage & (KU_KEY_CERT_SIGN | KU_CRL_SIGN))
     62            return NULL;
     63    }
     64 
     65    if (cert->emailAddr && cert->emailAddr[0]) {
     66        email = PORT_Strdup(cert->emailAddr);
     67    } else {
     68        if (tmp1)
     69            tmp1 += 2; /* "E="  */
     70        else
     71            tmp1 = tmp2 + 5; /* "MAIL=" */
     72        len = strcspn(tmp1, ", ");
     73        email = (char *)PORT_Alloc(len + 1);
     74        PORT_Strncpy(email, tmp1, len);
     75        email[len] = '\0';
     76    }
     77 
     78    return email;
     79 }
     80 
     81 SECStatus
     82 deleteit(CERTCertificate *cert, void *arg)
     83 {
     84    return SEC_DeletePermCertificate(cert);
     85 }
     86 
     87 /*  Different than DeleteCertificate - has the added bonus of removing
     88 *  all certs with the same DN.
     89 */
     90 SECStatus
     91 deleteAllEntriesForCert(NSSLOWCERTCertDBHandle *handle, CERTCertificate *cert,
     92                        PRFileDesc *outfile)
     93 {
     94 #if 0
     95    certDBEntrySubject *subjectEntry;
     96    certDBEntryNickname *nicknameEntry;
     97    certDBEntrySMime *smimeEntry;
     98    int i;
     99 #endif
    100 
    101    if (outfile) {
    102        PR_fprintf(outfile, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
    103        PR_fprintf(outfile, "Deleting redundant certificate:\n");
    104        dumpCertificate(cert, -1, outfile);
    105    }
    106 
    107    CERT_TraverseCertsForSubject(handle, cert->subjectList, deleteit, NULL);
    108 #if 0
    109    CERT_LockDB(handle);
    110    subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
    111    /*  It had better be there, or created a bad db.  */
    112    PORT_Assert(subjectEntry);
    113    for (i=0; i<subjectEntry->ncerts; i++) {
    114        DeleteDBCertEntry(handle, &subjectEntry->certKeys[i]);
    115    }
    116    DeleteDBSubjectEntry(handle, &cert->derSubject);
    117    if (subjectEntry->emailAddr && subjectEntry->emailAddr[0]) {
    118        smimeEntry = ReadDBSMimeEntry(handle, subjectEntry->emailAddr);
    119        if (smimeEntry) {
    120            if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
    121                                      &smimeEntry->subjectName))
    122                /*  Only delete it if it's for this subject!  */
    123                DeleteDBSMimeEntry(handle, subjectEntry->emailAddr);
    124            SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
    125        }
    126    }
    127    if (subjectEntry->nickname) {
    128        nicknameEntry = ReadDBNicknameEntry(handle, subjectEntry->nickname);
    129        if (nicknameEntry) {
    130            if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
    131                                      &nicknameEntry->subjectName))
    132                /*  Only delete it if it's for this subject!  */
    133                DeleteDBNicknameEntry(handle, subjectEntry->nickname);
    134            SEC_DestroyDBEntry((certDBEntry*)nicknameEntry);
    135        }
    136    }
    137    SEC_DestroyDBEntry((certDBEntry*)subjectEntry);
    138    CERT_UnlockDB(handle);
    139 #endif
    140    return SECSuccess;
    141 }
    142 
    143 void
    144 getCertsToDelete(char *numlist, int len, int *certNums, int nCerts)
    145 {
    146    int j, num;
    147    char *numstr, *numend, *end;
    148 
    149    numstr = numlist;
    150    end = numstr + len - 1;
    151    while (numstr != end) {
    152        numend = strpbrk(numstr, ", \n");
    153        *numend = '\0';
    154        if (PORT_Strlen(numstr) == 0)
    155            return;
    156        num = PORT_Atoi(numstr);
    157        if (numstr == numlist)
    158            certNums[0] = num;
    159        for (j = 1; j < nCerts + 1; j++) {
    160            if (num == certNums[j]) {
    161                certNums[j] = -1;
    162                break;
    163            }
    164        }
    165        if (numend == end)
    166            break;
    167        numstr = strpbrk(numend + 1, "0123456789");
    168    }
    169 }
    170 
    171 PRBool
    172 userSaysDeleteCert(CERTCertificate **certs, int nCerts,
    173                   int errtype, dbRestoreInfo *info, int *certNums)
    174 {
    175    char response[32];
    176    PRInt32 nb;
    177    int i;
    178    /*  User wants to remove cert without prompting.  */
    179    if (info->promptUser[errtype] == PR_FALSE)
    180        return (info->removeType[errtype]);
    181    switch (errtype) {
    182        case dbInvalidCert:
    183            PR_fprintf(PR_STDOUT, "********  Expired ********\n");
    184            PR_fprintf(PR_STDOUT, "Cert has expired.\n\n");
    185            dumpCertificate(certs[0], -1, PR_STDOUT);
    186            PR_fprintf(PR_STDOUT,
    187                       "Keep it? (y/n - this one, Y/N - all expired certs) [n] ");
    188            break;
    189        case dbNoSMimeProfile:
    190            PR_fprintf(PR_STDOUT, "********  No Profile ********\n");
    191            PR_fprintf(PR_STDOUT, "S/MIME cert has no profile.\n\n");
    192            dumpCertificate(certs[0], -1, PR_STDOUT);
    193            PR_fprintf(PR_STDOUT,
    194                       "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] ");
    195            break;
    196        case dbOlderCert:
    197            PR_fprintf(PR_STDOUT, "*******  Redundant nickname/email *******\n\n");
    198            PR_fprintf(PR_STDOUT, "These certs have the same nickname/email:\n");
    199            for (i = 0; i < nCerts; i++)
    200                dumpCertificate(certs[i], i, PR_STDOUT);
    201            PR_fprintf(PR_STDOUT,
    202                       "Enter the certs you would like to keep from those listed above.\n");
    203            PR_fprintf(PR_STDOUT,
    204                       "Use a comma-separated list of the cert numbers (ex. 0, 8, 12).\n");
    205            PR_fprintf(PR_STDOUT,
    206                       "The first cert in the list will be the primary cert\n");
    207            PR_fprintf(PR_STDOUT,
    208                       " accessed by the nickname/email handle.\n");
    209            PR_fprintf(PR_STDOUT,
    210                       "List cert numbers to keep here, or hit enter\n");
    211            PR_fprintf(PR_STDOUT,
    212                       " to always keep only the newest cert:  ");
    213            break;
    214        default:
    215    }
    216    nb = PR_Read(PR_STDIN, response, sizeof(response));
    217    PR_fprintf(PR_STDOUT, "\n\n");
    218    if (errtype == dbOlderCert) {
    219        if (!isdigit((unsigned char)response[0])) {
    220            info->promptUser[errtype] = PR_FALSE;
    221            info->removeType[errtype] = PR_TRUE;
    222            return PR_TRUE;
    223        }
    224        getCertsToDelete(response, nb, certNums, nCerts);
    225        return PR_TRUE;
    226    }
    227    /*  User doesn't want to be prompted for this type anymore.  */
    228    if (response[0] == 'Y') {
    229        info->promptUser[errtype] = PR_FALSE;
    230        info->removeType[errtype] = PR_FALSE;
    231        return PR_FALSE;
    232    } else if (response[0] == 'N') {
    233        info->promptUser[errtype] = PR_FALSE;
    234        info->removeType[errtype] = PR_TRUE;
    235        return PR_TRUE;
    236    }
    237    return (response[0] != 'y') ? PR_TRUE : PR_FALSE;
    238 }
    239 
    240 SECStatus
    241 addCertToDB(certDBEntryCert *certEntry, dbRestoreInfo *info,
    242            NSSLOWCERTCertDBHandle *oldhandle)
    243 {
    244    SECStatus rv = SECSuccess;
    245    PRBool allowOverride;
    246    PRBool userCert;
    247    SECCertTimeValidity validity;
    248    CERTCertificate *oldCert = NULL;
    249    CERTCertificate *dbCert = NULL;
    250    CERTCertificate *newCert = NULL;
    251    CERTCertTrust *trust;
    252    certDBEntrySMime *smimeEntry = NULL;
    253    char *email = NULL;
    254    char *nickname = NULL;
    255    int nCertsForSubject = 1;
    256 
    257    oldCert = CERT_DecodeDERCertificate(&certEntry->derCert, PR_FALSE,
    258                                        certEntry->nickname);
    259    if (!oldCert) {
    260        info->dbErrors[dbBadCertificate]++;
    261        SEC_DestroyDBEntry((certDBEntry *)certEntry);
    262        return SECSuccess;
    263    }
    264 
    265    oldCert->dbEntry = certEntry;
    266    oldCert->trust = &certEntry->trust;
    267    oldCert->dbhandle = oldhandle;
    268 
    269    trust = oldCert->trust;
    270 
    271    info->nOldCerts++;
    272 
    273    if (info->verbose)
    274        PR_fprintf(info->out, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n");
    275 
    276    if (oldCert->nickname)
    277        nickname = PORT_Strdup(oldCert->nickname);
    278 
    279    /*  Always keep user certs.  Skip ahead.  */
    280    /*  XXX if someone sends themselves a signed message, it is possible
    281        for their cert to be imported as an "other" cert, not a user cert.
    282        this mucks with smime entries...  */
    283    userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
    284               (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
    285               (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
    286    if (userCert)
    287        goto createcert;
    288 
    289    /*  If user chooses so, ignore expired certificates.  */
    290    allowOverride = (PRBool)((oldCert->keyUsage == certUsageSSLServer) ||
    291                             (oldCert->keyUsage == certUsageSSLServerWithStepUp) ||
    292                             (oldCert->keyUsage == certUsageIPsec));
    293    validity = CERT_CheckCertValidTimes(oldCert, PR_Now(), allowOverride);
    294    /*  If cert expired and user wants to delete it, ignore it. */
    295    if ((validity != secCertTimeValid) &&
    296        userSaysDeleteCert(&oldCert, 1, dbInvalidCert, info, 0)) {
    297        info->dbErrors[dbInvalidCert]++;
    298        if (info->verbose) {
    299            PR_fprintf(info->out, "Deleting expired certificate:\n");
    300            dumpCertificate(oldCert, -1, info->out);
    301        }
    302        goto cleanup;
    303    }
    304 
    305    /*  New database will already have default certs, don't attempt
    306        to overwrite them.  */
    307    dbCert = CERT_FindCertByDERCert(info->handle, &oldCert->derCert);
    308    if (dbCert) {
    309        info->nCerts++;
    310        if (info->verbose) {
    311            PR_fprintf(info->out, "Added certificate to database:\n");
    312            dumpCertificate(oldCert, -1, info->out);
    313        }
    314        goto cleanup;
    315    }
    316 
    317    /*  Determine if cert is S/MIME and get its email if so.  */
    318    email = IsEmailCert(oldCert);
    319 
    320    /*
    321        XXX  Just create empty profiles?
    322    if (email) {
    323        SECItem *profile = CERT_FindSMimeProfile(oldCert);
    324        if (!profile &&
    325            userSaysDeleteCert(&oldCert, 1, dbNoSMimeProfile, info, 0)) {
    326            info->dbErrors[dbNoSMimeProfile]++;
    327            if (info->verbose) {
    328                PR_fprintf(info->out,
    329                           "Deleted cert missing S/MIME profile.\n");
    330                dumpCertificate(oldCert, -1, info->out);
    331            }
    332            goto cleanup;
    333        } else {
    334            SECITEM_FreeItem(profile);
    335        }
    336    }
    337    */
    338 
    339 createcert:
    340 
    341    /*  Sometimes happens... */
    342    if (!nickname && userCert)
    343        nickname = PORT_Strdup(oldCert->subjectName);
    344 
    345    /*  Create a new certificate, copy of the old one.  */
    346    newCert = CERT_NewTempCertificate(info->handle, &oldCert->derCert,
    347                                      nickname, PR_FALSE, PR_TRUE);
    348    if (!newCert) {
    349        PR_fprintf(PR_STDERR, "Unable to create new certificate.\n");
    350        dumpCertificate(oldCert, -1, PR_STDERR);
    351        info->dbErrors[dbBadCertificate]++;
    352        goto cleanup;
    353    }
    354 
    355    /*  Add the cert to the new database.  */
    356    rv = CERT_AddTempCertToPerm(newCert, nickname, oldCert->trust);
    357    if (rv) {
    358        PR_fprintf(PR_STDERR, "Failed to write temp cert to perm database.\n");
    359        dumpCertificate(oldCert, -1, PR_STDERR);
    360        info->dbErrors[dbCertNotWrittenToDB]++;
    361        goto cleanup;
    362    }
    363 
    364    if (info->verbose) {
    365        PR_fprintf(info->out, "Added certificate to database:\n");
    366        dumpCertificate(oldCert, -1, info->out);
    367    }
    368 
    369    /*  If the cert is an S/MIME cert, and the first with it's subject,
    370     *  modify the subject entry to include the email address,
    371     *  CERT_AddTempCertToPerm does not do email addresses and S/MIME entries.
    372     */
    373    if (smimeEntry) { /*&& !userCert && nCertsForSubject == 1) { */
    374 #if 0
    375        UpdateSubjectWithEmailAddr(newCert, email);
    376 #endif
    377        SECItem emailProfile, profileTime;
    378        rv = CERT_FindFullSMimeProfile(oldCert, &emailProfile, &profileTime);
    379        /*  calls UpdateSubjectWithEmailAddr  */
    380        if (rv == SECSuccess)
    381            rv = CERT_SaveSMimeProfile(newCert, &emailProfile, &profileTime);
    382    }
    383 
    384    info->nCerts++;
    385 
    386 cleanup:
    387 
    388    if (nickname)
    389        PORT_Free(nickname);
    390    if (email)
    391        PORT_Free(email);
    392    if (oldCert)
    393        CERT_DestroyCertificate(oldCert);
    394    if (dbCert)
    395        CERT_DestroyCertificate(dbCert);
    396    if (newCert)
    397        CERT_DestroyCertificate(newCert);
    398    if (smimeEntry)
    399        SEC_DestroyDBEntry((certDBEntry *)smimeEntry);
    400    return SECSuccess;
    401 }
    402 
    403 #if 0
    404 SECStatus
    405 copyDBEntry(SECItem *data, SECItem *key, certDBEntryType type, void *pdata)
    406 {
    407    SECStatus rv;
    408    NSSLOWCERTCertDBHandle *newdb = (NSSLOWCERTCertDBHandle *)pdata;
    409    certDBEntryCommon common;
    410    SECItem dbkey;
    411 
    412    common.type = type;
    413    common.version = CERT_DB_FILE_VERSION;
    414    common.flags = data->data[2];
    415    common.arena = NULL;
    416 
    417    dbkey.len = key->len + SEC_DB_KEY_HEADER_LEN;
    418    dbkey.data = (unsigned char *)PORT_Alloc(dbkey.len*sizeof(unsigned char));
    419    PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], key->data, key->len);
    420    dbkey.data[0] = type;
    421 
    422    rv = WriteDBEntry(newdb, &common, &dbkey, data);
    423 
    424    PORT_Free(dbkey.data);
    425    return rv;
    426 }
    427 #endif
    428 
    429 int
    430 certIsOlder(CERTCertificate **cert1, CERTCertificate **cert2)
    431 {
    432    return !CERT_IsNewer(*cert1, *cert2);
    433 }
    434 
    435 int
    436 findNewestSubjectForEmail(NSSLOWCERTCertDBHandle *handle, int subjectNum,
    437                          certDBArray *dbArray, dbRestoreInfo *info,
    438                          int *subjectWithSMime, int *smimeForSubject)
    439 {
    440    int newestSubject;
    441    int subjectsForEmail[50];
    442    int i, j, ns, sNum;
    443    certDBEntryListNode *subjects = &dbArray->subjects;
    444    certDBEntryListNode *smime = &dbArray->smime;
    445    certDBEntrySubject *subjectEntry1, *subjectEntry2;
    446    certDBEntrySMime *smimeEntry;
    447    CERTCertificate **certs;
    448    CERTCertificate *cert;
    449    CERTCertTrust *trust;
    450    PRBool userCert;
    451    int *certNums;
    452 
    453    ns = 0;
    454    subjectEntry1 = (certDBEntrySubject *)&subjects.entries[subjectNum];
    455    subjectsForEmail[ns++] = subjectNum;
    456 
    457    *subjectWithSMime = -1;
    458    *smimeForSubject = -1;
    459    newestSubject = subjectNum;
    460 
    461    cert = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
    462    if (cert) {
    463        trust = cert->trust;
    464        userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
    465                   (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
    466                   (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
    467        CERT_DestroyCertificate(cert);
    468    }
    469 
    470    /*
    471     * XXX Should we make sure that subjectEntry1->emailAddr is not
    472     * a null pointer or an empty string before going into the next
    473     * two for loops, which pass it to PORT_Strcmp?
    474     */
    475 
    476    /*  Loop over the remaining subjects.  */
    477    for (i = subjectNum + 1; i < subjects.numEntries; i++) {
    478        subjectEntry2 = (certDBEntrySubject *)&subjects.entries[i];
    479        if (!subjectEntry2)
    480            continue;
    481        if (subjectEntry2->emailAddr && subjectEntry2->emailAddr[0] &&
    482            PORT_Strcmp(subjectEntry1->emailAddr,
    483                        subjectEntry2->emailAddr) == 0) {
    484            /*  Found a subject using the same email address.  */
    485            subjectsForEmail[ns++] = i;
    486        }
    487    }
    488 
    489    /*  Find the S/MIME entry for this email address.  */
    490    for (i = 0; i < smime.numEntries; i++) {
    491        smimeEntry = (certDBEntrySMime *)&smime.entries[i];
    492        if (smimeEntry->common.arena == NULL)
    493            continue;
    494        if (smimeEntry->emailAddr && smimeEntry->emailAddr[0] &&
    495            PORT_Strcmp(subjectEntry1->emailAddr, smimeEntry->emailAddr) == 0) {
    496            /*  Find which of the subjects uses this S/MIME entry.  */
    497            for (j = 0; j < ns && *subjectWithSMime < 0; j++) {
    498                sNum = subjectsForEmail[j];
    499                subjectEntry2 = (certDBEntrySubject *)&subjects.entries[sNum];
    500                if (SECITEM_ItemsAreEqual(&smimeEntry->subjectName,
    501                                          &subjectEntry2->derSubject)) {
    502                    /*  Found the subject corresponding to the S/MIME entry. */
    503                    *subjectWithSMime = sNum;
    504                    *smimeForSubject = i;
    505                }
    506            }
    507            SEC_DestroyDBEntry((certDBEntry *)smimeEntry);
    508            PORT_Memset(smimeEntry, 0, sizeof(certDBEntry));
    509            break;
    510        }
    511    }
    512 
    513    if (ns <= 1)
    514        return subjectNum;
    515 
    516    if (userCert)
    517        return *subjectWithSMime;
    518 
    519    /*  Now find which of the subjects has the newest cert.  */
    520    certs = (CERTCertificate **)PORT_Alloc(ns * sizeof(CERTCertificate *));
    521    certNums = (int *)PORT_Alloc((ns + 1) * sizeof(int));
    522    certNums[0] = 0;
    523    for (i = 0; i < ns; i++) {
    524        sNum = subjectsForEmail[i];
    525        subjectEntry1 = (certDBEntrySubject *)&subjects.entries[sNum];
    526        certs[i] = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]);
    527        certNums[i + 1] = i;
    528    }
    529    /*  Sort the array by validity.  */
    530    qsort(certs, ns, sizeof(CERTCertificate *),
    531          (int (*)(const void *, const void *))certIsOlder);
    532    newestSubject = -1;
    533    for (i = 0; i < ns; i++) {
    534        sNum = subjectsForEmail[i];
    535        subjectEntry1 = (certDBEntrySubject *)&subjects.entries[sNum];
    536        if (SECITEM_ItemsAreEqual(&subjectEntry1->derSubject,
    537                                  &certs[0]->derSubject))
    538            newestSubject = sNum;
    539        else
    540            SEC_DestroyDBEntry((certDBEntry *)subjectEntry1);
    541    }
    542    if (info && userSaysDeleteCert(certs, ns, dbOlderCert, info, certNums)) {
    543        for (i = 1; i < ns + 1; i++) {
    544            if (certNums[i] >= 0 && certNums[i] != certNums[0]) {
    545                deleteAllEntriesForCert(handle, certs[certNums[i]], info->out);
    546                info->dbErrors[dbOlderCert]++;
    547            }
    548        }
    549    }
    550    CERT_DestroyCertArray(certs, ns);
    551    return newestSubject;
    552 }
    553 
    554 NSSLOWCERTCertDBHandle *
    555 DBCK_ReconstructDBFromCerts(NSSLOWCERTCertDBHandle *oldhandle, char *newdbname,
    556                            PRFileDesc *outfile, PRBool removeExpired,
    557                            PRBool requireProfile, PRBool singleEntry,
    558                            PRBool promptUser)
    559 {
    560    SECStatus rv;
    561    dbRestoreInfo info;
    562    certDBEntryContentVersion *oldContentVersion;
    563    certDBArray dbArray;
    564    int i;
    565 
    566    PORT_Memset(&dbArray, 0, sizeof(dbArray));
    567    PORT_Memset(&info, 0, sizeof(info));
    568    info.verbose = (outfile) ? PR_TRUE : PR_FALSE;
    569    info.out = (outfile) ? outfile : PR_STDOUT;
    570    info.removeType[dbInvalidCert] = removeExpired;
    571    info.removeType[dbNoSMimeProfile] = requireProfile;
    572    info.removeType[dbOlderCert] = singleEntry;
    573    info.promptUser[dbInvalidCert] = promptUser;
    574    info.promptUser[dbNoSMimeProfile] = promptUser;
    575    info.promptUser[dbOlderCert] = promptUser;
    576 
    577    /*  Allocate a handle to fill with CERT_OpenCertDB below.  */
    578    info.handle = PORT_ZNew(NSSLOWCERTCertDBHandle);
    579    if (!info.handle) {
    580        fprintf(stderr, "unable to get database handle");
    581        return NULL;
    582    }
    583 
    584    /*  Create a certdb with the most recent set of roots.  */
    585    rv = CERT_OpenCertDBFilename(info.handle, newdbname, PR_FALSE);
    586 
    587    if (rv) {
    588        fprintf(stderr, "could not open certificate database");
    589        goto loser;
    590    }
    591 
    592    /*  Create certificate, subject, nickname, and email records.
    593     *  mcom_db seems to have a sequential access bug.  Though reads and writes
    594     *  should be allowed during traversal, they seem to screw up the sequence.
    595     *  So, stuff all the cert entries into an array, and loop over the array
    596     *  doing read/writes in the db.
    597     */
    598    fillDBEntryArray(oldhandle, certDBEntryTypeCert, &dbArray.certs);
    599    for (elem = PR_LIST_HEAD(&dbArray->certs.link);
    600         elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) {
    601        node = LISTNODE_CAST(elem);
    602        addCertToDB((certDBEntryCert *)&node->entry, &info, oldhandle);
    603        /* entries get destroyed in addCertToDB */
    604    }
    605 #if 0
    606    rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeSMimeProfile,
    607                               copyDBEntry, info.handle);
    608 #endif
    609 
    610 /*  Fix up the pointers between (nickname|S/MIME) --> (subject).
    611 *  Create S/MIME entries for S/MIME certs.
    612 *  Have the S/MIME entry point to the last-expiring cert using
    613 *  an email address.
    614 */
    615 #if 0
    616    CERT_RedoHandlesForSubjects(info.handle, singleEntry, &info);
    617 #endif
    618 
    619    freeDBEntryList(&dbArray.certs.link);
    620 
    621 /*  Copy over the version record.  */
    622 /*  XXX Already exists - and _must_ be correct... */
    623 /*
    624    versionEntry = ReadDBVersionEntry(oldhandle);
    625    rv = WriteDBVersionEntry(info.handle, versionEntry);
    626    */
    627 
    628 /*  Copy over the content version record.  */
    629 /*  XXX Can probably get useful info from old content version?
    630 *      Was this db created before/after this tool?  etc.
    631 */
    632 #if 0
    633    oldContentVersion = ReadDBContentVersionEntry(oldhandle);
    634    CERT_SetDBContentVersion(oldContentVersion->contentVersion, info.handle);
    635 #endif
    636 
    637 #if 0
    638    /*  Copy over the CRL & KRL records.  */
    639    rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeRevocation,
    640                               copyDBEntry, info.handle);
    641    /*  XXX Only one KRL, just do db->get? */
    642    rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeKeyRevocation,
    643                               copyDBEntry, info.handle);
    644 #endif
    645 
    646    PR_fprintf(info.out, "Database had %d certificates.\n", info.nOldCerts);
    647 
    648    PR_fprintf(info.out, "Reconstructed %d certificates.\n", info.nCerts);
    649    PR_fprintf(info.out, "(ax) Rejected %d expired certificates.\n",
    650               info.dbErrors[dbInvalidCert]);
    651    PR_fprintf(info.out, "(as) Rejected %d S/MIME certificates missing a profile.\n",
    652               info.dbErrors[dbNoSMimeProfile]);
    653    PR_fprintf(info.out, "(ar) Rejected %d certificates for which a newer certificate was found.\n",
    654               info.dbErrors[dbOlderCert]);
    655    PR_fprintf(info.out, "     Rejected %d corrupt certificates.\n",
    656               info.dbErrors[dbBadCertificate]);
    657    PR_fprintf(info.out, "     Rejected %d certificates which did not write to the DB.\n",
    658               info.dbErrors[dbCertNotWrittenToDB]);
    659 
    660    if (rv)
    661        goto loser;
    662 
    663    return info.handle;
    664 
    665 loser:
    666    if (info.handle)
    667        PORT_Free(info.handle);
    668    return NULL;
    669 }