1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/cmd/dbck/dbrecover.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,670 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +enum { 1.9 + dbInvalidCert = 0, 1.10 + dbNoSMimeProfile, 1.11 + dbOlderCert, 1.12 + dbBadCertificate, 1.13 + dbCertNotWrittenToDB 1.14 +}; 1.15 + 1.16 +typedef struct dbRestoreInfoStr 1.17 +{ 1.18 + NSSLOWCERTCertDBHandle *handle; 1.19 + PRBool verbose; 1.20 + PRFileDesc *out; 1.21 + int nCerts; 1.22 + int nOldCerts; 1.23 + int dbErrors[5]; 1.24 + PRBool removeType[3]; 1.25 + PRBool promptUser[3]; 1.26 +} dbRestoreInfo; 1.27 + 1.28 +char * 1.29 +IsEmailCert(CERTCertificate *cert) 1.30 +{ 1.31 + char *email, *tmp1, *tmp2; 1.32 + PRBool isCA; 1.33 + int len; 1.34 + 1.35 + if (!cert->subjectName) { 1.36 + return NULL; 1.37 + } 1.38 + 1.39 + tmp1 = PORT_Strstr(cert->subjectName, "E="); 1.40 + tmp2 = PORT_Strstr(cert->subjectName, "MAIL="); 1.41 + /* XXX Nelson has cert for KTrilli which does not have either 1.42 + * of above but is email cert (has cert->emailAddr). 1.43 + */ 1.44 + if (!tmp1 && !tmp2 && !(cert->emailAddr && cert->emailAddr[0])) { 1.45 + return NULL; 1.46 + } 1.47 + 1.48 + /* Server or CA cert, not personal email. */ 1.49 + isCA = CERT_IsCACert(cert, NULL); 1.50 + if (isCA) 1.51 + return NULL; 1.52 + 1.53 + /* XXX CERT_IsCACert advertises checking the key usage ext., 1.54 + but doesn't appear to. */ 1.55 + /* Check the key usage extension. */ 1.56 + if (cert->keyUsagePresent) { 1.57 + /* Must at least be able to sign or encrypt (not neccesarily 1.58 + * both if it is one of a dual cert). 1.59 + */ 1.60 + if (!((cert->rawKeyUsage & KU_DIGITAL_SIGNATURE) || 1.61 + (cert->rawKeyUsage & KU_KEY_ENCIPHERMENT))) 1.62 + return NULL; 1.63 + 1.64 + /* CA cert, not personal email. */ 1.65 + if (cert->rawKeyUsage & (KU_KEY_CERT_SIGN | KU_CRL_SIGN)) 1.66 + return NULL; 1.67 + } 1.68 + 1.69 + if (cert->emailAddr && cert->emailAddr[0]) { 1.70 + email = PORT_Strdup(cert->emailAddr); 1.71 + } else { 1.72 + if (tmp1) 1.73 + tmp1 += 2; /* "E=" */ 1.74 + else 1.75 + tmp1 = tmp2 + 5; /* "MAIL=" */ 1.76 + len = strcspn(tmp1, ", "); 1.77 + email = (char*)PORT_Alloc(len+1); 1.78 + PORT_Strncpy(email, tmp1, len); 1.79 + email[len] = '\0'; 1.80 + } 1.81 + 1.82 + return email; 1.83 +} 1.84 + 1.85 +SECStatus 1.86 +deleteit(CERTCertificate *cert, void *arg) 1.87 +{ 1.88 + return SEC_DeletePermCertificate(cert); 1.89 +} 1.90 + 1.91 +/* Different than DeleteCertificate - has the added bonus of removing 1.92 + * all certs with the same DN. 1.93 + */ 1.94 +SECStatus 1.95 +deleteAllEntriesForCert(NSSLOWCERTCertDBHandle *handle, CERTCertificate *cert, 1.96 + PRFileDesc *outfile) 1.97 +{ 1.98 +#if 0 1.99 + certDBEntrySubject *subjectEntry; 1.100 + certDBEntryNickname *nicknameEntry; 1.101 + certDBEntrySMime *smimeEntry; 1.102 + int i; 1.103 +#endif 1.104 + 1.105 + if (outfile) { 1.106 + PR_fprintf(outfile, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n"); 1.107 + PR_fprintf(outfile, "Deleting redundant certificate:\n"); 1.108 + dumpCertificate(cert, -1, outfile); 1.109 + } 1.110 + 1.111 + CERT_TraverseCertsForSubject(handle, cert->subjectList, deleteit, NULL); 1.112 +#if 0 1.113 + CERT_LockDB(handle); 1.114 + subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject); 1.115 + /* It had better be there, or created a bad db. */ 1.116 + PORT_Assert(subjectEntry); 1.117 + for (i=0; i<subjectEntry->ncerts; i++) { 1.118 + DeleteDBCertEntry(handle, &subjectEntry->certKeys[i]); 1.119 + } 1.120 + DeleteDBSubjectEntry(handle, &cert->derSubject); 1.121 + if (subjectEntry->emailAddr && subjectEntry->emailAddr[0]) { 1.122 + smimeEntry = ReadDBSMimeEntry(handle, subjectEntry->emailAddr); 1.123 + if (smimeEntry) { 1.124 + if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject, 1.125 + &smimeEntry->subjectName)) 1.126 + /* Only delete it if it's for this subject! */ 1.127 + DeleteDBSMimeEntry(handle, subjectEntry->emailAddr); 1.128 + SEC_DestroyDBEntry((certDBEntry*)smimeEntry); 1.129 + } 1.130 + } 1.131 + if (subjectEntry->nickname) { 1.132 + nicknameEntry = ReadDBNicknameEntry(handle, subjectEntry->nickname); 1.133 + if (nicknameEntry) { 1.134 + if (SECITEM_ItemsAreEqual(&subjectEntry->derSubject, 1.135 + &nicknameEntry->subjectName)) 1.136 + /* Only delete it if it's for this subject! */ 1.137 + DeleteDBNicknameEntry(handle, subjectEntry->nickname); 1.138 + SEC_DestroyDBEntry((certDBEntry*)nicknameEntry); 1.139 + } 1.140 + } 1.141 + SEC_DestroyDBEntry((certDBEntry*)subjectEntry); 1.142 + CERT_UnlockDB(handle); 1.143 +#endif 1.144 + return SECSuccess; 1.145 +} 1.146 + 1.147 +void 1.148 +getCertsToDelete(char *numlist, int len, int *certNums, int nCerts) 1.149 +{ 1.150 + int j, num; 1.151 + char *numstr, *numend, *end; 1.152 + 1.153 + numstr = numlist; 1.154 + end = numstr + len - 1; 1.155 + while (numstr != end) { 1.156 + numend = strpbrk(numstr, ", \n"); 1.157 + *numend = '\0'; 1.158 + if (PORT_Strlen(numstr) == 0) 1.159 + return; 1.160 + num = PORT_Atoi(numstr); 1.161 + if (numstr == numlist) 1.162 + certNums[0] = num; 1.163 + for (j=1; j<nCerts+1; j++) { 1.164 + if (num == certNums[j]) { 1.165 + certNums[j] = -1; 1.166 + break; 1.167 + } 1.168 + } 1.169 + if (numend == end) 1.170 + break; 1.171 + numstr = strpbrk(numend+1, "0123456789"); 1.172 + } 1.173 +} 1.174 + 1.175 +PRBool 1.176 +userSaysDeleteCert(CERTCertificate **certs, int nCerts, 1.177 + int errtype, dbRestoreInfo *info, int *certNums) 1.178 +{ 1.179 + char response[32]; 1.180 + PRInt32 nb; 1.181 + int i; 1.182 + /* User wants to remove cert without prompting. */ 1.183 + if (info->promptUser[errtype] == PR_FALSE) 1.184 + return (info->removeType[errtype]); 1.185 + switch (errtype) { 1.186 + case dbInvalidCert: 1.187 + PR_fprintf(PR_STDOUT, "******** Expired ********\n"); 1.188 + PR_fprintf(PR_STDOUT, "Cert has expired.\n\n"); 1.189 + dumpCertificate(certs[0], -1, PR_STDOUT); 1.190 + PR_fprintf(PR_STDOUT, 1.191 + "Keep it? (y/n - this one, Y/N - all expired certs) [n] "); 1.192 + break; 1.193 + case dbNoSMimeProfile: 1.194 + PR_fprintf(PR_STDOUT, "******** No Profile ********\n"); 1.195 + PR_fprintf(PR_STDOUT, "S/MIME cert has no profile.\n\n"); 1.196 + dumpCertificate(certs[0], -1, PR_STDOUT); 1.197 + PR_fprintf(PR_STDOUT, 1.198 + "Keep it? (y/n - this one, Y/N - all S/MIME w/o profile) [n] "); 1.199 + break; 1.200 + case dbOlderCert: 1.201 + PR_fprintf(PR_STDOUT, "******* Redundant nickname/email *******\n\n"); 1.202 + PR_fprintf(PR_STDOUT, "These certs have the same nickname/email:\n"); 1.203 + for (i=0; i<nCerts; i++) 1.204 + dumpCertificate(certs[i], i, PR_STDOUT); 1.205 + PR_fprintf(PR_STDOUT, 1.206 + "Enter the certs you would like to keep from those listed above.\n"); 1.207 + PR_fprintf(PR_STDOUT, 1.208 + "Use a comma-separated list of the cert numbers (ex. 0, 8, 12).\n"); 1.209 + PR_fprintf(PR_STDOUT, 1.210 + "The first cert in the list will be the primary cert\n"); 1.211 + PR_fprintf(PR_STDOUT, 1.212 + " accessed by the nickname/email handle.\n"); 1.213 + PR_fprintf(PR_STDOUT, 1.214 + "List cert numbers to keep here, or hit enter\n"); 1.215 + PR_fprintf(PR_STDOUT, 1.216 + " to always keep only the newest cert: "); 1.217 + break; 1.218 + default: 1.219 + } 1.220 + nb = PR_Read(PR_STDIN, response, sizeof(response)); 1.221 + PR_fprintf(PR_STDOUT, "\n\n"); 1.222 + if (errtype == dbOlderCert) { 1.223 + if (!isdigit(response[0])) { 1.224 + info->promptUser[errtype] = PR_FALSE; 1.225 + info->removeType[errtype] = PR_TRUE; 1.226 + return PR_TRUE; 1.227 + } 1.228 + getCertsToDelete(response, nb, certNums, nCerts); 1.229 + return PR_TRUE; 1.230 + } 1.231 + /* User doesn't want to be prompted for this type anymore. */ 1.232 + if (response[0] == 'Y') { 1.233 + info->promptUser[errtype] = PR_FALSE; 1.234 + info->removeType[errtype] = PR_FALSE; 1.235 + return PR_FALSE; 1.236 + } else if (response[0] == 'N') { 1.237 + info->promptUser[errtype] = PR_FALSE; 1.238 + info->removeType[errtype] = PR_TRUE; 1.239 + return PR_TRUE; 1.240 + } 1.241 + return (response[0] != 'y') ? PR_TRUE : PR_FALSE; 1.242 +} 1.243 + 1.244 +SECStatus 1.245 +addCertToDB(certDBEntryCert *certEntry, dbRestoreInfo *info, 1.246 + NSSLOWCERTCertDBHandle *oldhandle) 1.247 +{ 1.248 + SECStatus rv = SECSuccess; 1.249 + PRBool allowOverride; 1.250 + PRBool userCert; 1.251 + SECCertTimeValidity validity; 1.252 + CERTCertificate *oldCert = NULL; 1.253 + CERTCertificate *dbCert = NULL; 1.254 + CERTCertificate *newCert = NULL; 1.255 + CERTCertTrust *trust; 1.256 + certDBEntrySMime *smimeEntry = NULL; 1.257 + char *email = NULL; 1.258 + char *nickname = NULL; 1.259 + int nCertsForSubject = 1; 1.260 + 1.261 + oldCert = CERT_DecodeDERCertificate(&certEntry->derCert, PR_FALSE, 1.262 + certEntry->nickname); 1.263 + if (!oldCert) { 1.264 + info->dbErrors[dbBadCertificate]++; 1.265 + SEC_DestroyDBEntry((certDBEntry*)certEntry); 1.266 + return SECSuccess; 1.267 + } 1.268 + 1.269 + oldCert->dbEntry = certEntry; 1.270 + oldCert->trust = &certEntry->trust; 1.271 + oldCert->dbhandle = oldhandle; 1.272 + 1.273 + trust = oldCert->trust; 1.274 + 1.275 + info->nOldCerts++; 1.276 + 1.277 + if (info->verbose) 1.278 + PR_fprintf(info->out, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n"); 1.279 + 1.280 + if (oldCert->nickname) 1.281 + nickname = PORT_Strdup(oldCert->nickname); 1.282 + 1.283 + /* Always keep user certs. Skip ahead. */ 1.284 + /* XXX if someone sends themselves a signed message, it is possible 1.285 + for their cert to be imported as an "other" cert, not a user cert. 1.286 + this mucks with smime entries... */ 1.287 + userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) || 1.288 + (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) || 1.289 + (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER); 1.290 + if (userCert) 1.291 + goto createcert; 1.292 + 1.293 + /* If user chooses so, ignore expired certificates. */ 1.294 + allowOverride = (PRBool)((oldCert->keyUsage == certUsageSSLServer) || 1.295 + (oldCert->keyUsage == certUsageSSLServerWithStepUp)); 1.296 + validity = CERT_CheckCertValidTimes(oldCert, PR_Now(), allowOverride); 1.297 + /* If cert expired and user wants to delete it, ignore it. */ 1.298 + if ((validity != secCertTimeValid) && 1.299 + userSaysDeleteCert(&oldCert, 1, dbInvalidCert, info, 0)) { 1.300 + info->dbErrors[dbInvalidCert]++; 1.301 + if (info->verbose) { 1.302 + PR_fprintf(info->out, "Deleting expired certificate:\n"); 1.303 + dumpCertificate(oldCert, -1, info->out); 1.304 + } 1.305 + goto cleanup; 1.306 + } 1.307 + 1.308 + /* New database will already have default certs, don't attempt 1.309 + to overwrite them. */ 1.310 + dbCert = CERT_FindCertByDERCert(info->handle, &oldCert->derCert); 1.311 + if (dbCert) { 1.312 + info->nCerts++; 1.313 + if (info->verbose) { 1.314 + PR_fprintf(info->out, "Added certificate to database:\n"); 1.315 + dumpCertificate(oldCert, -1, info->out); 1.316 + } 1.317 + goto cleanup; 1.318 + } 1.319 + 1.320 + /* Determine if cert is S/MIME and get its email if so. */ 1.321 + email = IsEmailCert(oldCert); 1.322 + 1.323 + /* 1.324 + XXX Just create empty profiles? 1.325 + if (email) { 1.326 + SECItem *profile = CERT_FindSMimeProfile(oldCert); 1.327 + if (!profile && 1.328 + userSaysDeleteCert(&oldCert, 1, dbNoSMimeProfile, info, 0)) { 1.329 + info->dbErrors[dbNoSMimeProfile]++; 1.330 + if (info->verbose) { 1.331 + PR_fprintf(info->out, 1.332 + "Deleted cert missing S/MIME profile.\n"); 1.333 + dumpCertificate(oldCert, -1, info->out); 1.334 + } 1.335 + goto cleanup; 1.336 + } else { 1.337 + SECITEM_FreeItem(profile); 1.338 + } 1.339 + } 1.340 + */ 1.341 + 1.342 +createcert: 1.343 + 1.344 + /* Sometimes happens... */ 1.345 + if (!nickname && userCert) 1.346 + nickname = PORT_Strdup(oldCert->subjectName); 1.347 + 1.348 + /* Create a new certificate, copy of the old one. */ 1.349 + newCert = CERT_NewTempCertificate(info->handle, &oldCert->derCert, 1.350 + nickname, PR_FALSE, PR_TRUE); 1.351 + if (!newCert) { 1.352 + PR_fprintf(PR_STDERR, "Unable to create new certificate.\n"); 1.353 + dumpCertificate(oldCert, -1, PR_STDERR); 1.354 + info->dbErrors[dbBadCertificate]++; 1.355 + goto cleanup; 1.356 + } 1.357 + 1.358 + /* Add the cert to the new database. */ 1.359 + rv = CERT_AddTempCertToPerm(newCert, nickname, oldCert->trust); 1.360 + if (rv) { 1.361 + PR_fprintf(PR_STDERR, "Failed to write temp cert to perm database.\n"); 1.362 + dumpCertificate(oldCert, -1, PR_STDERR); 1.363 + info->dbErrors[dbCertNotWrittenToDB]++; 1.364 + goto cleanup; 1.365 + } 1.366 + 1.367 + if (info->verbose) { 1.368 + PR_fprintf(info->out, "Added certificate to database:\n"); 1.369 + dumpCertificate(oldCert, -1, info->out); 1.370 + } 1.371 + 1.372 + /* If the cert is an S/MIME cert, and the first with it's subject, 1.373 + * modify the subject entry to include the email address, 1.374 + * CERT_AddTempCertToPerm does not do email addresses and S/MIME entries. 1.375 + */ 1.376 + if (smimeEntry) { /*&& !userCert && nCertsForSubject == 1) { */ 1.377 +#if 0 1.378 + UpdateSubjectWithEmailAddr(newCert, email); 1.379 +#endif 1.380 + SECItem emailProfile, profileTime; 1.381 + rv = CERT_FindFullSMimeProfile(oldCert, &emailProfile, &profileTime); 1.382 + /* calls UpdateSubjectWithEmailAddr */ 1.383 + if (rv == SECSuccess) 1.384 + rv = CERT_SaveSMimeProfile(newCert, &emailProfile, &profileTime); 1.385 + } 1.386 + 1.387 + info->nCerts++; 1.388 + 1.389 +cleanup: 1.390 + 1.391 + if (nickname) 1.392 + PORT_Free(nickname); 1.393 + if (email) 1.394 + PORT_Free(email); 1.395 + if (oldCert) 1.396 + CERT_DestroyCertificate(oldCert); 1.397 + if (dbCert) 1.398 + CERT_DestroyCertificate(dbCert); 1.399 + if (newCert) 1.400 + CERT_DestroyCertificate(newCert); 1.401 + if (smimeEntry) 1.402 + SEC_DestroyDBEntry((certDBEntry*)smimeEntry); 1.403 + return SECSuccess; 1.404 +} 1.405 + 1.406 +#if 0 1.407 +SECStatus 1.408 +copyDBEntry(SECItem *data, SECItem *key, certDBEntryType type, void *pdata) 1.409 +{ 1.410 + SECStatus rv; 1.411 + NSSLOWCERTCertDBHandle *newdb = (NSSLOWCERTCertDBHandle *)pdata; 1.412 + certDBEntryCommon common; 1.413 + SECItem dbkey; 1.414 + 1.415 + common.type = type; 1.416 + common.version = CERT_DB_FILE_VERSION; 1.417 + common.flags = data->data[2]; 1.418 + common.arena = NULL; 1.419 + 1.420 + dbkey.len = key->len + SEC_DB_KEY_HEADER_LEN; 1.421 + dbkey.data = (unsigned char *)PORT_Alloc(dbkey.len*sizeof(unsigned char)); 1.422 + PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], key->data, key->len); 1.423 + dbkey.data[0] = type; 1.424 + 1.425 + rv = WriteDBEntry(newdb, &common, &dbkey, data); 1.426 + 1.427 + PORT_Free(dbkey.data); 1.428 + return rv; 1.429 +} 1.430 +#endif 1.431 + 1.432 +int 1.433 +certIsOlder(CERTCertificate **cert1, CERTCertificate** cert2) 1.434 +{ 1.435 + return !CERT_IsNewer(*cert1, *cert2); 1.436 +} 1.437 + 1.438 +int 1.439 +findNewestSubjectForEmail(NSSLOWCERTCertDBHandle *handle, int subjectNum, 1.440 + certDBArray *dbArray, dbRestoreInfo *info, 1.441 + int *subjectWithSMime, int *smimeForSubject) 1.442 +{ 1.443 + int newestSubject; 1.444 + int subjectsForEmail[50]; 1.445 + int i, j, ns, sNum; 1.446 + certDBEntryListNode *subjects = &dbArray->subjects; 1.447 + certDBEntryListNode *smime = &dbArray->smime; 1.448 + certDBEntrySubject *subjectEntry1, *subjectEntry2; 1.449 + certDBEntrySMime *smimeEntry; 1.450 + CERTCertificate **certs; 1.451 + CERTCertificate *cert; 1.452 + CERTCertTrust *trust; 1.453 + PRBool userCert; 1.454 + int *certNums; 1.455 + 1.456 + ns = 0; 1.457 + subjectEntry1 = (certDBEntrySubject*)&subjects.entries[subjectNum]; 1.458 + subjectsForEmail[ns++] = subjectNum; 1.459 + 1.460 + *subjectWithSMime = -1; 1.461 + *smimeForSubject = -1; 1.462 + newestSubject = subjectNum; 1.463 + 1.464 + cert = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]); 1.465 + if (cert) { 1.466 + trust = cert->trust; 1.467 + userCert = (SEC_GET_TRUST_FLAGS(trust, trustSSL) & CERTDB_USER) || 1.468 + (SEC_GET_TRUST_FLAGS(trust, trustEmail) & CERTDB_USER) || 1.469 + (SEC_GET_TRUST_FLAGS(trust, trustObjectSigning) & CERTDB_USER); 1.470 + CERT_DestroyCertificate(cert); 1.471 + } 1.472 + 1.473 + /* 1.474 + * XXX Should we make sure that subjectEntry1->emailAddr is not 1.475 + * a null pointer or an empty string before going into the next 1.476 + * two for loops, which pass it to PORT_Strcmp? 1.477 + */ 1.478 + 1.479 + /* Loop over the remaining subjects. */ 1.480 + for (i=subjectNum+1; i<subjects.numEntries; i++) { 1.481 + subjectEntry2 = (certDBEntrySubject*)&subjects.entries[i]; 1.482 + if (!subjectEntry2) 1.483 + continue; 1.484 + if (subjectEntry2->emailAddr && subjectEntry2->emailAddr[0] && 1.485 + PORT_Strcmp(subjectEntry1->emailAddr, 1.486 + subjectEntry2->emailAddr) == 0) { 1.487 + /* Found a subject using the same email address. */ 1.488 + subjectsForEmail[ns++] = i; 1.489 + } 1.490 + } 1.491 + 1.492 + /* Find the S/MIME entry for this email address. */ 1.493 + for (i=0; i<smime.numEntries; i++) { 1.494 + smimeEntry = (certDBEntrySMime*)&smime.entries[i]; 1.495 + if (smimeEntry->common.arena == NULL) 1.496 + continue; 1.497 + if (smimeEntry->emailAddr && smimeEntry->emailAddr[0] && 1.498 + PORT_Strcmp(subjectEntry1->emailAddr, smimeEntry->emailAddr) == 0) { 1.499 + /* Find which of the subjects uses this S/MIME entry. */ 1.500 + for (j=0; j<ns && *subjectWithSMime < 0; j++) { 1.501 + sNum = subjectsForEmail[j]; 1.502 + subjectEntry2 = (certDBEntrySubject*)&subjects.entries[sNum]; 1.503 + if (SECITEM_ItemsAreEqual(&smimeEntry->subjectName, 1.504 + &subjectEntry2->derSubject)) { 1.505 + /* Found the subject corresponding to the S/MIME entry. */ 1.506 + *subjectWithSMime = sNum; 1.507 + *smimeForSubject = i; 1.508 + } 1.509 + } 1.510 + SEC_DestroyDBEntry((certDBEntry*)smimeEntry); 1.511 + PORT_Memset(smimeEntry, 0, sizeof(certDBEntry)); 1.512 + break; 1.513 + } 1.514 + } 1.515 + 1.516 + if (ns <= 1) 1.517 + return subjectNum; 1.518 + 1.519 + if (userCert) 1.520 + return *subjectWithSMime; 1.521 + 1.522 + /* Now find which of the subjects has the newest cert. */ 1.523 + certs = (CERTCertificate**)PORT_Alloc(ns*sizeof(CERTCertificate*)); 1.524 + certNums = (int*)PORT_Alloc((ns+1)*sizeof(int)); 1.525 + certNums[0] = 0; 1.526 + for (i=0; i<ns; i++) { 1.527 + sNum = subjectsForEmail[i]; 1.528 + subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum]; 1.529 + certs[i] = CERT_FindCertByKey(handle, &subjectEntry1->certKeys[0]); 1.530 + certNums[i+1] = i; 1.531 + } 1.532 + /* Sort the array by validity. */ 1.533 + qsort(certs, ns, sizeof(CERTCertificate*), 1.534 + (int (*)(const void *, const void *))certIsOlder); 1.535 + newestSubject = -1; 1.536 + for (i=0; i<ns; i++) { 1.537 + sNum = subjectsForEmail[i]; 1.538 + subjectEntry1 = (certDBEntrySubject*)&subjects.entries[sNum]; 1.539 + if (SECITEM_ItemsAreEqual(&subjectEntry1->derSubject, 1.540 + &certs[0]->derSubject)) 1.541 + newestSubject = sNum; 1.542 + else 1.543 + SEC_DestroyDBEntry((certDBEntry*)subjectEntry1); 1.544 + } 1.545 + if (info && userSaysDeleteCert(certs, ns, dbOlderCert, info, certNums)) { 1.546 + for (i=1; i<ns+1; i++) { 1.547 + if (certNums[i] >= 0 && certNums[i] != certNums[0]) { 1.548 + deleteAllEntriesForCert(handle, certs[certNums[i]], info->out); 1.549 + info->dbErrors[dbOlderCert]++; 1.550 + } 1.551 + } 1.552 + } 1.553 + CERT_DestroyCertArray(certs, ns); 1.554 + return newestSubject; 1.555 +} 1.556 + 1.557 +NSSLOWCERTCertDBHandle * 1.558 +DBCK_ReconstructDBFromCerts(NSSLOWCERTCertDBHandle *oldhandle, char *newdbname, 1.559 + PRFileDesc *outfile, PRBool removeExpired, 1.560 + PRBool requireProfile, PRBool singleEntry, 1.561 + PRBool promptUser) 1.562 +{ 1.563 + SECStatus rv; 1.564 + dbRestoreInfo info; 1.565 + certDBEntryContentVersion *oldContentVersion; 1.566 + certDBArray dbArray; 1.567 + int i; 1.568 + 1.569 + PORT_Memset(&dbArray, 0, sizeof(dbArray)); 1.570 + PORT_Memset(&info, 0, sizeof(info)); 1.571 + info.verbose = (outfile) ? PR_TRUE : PR_FALSE; 1.572 + info.out = (outfile) ? outfile : PR_STDOUT; 1.573 + info.removeType[dbInvalidCert] = removeExpired; 1.574 + info.removeType[dbNoSMimeProfile] = requireProfile; 1.575 + info.removeType[dbOlderCert] = singleEntry; 1.576 + info.promptUser[dbInvalidCert] = promptUser; 1.577 + info.promptUser[dbNoSMimeProfile] = promptUser; 1.578 + info.promptUser[dbOlderCert] = promptUser; 1.579 + 1.580 + /* Allocate a handle to fill with CERT_OpenCertDB below. */ 1.581 + info.handle = PORT_ZNew(NSSLOWCERTCertDBHandle); 1.582 + if (!info.handle) { 1.583 + fprintf(stderr, "unable to get database handle"); 1.584 + return NULL; 1.585 + } 1.586 + 1.587 + /* Create a certdb with the most recent set of roots. */ 1.588 + rv = CERT_OpenCertDBFilename(info.handle, newdbname, PR_FALSE); 1.589 + 1.590 + if (rv) { 1.591 + fprintf(stderr, "could not open certificate database"); 1.592 + goto loser; 1.593 + } 1.594 + 1.595 + /* Create certificate, subject, nickname, and email records. 1.596 + * mcom_db seems to have a sequential access bug. Though reads and writes 1.597 + * should be allowed during traversal, they seem to screw up the sequence. 1.598 + * So, stuff all the cert entries into an array, and loop over the array 1.599 + * doing read/writes in the db. 1.600 + */ 1.601 + fillDBEntryArray(oldhandle, certDBEntryTypeCert, &dbArray.certs); 1.602 + for (elem = PR_LIST_HEAD(&dbArray->certs.link); 1.603 + elem != &dbArray->certs.link; elem = PR_NEXT_LINK(elem)) { 1.604 + node = LISTNODE_CAST(elem); 1.605 + addCertToDB((certDBEntryCert*)&node->entry, &info, oldhandle); 1.606 + /* entries get destroyed in addCertToDB */ 1.607 + } 1.608 +#if 0 1.609 + rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeSMimeProfile, 1.610 + copyDBEntry, info.handle); 1.611 +#endif 1.612 + 1.613 + /* Fix up the pointers between (nickname|S/MIME) --> (subject). 1.614 + * Create S/MIME entries for S/MIME certs. 1.615 + * Have the S/MIME entry point to the last-expiring cert using 1.616 + * an email address. 1.617 + */ 1.618 +#if 0 1.619 + CERT_RedoHandlesForSubjects(info.handle, singleEntry, &info); 1.620 +#endif 1.621 + 1.622 + freeDBEntryList(&dbArray.certs.link); 1.623 + 1.624 + /* Copy over the version record. */ 1.625 + /* XXX Already exists - and _must_ be correct... */ 1.626 + /* 1.627 + versionEntry = ReadDBVersionEntry(oldhandle); 1.628 + rv = WriteDBVersionEntry(info.handle, versionEntry); 1.629 + */ 1.630 + 1.631 + /* Copy over the content version record. */ 1.632 + /* XXX Can probably get useful info from old content version? 1.633 + * Was this db created before/after this tool? etc. 1.634 + */ 1.635 +#if 0 1.636 + oldContentVersion = ReadDBContentVersionEntry(oldhandle); 1.637 + CERT_SetDBContentVersion(oldContentVersion->contentVersion, info.handle); 1.638 +#endif 1.639 + 1.640 +#if 0 1.641 + /* Copy over the CRL & KRL records. */ 1.642 + rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeRevocation, 1.643 + copyDBEntry, info.handle); 1.644 + /* XXX Only one KRL, just do db->get? */ 1.645 + rv = nsslowcert_TraverseDBEntries(oldhandle, certDBEntryTypeKeyRevocation, 1.646 + copyDBEntry, info.handle); 1.647 +#endif 1.648 + 1.649 + PR_fprintf(info.out, "Database had %d certificates.\n", info.nOldCerts); 1.650 + 1.651 + PR_fprintf(info.out, "Reconstructed %d certificates.\n", info.nCerts); 1.652 + PR_fprintf(info.out, "(ax) Rejected %d expired certificates.\n", 1.653 + info.dbErrors[dbInvalidCert]); 1.654 + PR_fprintf(info.out, "(as) Rejected %d S/MIME certificates missing a profile.\n", 1.655 + info.dbErrors[dbNoSMimeProfile]); 1.656 + PR_fprintf(info.out, "(ar) Rejected %d certificates for which a newer certificate was found.\n", 1.657 + info.dbErrors[dbOlderCert]); 1.658 + PR_fprintf(info.out, " Rejected %d corrupt certificates.\n", 1.659 + info.dbErrors[dbBadCertificate]); 1.660 + PR_fprintf(info.out, " Rejected %d certificates which did not write to the DB.\n", 1.661 + info.dbErrors[dbCertNotWrittenToDB]); 1.662 + 1.663 + if (rv) 1.664 + goto loser; 1.665 + 1.666 + return info.handle; 1.667 + 1.668 +loser: 1.669 + if (info.handle) 1.670 + PORT_Free(info.handle); 1.671 + return NULL; 1.672 +} 1.673 +