security/nss/cmd/dbck/dbrecover.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     5 enum {
     6     dbInvalidCert = 0,
     7     dbNoSMimeProfile,
     8     dbOlderCert,
     9     dbBadCertificate,
    10     dbCertNotWrittenToDB
    11 };
    13 typedef struct dbRestoreInfoStr
    14 {
    15     NSSLOWCERTCertDBHandle *handle;
    16     PRBool verbose;
    17     PRFileDesc *out;
    18     int nCerts;
    19     int nOldCerts;
    20     int dbErrors[5];
    21     PRBool removeType[3];
    22     PRBool promptUser[3];
    23 } dbRestoreInfo;
    25 char *
    26 IsEmailCert(CERTCertificate *cert)
    27 {
    28     char *email, *tmp1, *tmp2;
    29     PRBool isCA;
    30     int len;
    32     if (!cert->subjectName) {
    33 	return NULL;
    34     }
    36     tmp1 = PORT_Strstr(cert->subjectName, "E=");
    37     tmp2 = PORT_Strstr(cert->subjectName, "MAIL=");
    38     /* XXX Nelson has cert for KTrilli which does not have either
    39      * of above but is email cert (has cert->emailAddr). 
    40      */
    41     if (!tmp1 && !tmp2 && !(cert->emailAddr && cert->emailAddr[0])) {
    42 	return NULL;
    43     }
    45     /*  Server or CA cert, not personal email.  */
    46     isCA = CERT_IsCACert(cert, NULL);
    47     if (isCA)
    48 	return NULL;
    50     /*  XXX CERT_IsCACert advertises checking the key usage ext.,
    51 	but doesn't appear to. */
    52     /*  Check the key usage extension.  */
    53     if (cert->keyUsagePresent) {
    54 	/*  Must at least be able to sign or encrypt (not neccesarily
    55 	 *  both if it is one of a dual cert).  
    56 	 */
    57 	if (!((cert->rawKeyUsage & KU_DIGITAL_SIGNATURE) || 
    58               (cert->rawKeyUsage & KU_KEY_ENCIPHERMENT)))
    59 	    return NULL;
    61 	/*  CA cert, not personal email.  */
    62 	if (cert->rawKeyUsage & (KU_KEY_CERT_SIGN | KU_CRL_SIGN))
    63 	    return NULL;
    64     }
    66     if (cert->emailAddr && cert->emailAddr[0]) {
    67 	email = PORT_Strdup(cert->emailAddr);
    68     } else {
    69 	if (tmp1)
    70 	    tmp1 += 2; /* "E="  */
    71 	else
    72 	    tmp1 = tmp2 + 5; /* "MAIL=" */
    73 	len = strcspn(tmp1, ", ");
    74 	email = (char*)PORT_Alloc(len+1);
    75 	PORT_Strncpy(email, tmp1, len);
    76 	email[len] = '\0';
    77     }
    79     return email;
    80 }
    82 SECStatus
    83 deleteit(CERTCertificate *cert, void *arg)
    84 {
    85     return SEC_DeletePermCertificate(cert);
    86 }
    88 /*  Different than DeleteCertificate - has the added bonus of removing
    89  *  all certs with the same DN.  
    90  */
    91 SECStatus
    92 deleteAllEntriesForCert(NSSLOWCERTCertDBHandle *handle, CERTCertificate *cert,
    93                         PRFileDesc *outfile)
    94 {
    95 #if 0
    96     certDBEntrySubject *subjectEntry;
    97     certDBEntryNickname *nicknameEntry;
    98     certDBEntrySMime *smimeEntry;
    99     int i;
   100 #endif
   102     if (outfile) {
   103 	PR_fprintf(outfile, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n");
   104 	PR_fprintf(outfile, "Deleting redundant certificate:\n");
   105 	dumpCertificate(cert, -1, outfile);
   106     }
   108     CERT_TraverseCertsForSubject(handle, cert->subjectList, deleteit, NULL);
   109 #if 0
   110     CERT_LockDB(handle);
   111     subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
   112     /*  It had better be there, or created a bad db.  */
   113     PORT_Assert(subjectEntry);
   114     for (i=0; i<subjectEntry->ncerts; i++) {
   115 	DeleteDBCertEntry(handle, &subjectEntry->certKeys[i]);
   116     }
   117     DeleteDBSubjectEntry(handle, &cert->derSubject);
   118     if (subjectEntry->emailAddr && subjectEntry->emailAddr[0]) {
   119 	smimeEntry = ReadDBSMimeEntry(handle, subjectEntry->emailAddr);
   120 	if (smimeEntry) {
   121 	    if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
   122 	                              &smimeEntry->subjectName))
   123 		/*  Only delete it if it's for this subject!  */
   124 		DeleteDBSMimeEntry(handle, subjectEntry->emailAddr);
   125 	    SEC_DestroyDBEntry((certDBEntry*)smimeEntry);
   126 	}
   127     }
   128     if (subjectEntry->nickname) {
   129 	nicknameEntry = ReadDBNicknameEntry(handle, subjectEntry->nickname);
   130 	if (nicknameEntry) {
   131 	    if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject,
   132 	                              &nicknameEntry->subjectName))
   133 		/*  Only delete it if it's for this subject!  */
   134 		DeleteDBNicknameEntry(handle, subjectEntry->nickname);
   135 	    SEC_DestroyDBEntry((certDBEntry*)nicknameEntry);
   136 	}
   137     }
   138     SEC_DestroyDBEntry((certDBEntry*)subjectEntry);
   139     CERT_UnlockDB(handle);
   140 #endif
   141     return SECSuccess;
   142 }
   144 void
   145 getCertsToDelete(char *numlist, int len, int *certNums, int nCerts)
   146 {
   147     int j, num;
   148     char *numstr, *numend, *end;
   150     numstr = numlist;
   151     end = numstr + len - 1;
   152     while (numstr != end) {
   153 	numend = strpbrk(numstr, ", \n");
   154 	*numend = '\0';
   155 	if (PORT_Strlen(numstr) == 0)
   156 	    return;
   157 	num = PORT_Atoi(numstr);
   158 	if (numstr == numlist)
   159 	    certNums[0] = num;
   160 	for (j=1; j<nCerts+1; j++) {
   161 	    if (num == certNums[j]) {
   162 		certNums[j] = -1;
   163 		break;
   164 	    }
   165 	}
   166 	if (numend == end)
   167 	    break;
   168 	numstr = strpbrk(numend+1, "0123456789");
   169     }
   170 }
   172 PRBool
   173 userSaysDeleteCert(CERTCertificate **certs, int nCerts,
   174                    int errtype, dbRestoreInfo *info, int *certNums)
   175 {
   176     char response[32];
   177     PRInt32 nb;
   178     int i;
   179     /*  User wants to remove cert without prompting.  */
   180     if (info->promptUser[errtype] == PR_FALSE)
   181 	return (info->removeType[errtype]);
   182     switch (errtype) {
   183     case dbInvalidCert:
   184 	PR_fprintf(PR_STDOUT, "********  Expired ********\n");
   185 	PR_fprintf(PR_STDOUT, "Cert has expired.\n\n");
   186 	dumpCertificate(certs[0], -1, PR_STDOUT);
   187 	PR_fprintf(PR_STDOUT,
   188 	           "Keep it? (y/n - this one, Y/N - all expired certs) [n] ");
   189 	break;
   190     case dbNoSMimeProfile:
   191 	PR_fprintf(PR_STDOUT, "********  No Profile ********\n");
   192 	PR_fprintf(PR_STDOUT, "S/MIME cert has no profile.\n\n");
   193 	dumpCertificate(certs[0], -1, PR_STDOUT);
   194 	PR_fprintf(PR_STDOUT,
   195 	      "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] ");
   196 	break;
   197     case dbOlderCert:
   198 	PR_fprintf(PR_STDOUT, "*******  Redundant nickname/email *******\n\n");
   199 	PR_fprintf(PR_STDOUT, "These certs have the same nickname/email:\n");
   200 	for (i=0; i<nCerts; i++)
   201 	    dumpCertificate(certs[i], i, PR_STDOUT);
   202 	PR_fprintf(PR_STDOUT, 
   203 	"Enter the certs you would like to keep from those listed above.\n");
   204 	PR_fprintf(PR_STDOUT, 
   205 	"Use a comma-separated list of the cert numbers (ex. 0, 8, 12).\n");
   206 	PR_fprintf(PR_STDOUT, 
   207 	"The first cert in the list will be the primary cert\n");
   208 	PR_fprintf(PR_STDOUT, 
   209 	" accessed by the nickname/email handle.\n");
   210 	PR_fprintf(PR_STDOUT, 
   211 	"List cert numbers to keep here, or hit enter\n");
   212 	PR_fprintf(PR_STDOUT, 
   213 	" to always keep only the newest cert:  ");
   214 	break;
   215     default:
   216     }
   217     nb = PR_Read(PR_STDIN, response, sizeof(response));
   218     PR_fprintf(PR_STDOUT, "\n\n");
   219     if (errtype == dbOlderCert) {
   220 	if (!isdigit(response[0])) {
   221 	    info->promptUser[errtype] = PR_FALSE;
   222 	    info->removeType[errtype] = PR_TRUE;
   223 	    return PR_TRUE;
   224 	}
   225 	getCertsToDelete(response, nb, certNums, nCerts);
   226 	return PR_TRUE;
   227     }
   228     /*  User doesn't want to be prompted for this type anymore.  */
   229     if (response[0] == 'Y') {
   230 	info->promptUser[errtype] = PR_FALSE;
   231 	info->removeType[errtype] = PR_FALSE;
   232 	return PR_FALSE;
   233     } else if (response[0] == 'N') {
   234 	info->promptUser[errtype] = PR_FALSE;
   235 	info->removeType[errtype] = PR_TRUE;
   236 	return PR_TRUE;
   237     }
   238     return (response[0] != 'y') ? PR_TRUE : PR_FALSE;
   239 }
   241 SECStatus
   242 addCertToDB(certDBEntryCert *certEntry, dbRestoreInfo *info, 
   243             NSSLOWCERTCertDBHandle *oldhandle)
   244 {
   245     SECStatus rv = SECSuccess;
   246     PRBool allowOverride;
   247     PRBool userCert;
   248     SECCertTimeValidity validity;
   249     CERTCertificate *oldCert = NULL;
   250     CERTCertificate *dbCert = NULL;
   251     CERTCertificate *newCert = NULL;
   252     CERTCertTrust *trust;
   253     certDBEntrySMime *smimeEntry = NULL;
   254     char *email = NULL;
   255     char *nickname = NULL;
   256     int nCertsForSubject = 1;
   258     oldCert = CERT_DecodeDERCertificate(&certEntry->derCert, PR_FALSE,
   259                                         certEntry->nickname);
   260     if (!oldCert) {
   261 	info->dbErrors[dbBadCertificate]++;
   262 	SEC_DestroyDBEntry((certDBEntry*)certEntry);
   263 	return SECSuccess;
   264     }
   266     oldCert->dbEntry = certEntry;
   267     oldCert->trust = &certEntry->trust;
   268     oldCert->dbhandle = oldhandle;
   270     trust = oldCert->trust;
   272     info->nOldCerts++;
   274     if (info->verbose)
   275 	PR_fprintf(info->out, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n");
   277     if (oldCert->nickname)
   278 	nickname = PORT_Strdup(oldCert->nickname);
   280     /*  Always keep user certs.  Skip ahead.  */
   281     /*  XXX if someone sends themselves a signed message, it is possible
   282 	for their cert to be imported as an "other" cert, not a user cert.
   283 	this mucks with smime entries...  */
   284     userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) ||
   285                (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) ||
   286                (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER);
   287     if (userCert)
   288 	goto createcert;
   290     /*  If user chooses so, ignore expired certificates.  */
   291     allowOverride = (PRBool)((oldCert->keyUsage == certUsageSSLServer) ||
   292                          (oldCert->keyUsage == certUsageSSLServerWithStepUp));
   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     }
   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     }
   317     /*  Determine if cert is S/MIME and get its email if so.  */
   318     email = IsEmailCert(oldCert);
   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     */
   339 createcert:
   341     /*  Sometimes happens... */
   342     if (!nickname && userCert)
   343 	nickname = PORT_Strdup(oldCert->subjectName);
   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     }
   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     }
   364     if (info->verbose) {
   365 	PR_fprintf(info->out, "Added certificate to database:\n");
   366 	dumpCertificate(oldCert, -1, info->out);
   367     }
   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     }
   384     info->nCerts++;
   386 cleanup:
   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 }
   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;
   412     common.type = type;
   413     common.version = CERT_DB_FILE_VERSION;
   414     common.flags = data->data[2];
   415     common.arena = NULL;
   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;
   422     rv = WriteDBEntry(newdb, &common, &dbkey, data);
   424     PORT_Free(dbkey.data);
   425     return rv;
   426 }
   427 #endif
   429 int
   430 certIsOlder(CERTCertificate **cert1, CERTCertificate** cert2)
   431 {
   432     return !CERT_IsNewer(*cert1, *cert2);
   433 }
   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;
   453     ns = 0;
   454     subjectEntry1 = (certDBEntrySubject*)&subjects.entries[subjectNum];
   455     subjectsForEmail[ns++] = subjectNum;
   457     *subjectWithSMime = -1;
   458     *smimeForSubject = -1;
   459     newestSubject = subjectNum;
   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     }
   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      */
   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     }
   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     }
   513     if (ns <= 1)
   514 	return subjectNum;
   516     if (userCert)
   517 	return *subjectWithSMime;
   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 }
   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;
   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;
   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     }
   584     /*  Create a certdb with the most recent set of roots.  */
   585     rv = CERT_OpenCertDBFilename(info.handle, newdbname, PR_FALSE);
   587     if (rv) {
   588 	fprintf(stderr, "could not open certificate database");
   589 	goto loser;
   590     }
   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
   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
   619     freeDBEntryList(&dbArray.certs.link);
   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     */
   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
   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
   646     PR_fprintf(info.out, "Database had %d certificates.\n", info.nOldCerts);
   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]);
   660     if (rv)
   661 	goto loser;
   663     return info.handle;
   665 loser:
   666     if (info.handle) 
   667 	PORT_Free(info.handle);
   668     return NULL;
   669 }

mercurial