security/nss/cmd/dbck/dbrecover.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial