1017 lines
34 KiB
Diff
1017 lines
34 KiB
Diff
From 3da0e186904ad81dd87cf74bfae88270f14bb770 Mon Sep 17 00:00:00 2001
|
|
From: Rob Crittenden <rcritten@redhat.com>
|
|
Date: Tue, 21 Aug 2018 17:25:21 -0400
|
|
Subject: [PATCH 1/7] Use the correct slot when saving certificates in NSS
|
|
|
|
Certificates were always stored in the NSS certdb.
|
|
---
|
|
src/certsave-n.c | 915 ++++++++++++++++++++++++++++---------------------------
|
|
1 file changed, 474 insertions(+), 441 deletions(-)
|
|
|
|
diff --git a/src/certsave-n.c b/src/certsave-n.c
|
|
index 8e15a18a..af176ce5 100644
|
|
--- a/src/certsave-n.c
|
|
+++ b/src/certsave-n.c
|
|
@@ -92,7 +92,11 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry,
|
|
SECStatus error;
|
|
SECItem *item, subject;
|
|
char *p, *q, *pin;
|
|
+ const char *token;
|
|
const char *es;
|
|
+ PK11SlotList *slotlist;
|
|
+ PK11SlotListElement *sle;
|
|
+ CK_MECHANISM_TYPE mech;
|
|
NSSInitContext *ctx;
|
|
CERTCertDBHandle *certdb;
|
|
CERTCertList *certlist;
|
|
@@ -192,231 +196,253 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry,
|
|
}
|
|
_exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
}
|
|
- /* Be ready to count our uses of a PIN. */
|
|
- memset(&cb_data, 0, sizeof(cb_data));
|
|
- cb_data.entry = entry;
|
|
- cb_data.n_attempts = 0;
|
|
- pin = NULL;
|
|
- if (cm_pin_read_for_key(entry, &pin) != 0) {
|
|
- cm_log(1, "Error reading PIN for key store, "
|
|
- "failing to save certificate.\n");
|
|
+ /* Find the tokens that we might use for cert storage. */
|
|
+ mech = CKM_RSA_X_509;
|
|
+ slotlist = PK11_GetAllTokens(mech, PR_FALSE, PR_FALSE, NULL);
|
|
+ if (slotlist == NULL) {
|
|
+ cm_log(1, "Error getting list of tokens.\n");
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
- error = NSS_ShutdownContext(ctx);
|
|
- if (error != SECSuccess) {
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
cm_log(1, "Error shutting down NSS.\n");
|
|
}
|
|
- _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
+ _exit(2);
|
|
}
|
|
- /* Set a PIN if we're supposed to be using one and aren't using
|
|
- * one yet in this database. */
|
|
- if (PK11_NeedUserInit(PK11_GetInternalKeySlot())) {
|
|
- PK11_InitPin(PK11_GetInternalKeySlot(), NULL,
|
|
- pin ? pin : "");
|
|
- ec = PORT_GetError();
|
|
- if (ec != 0) {
|
|
- es = PR_ErrorToName(ec);
|
|
+ /* Walk the list looking for the requested slot, or the first one if
|
|
+ * none was requested. */
|
|
+ if (cm_pin_read_for_cert(entry, &pin) != 0) {
|
|
+ cm_log(1, "Error reading PIN for cert db.\n");
|
|
+ _exit(CM_SUB_STATUS_ERROR_AUTH);
|
|
+ }
|
|
+ PK11_SetPasswordFunc(&cm_pin_read_for_cert_nss_cb);
|
|
+ for (sle = slotlist->head;
|
|
+ ((sle != NULL) && (sle->slot != NULL));
|
|
+ sle = sle->next)
|
|
+ {
|
|
+ /* Log the slot's name. */
|
|
+ token = PK11_GetTokenName(sle->slot);
|
|
+ if (token != NULL) {
|
|
+ cm_log(3, "Found token '%s'.\n", token);
|
|
} else {
|
|
- es = NULL;
|
|
+ cm_log(3, "Found unnamed token.\n");
|
|
}
|
|
- if (PK11_NeedUserInit(PK11_GetInternalKeySlot())) {
|
|
- if (es != NULL) {
|
|
- cm_log(1, "Key storage slot still "
|
|
- "needs user PIN to be set: "
|
|
- "%s.\n", es);
|
|
- } else {
|
|
- cm_log(1, "Key storage slot still "
|
|
- "needs user PIN to be set.\n");
|
|
- }
|
|
+ /* If we're looking for a specific slot, and this isn't it,
|
|
+ * keep going. */
|
|
+ if ((entry->cm_cert_token != NULL) &&
|
|
+ ((token == NULL) ||
|
|
+ (strcmp(entry->cm_cert_token, token) != 0))) {
|
|
+ if (token != NULL) {
|
|
+ cm_log(1,
|
|
+ "Token is named \"%s\", not \"%s\", "
|
|
+ "skipping.\n",
|
|
+ token, entry->cm_cert_token);
|
|
+ } else {
|
|
+ cm_log(1,
|
|
+ "Token is unnamed, not \"%s\", "
|
|
+ "skipping.\n",
|
|
+ entry->cm_cert_token);
|
|
+ }
|
|
+ goto next_slot;
|
|
+ }
|
|
+ /* Be ready to count our uses of a PIN. */
|
|
+ memset(&cb_data, 0, sizeof(cb_data));
|
|
+ cb_data.entry = entry;
|
|
+ cb_data.n_attempts = 0;
|
|
+ pin = NULL;
|
|
+ if (cm_pin_read_for_key(entry, &pin) != 0) {
|
|
+ cm_log(1, "Error reading PIN for key store, "
|
|
+ "failing to save certificate.\n");
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
error = NSS_ShutdownContext(ctx);
|
|
if (error != SECSuccess) {
|
|
cm_log(1, "Error shutting down NSS.\n");
|
|
}
|
|
- switch (ec) {
|
|
- case PR_NO_ACCESS_RIGHTS_ERROR: /* EACCES or EPERM */
|
|
- _exit(CM_CERTSAVE_STATUS_PERMS);
|
|
- break;
|
|
- default:
|
|
- _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
- break;
|
|
- }
|
|
+ _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
}
|
|
- /* We're authenticated now, so count this as a use of
|
|
- * the PIN. */
|
|
- if ((pin != NULL) && (strlen(pin) > 0)) {
|
|
- cb_data.n_attempts++;
|
|
- }
|
|
- }
|
|
- /* Log in, if case we need to muck around with the key
|
|
- * database. */
|
|
- PK11_SetPasswordFunc(&cm_pin_read_for_key_nss_cb);
|
|
- error = PK11_Authenticate(PK11_GetInternalKeySlot(), PR_TRUE,
|
|
- &cb_data);
|
|
- ec = PORT_GetError();
|
|
- if (error != SECSuccess) {
|
|
- if (ec != 0) {
|
|
+ if (PK11_NeedUserInit(sle->slot)) {
|
|
+ PK11_InitPin(sle->slot, NULL, pin ? pin : "");
|
|
+ ec = PORT_GetError();
|
|
es = PR_ErrorToName(ec);
|
|
- } else {
|
|
- es = NULL;
|
|
- }
|
|
- if (es != NULL) {
|
|
- cm_log(1, "Error authenticating to key store: %s.\n",
|
|
- es);
|
|
- } else {
|
|
- cm_log(1, "Error authenticating to key store.\n");
|
|
- }
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- error = NSS_ShutdownContext(ctx);
|
|
- if (error != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
- }
|
|
- _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
- }
|
|
- if ((pin != NULL) &&
|
|
- (strlen(pin) > 0) &&
|
|
- (cb_data.n_attempts == 0)) {
|
|
- cm_log(1, "PIN was not needed to auth to key "
|
|
- "store, though one was provided. "
|
|
- "Treating this as an error.\n");
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- error = NSS_ShutdownContext(ctx);
|
|
- if (error != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
- }
|
|
- _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
- }
|
|
- certdb = CERT_GetDefaultCertDB();
|
|
- if (certdb != NULL) {
|
|
- /* Strip the header and footer. */
|
|
- p = entry->cm_cert;
|
|
- q = NULL;
|
|
- if (p != NULL) {
|
|
- while (strncmp(p, "-----BEGIN ", 11) == 0) {
|
|
- p += strcspn(p, "\r\n");
|
|
- p += strspn(p, "\r\n");
|
|
+ if (PK11_NeedUserInit(sle->slot)) {
|
|
+ if (es != NULL) {
|
|
+ cm_log(1, "Key storage slot still "
|
|
+ "needs user PIN to be set: "
|
|
+ "%s.\n", es);
|
|
+ } else {
|
|
+ cm_log(1, "Key storage slot still "
|
|
+ "needs user PIN to be set.\n");
|
|
+ }
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ error = NSS_ShutdownContext(ctx);
|
|
+ if (error != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ switch (ec) {
|
|
+ case PR_NO_ACCESS_RIGHTS_ERROR: /* EACCES or EPERM */
|
|
+ _exit(CM_CERTSAVE_STATUS_PERMS);
|
|
+ break;
|
|
+ default:
|
|
+ _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
+ break;
|
|
+ }
|
|
}
|
|
- q = strstr(p, "-----END");
|
|
+ /* count this as use of the PIN */
|
|
+ cb_data.n_attempts++;
|
|
}
|
|
- if ((q == NULL) || (*p == '\0')) {
|
|
- cm_log(1, "Unable to parse certificate.\n");
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
+ if (PK11_NeedLogin(sle->slot)) {
|
|
+ error = PK11_Authenticate(sle->slot, PR_TRUE, &cb_data);
|
|
+ if (error != SECSuccess) {
|
|
+ cm_log(1, "Error authenticating to cert db for token "
|
|
+ "%s.\n", token);
|
|
+ goto next_slot;
|
|
}
|
|
- _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
+ cb_data.n_attempts++;
|
|
}
|
|
- /* Handle the base64 decode. */
|
|
- item = NSSBase64_DecodeBuffer(arena, NULL, p, q - p);
|
|
- if (item == NULL) {
|
|
- cm_log(1, "Unable to decode certificate "
|
|
- "into buffer.\n");
|
|
+ if ((pin != NULL) &&
|
|
+ (strlen(pin) > 0) &&
|
|
+ (cb_data.n_attempts == 0)) {
|
|
+ cm_log(1, "PIN was not needed to auth to key "
|
|
+ "store, though one was provided. "
|
|
+ "Treating this as an error.\n");
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
- if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ error = NSS_ShutdownContext(ctx);
|
|
+ if (error != SECSuccess) {
|
|
cm_log(1, "Error shutting down NSS.\n");
|
|
}
|
|
- _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
+ _exit(CM_CERTSAVE_STATUS_AUTH);
|
|
}
|
|
- /* Do a "shallow" decode to pull out the subject name
|
|
- * so that we can check for a conflict. */
|
|
- memset(&csdata, 0, sizeof(csdata));
|
|
- if (SEC_ASN1DecodeItem(arena, &csdata,
|
|
- CERT_SignedDataTemplate,
|
|
- item) != SECSuccess) {
|
|
- cm_log(1, "Unable to decode certificate "
|
|
- "signed data into buffer.\n");
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
+ certdb = CERT_GetDefaultCertDB();
|
|
+ if (certdb != NULL) {
|
|
+ /* Strip the header and footer. */
|
|
+ p = entry->cm_cert;
|
|
+ q = NULL;
|
|
+ if (p != NULL) {
|
|
+ while (strncmp(p, "-----BEGIN ", 11) == 0) {
|
|
+ p += strcspn(p, "\r\n");
|
|
+ p += strspn(p, "\r\n");
|
|
+ }
|
|
+ q = strstr(p, "-----END");
|
|
}
|
|
- _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
- }
|
|
- memset(&cert, 0, sizeof(cert));
|
|
- if (SEC_ASN1DecodeItem(arena, &cert,
|
|
- CERT_CertificateTemplate,
|
|
- &csdata.data) != SECSuccess) {
|
|
- cm_log(1, "Unable to decode certificate "
|
|
- "data into buffer.\n");
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
+ if ((q == NULL) || (*p == '\0')) {
|
|
+ cm_log(1, "Unable to parse certificate.\n");
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
}
|
|
- _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
- }
|
|
- subject = cert.derSubject;
|
|
- /* Ask NSS if there would be a conflict. */
|
|
- have_trust = PR_FALSE;
|
|
- if (SEC_CertNicknameConflict(entry->cm_cert_nickname,
|
|
- &subject,
|
|
- certdb)) {
|
|
- /* Delete the certificate that's already there
|
|
- * with the nickname we want, otherwise our
|
|
- * cert with a different subject name will be
|
|
- * discarded. */
|
|
- certlist = PK11_FindCertsFromNickname(entry->cm_cert_nickname,
|
|
- NULL);
|
|
+ /* Handle the base64 decode. */
|
|
+ item = NSSBase64_DecodeBuffer(arena, NULL, p, q - p);
|
|
+ if (item == NULL) {
|
|
+ cm_log(1, "Unable to decode certificate "
|
|
+ "into buffer.\n");
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
+ }
|
|
+ /* Do a "shallow" decode to pull out the subject name
|
|
+ * so that we can check for a conflict. */
|
|
+ memset(&csdata, 0, sizeof(csdata));
|
|
+ if (SEC_ASN1DecodeItem(arena, &csdata,
|
|
+ CERT_SignedDataTemplate,
|
|
+ item) != SECSuccess) {
|
|
+ cm_log(1, "Unable to decode certificate "
|
|
+ "signed data into buffer.\n");
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
+ }
|
|
+ memset(&cert, 0, sizeof(cert));
|
|
+ if (SEC_ASN1DecodeItem(arena, &cert,
|
|
+ CERT_CertificateTemplate,
|
|
+ &csdata.data) != SECSuccess) {
|
|
+ cm_log(1, "Unable to decode certificate "
|
|
+ "data into buffer.\n");
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR);
|
|
+ }
|
|
+ subject = cert.derSubject;
|
|
+ /* Ask NSS if there would be a conflict. */
|
|
+ have_trust = PR_FALSE;
|
|
+ if (SEC_CertNicknameConflict(entry->cm_cert_nickname,
|
|
+ &subject,
|
|
+ certdb)) {
|
|
+ /* Delete the certificate that's already there
|
|
+ * with the nickname we want, otherwise our
|
|
+ * cert with a different subject name will be
|
|
+ * discarded. */
|
|
+ certlist = PK11_FindCertsFromNickname(entry->cm_cert_nickname,
|
|
+ NULL);
|
|
+ if (certlist != NULL) {
|
|
+ /* Look for certs with different
|
|
+ * subject names but the same nickname,
|
|
+ * because they've got to go. */
|
|
+ for (node = CERT_LIST_HEAD(certlist);
|
|
+ (node != NULL) &&
|
|
+ !CERT_LIST_EMPTY(certlist) &&
|
|
+ !CERT_LIST_END(node, certlist);
|
|
+ node = CERT_LIST_NEXT(node)) {
|
|
+ if (!SECITEM_ItemsAreEqual(&subject,
|
|
+ &node->cert->derSubject)) {
|
|
+ cm_log(3, "Found a "
|
|
+ "certificate "
|
|
+ "with the same "
|
|
+ "nickname but "
|
|
+ "different "
|
|
+ "subject, "
|
|
+ "removing "
|
|
+ "certificate "
|
|
+ "\"%s\" with "
|
|
+ "subject "
|
|
+ "\"%s\".\n",
|
|
+ node->cert->nickname,
|
|
+ node->cert->subjectName ?
|
|
+ node->cert->subjectName :
|
|
+ "");
|
|
+ /* Get a handle for
|
|
+ * this certificate's
|
|
+ * private key, in case
|
|
+ * we need to remove
|
|
+ * it. */
|
|
+ privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
+ privkeys = add_privkey_to_list(privkeys, privkey);
|
|
+ SEC_DeletePermCertificate(node->cert);
|
|
+ }
|
|
+ }
|
|
+ CERT_DestroyCertList(certlist);
|
|
+ }
|
|
+ } else {
|
|
+ cm_log(3, "No duplicate nickname entries.\n");
|
|
+ }
|
|
+ /* This certificate's subject may already be present
|
|
+ * with a different nickname. Delete those, too. */
|
|
+ certlist = CERT_CreateSubjectCertList(NULL, certdb,
|
|
+ &subject,
|
|
+ PR_FALSE,
|
|
+ PR_FALSE);
|
|
if (certlist != NULL) {
|
|
- /* Look for certs with different
|
|
- * subject names but the same nickname,
|
|
- * because they've got to go. */
|
|
+ /* Look for certs with different nicknames but
|
|
+ * the same subject name, because those have
|
|
+ * got to go. */
|
|
+ i = 0;
|
|
for (node = CERT_LIST_HEAD(certlist);
|
|
(node != NULL) &&
|
|
!CERT_LIST_EMPTY(certlist) &&
|
|
!CERT_LIST_END(node, certlist);
|
|
node = CERT_LIST_NEXT(node)) {
|
|
- if (!SECITEM_ItemsAreEqual(&subject,
|
|
- &node->cert->derSubject)) {
|
|
+ if ((node->cert->nickname != NULL) &&
|
|
+ (strcmp(entry->cm_cert_nickname,
|
|
+ node->cert->nickname) != 0))
|
|
+ {
|
|
+ i++;
|
|
cm_log(3, "Found a "
|
|
- "certificate "
|
|
- "with the same "
|
|
- "nickname but "
|
|
- "different "
|
|
- "subject, "
|
|
- "removing "
|
|
- "certificate "
|
|
- "\"%s\" with "
|
|
- "subject "
|
|
- "\"%s\".\n",
|
|
- node->cert->nickname,
|
|
- node->cert->subjectName ?
|
|
- node->cert->subjectName :
|
|
- "");
|
|
- /* Get a handle for
|
|
- * this certificate's
|
|
- * private key, in case
|
|
- * we need to remove
|
|
- * it. */
|
|
- privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
- privkeys = add_privkey_to_list(privkeys, privkey);
|
|
- SEC_DeletePermCertificate(node->cert);
|
|
- }
|
|
- }
|
|
- CERT_DestroyCertList(certlist);
|
|
- }
|
|
- } else {
|
|
- cm_log(3, "No duplicate nickname entries.\n");
|
|
- }
|
|
- /* This certificate's subject may already be present
|
|
- * with a different nickname. Delete those, too. */
|
|
- certlist = CERT_CreateSubjectCertList(NULL, certdb,
|
|
- &subject,
|
|
- PR_FALSE,
|
|
- PR_FALSE);
|
|
- if (certlist != NULL) {
|
|
- /* Look for certs with different nicknames but
|
|
- * the same subject name, because those have
|
|
- * got to go. */
|
|
- i = 0;
|
|
- for (node = CERT_LIST_HEAD(certlist);
|
|
- (node != NULL) &&
|
|
- !CERT_LIST_EMPTY(certlist) &&
|
|
- !CERT_LIST_END(node, certlist);
|
|
- node = CERT_LIST_NEXT(node)) {
|
|
- if ((node->cert->nickname != NULL) &&
|
|
- (strcmp(entry->cm_cert_nickname,
|
|
- node->cert->nickname) != 0)) {
|
|
- i++;
|
|
- cm_log(3, "Found a "
|
|
- "certificate with a "
|
|
+ "certificate with a "
|
|
"different nickname but "
|
|
"the same subject, "
|
|
"removing certificate "
|
|
@@ -426,284 +452,291 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry,
|
|
node->cert->subjectName ?
|
|
node->cert->subjectName :
|
|
"");
|
|
- /* Get a handle for this
|
|
- * certificate's private key,
|
|
- * in case we need to remove
|
|
- * it. */
|
|
- privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
- privkeys = add_privkey_to_list(privkeys, privkey);
|
|
- SEC_DeletePermCertificate(node->cert);
|
|
- } else {
|
|
- /* Same nickname, and we
|
|
- * already know it has the same
|
|
- * subject name. Save its
|
|
- * trust. */
|
|
- if (!have_trust) {
|
|
- if (CERT_GetCertTrust(node->cert,
|
|
+ /* Get a handle for this
|
|
+ * certificate's private key,
|
|
+ * in case we need to remove
|
|
+ * it. */
|
|
+ privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
+ privkeys = add_privkey_to_list(privkeys, privkey);
|
|
+ SEC_DeletePermCertificate(node->cert);
|
|
+ } else {
|
|
+ /* Same nickname, and we
|
|
+ * already know it has the same
|
|
+ * subject name. Save its
|
|
+ * trust. */
|
|
+ if (!have_trust) {
|
|
+ if (CERT_GetCertTrust(node->cert,
|
|
&trust) == SECSuccess) {
|
|
- have_trust = PR_TRUE;
|
|
+ have_trust = PR_TRUE;
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
- }
|
|
- if (i == 0) {
|
|
- cm_log(3, "No duplicate subject name entries.\n");
|
|
- }
|
|
- CERT_DestroyCertList(certlist);
|
|
- } else {
|
|
- cm_log(3, "No duplicate subject name entries.\n");
|
|
- }
|
|
- /* Make one more attempt at finding an existing trust
|
|
- * value. */
|
|
- if (!have_trust) {
|
|
- oldcert = PK11_FindCertFromNickname(entry->cm_cert_nickname, NULL);
|
|
- if (oldcert != NULL) {
|
|
- if (CERT_GetCertTrust(oldcert,
|
|
- &trust) == SECSuccess) {
|
|
- have_trust = PR_TRUE;
|
|
+ if (i == 0) {
|
|
+ cm_log(3, "No duplicate subject name entries.\n");
|
|
}
|
|
- CERT_DestroyCertificate(oldcert);
|
|
+ CERT_DestroyCertList(certlist);
|
|
+ } else {
|
|
+ cm_log(3, "No duplicate subject name entries.\n");
|
|
}
|
|
- }
|
|
- /* Import the certificate. */
|
|
- returned = NULL;
|
|
- error = CERT_ImportCerts(certdb,
|
|
- certUsageUserCertImport,
|
|
- 1, &item, &returned,
|
|
- PR_TRUE,
|
|
- PR_FALSE,
|
|
- entry->cm_cert_nickname);
|
|
- ec = PORT_GetError();
|
|
- if (error == SECSuccess) {
|
|
- /* If NSS uses SQL DB storage, CERT_ImportCerts creates
|
|
- * an incomplete internal state (the cert isn't
|
|
- * associated with the private key, and calling
|
|
- * PK11_FindKeyByAnyCert returns no result).
|
|
- * As a workaround, we import the cert again using
|
|
- * PK11_ImportCert, which magically fixes the issue.
|
|
- * See rhbz#1532188 */
|
|
- error = PK11_ImportCert(PK11_GetInternalKeySlot(),
|
|
- returned[0],
|
|
- CK_INVALID_HANDLE,
|
|
- returned[0]->nickname,
|
|
- PR_FALSE);
|
|
- }
|
|
- if (error == SECSuccess) {
|
|
- cm_log(1, "Imported certificate \"%s\", got "
|
|
- "nickname \"%s\".\n",
|
|
- entry->cm_cert_nickname,
|
|
- returned[0]->nickname);
|
|
- status = 0;
|
|
- /* Set the trust on the new certificate,
|
|
- * perhaps matching the trust on an
|
|
- * already-present certificate with the same
|
|
- * nickname. */
|
|
+ /* Make one more attempt at finding an existing trust
|
|
+ * value. */
|
|
if (!have_trust) {
|
|
- memset(&trust, 0, sizeof(trust));
|
|
- trust.sslFlags = CERTDB_USER;
|
|
- trust.emailFlags = CERTDB_USER;
|
|
- trust.objectSigningFlags = CERTDB_USER;
|
|
+ oldcert = PK11_FindCertFromNickname(entry->cm_cert_nickname, NULL);
|
|
+ if (oldcert != NULL) {
|
|
+ if (CERT_GetCertTrust(oldcert,
|
|
+ &trust) == SECSuccess) {
|
|
+ have_trust = PR_TRUE;
|
|
+ }
|
|
+ CERT_DestroyCertificate(oldcert);
|
|
+ }
|
|
}
|
|
- error = CERT_ChangeCertTrust(certdb,
|
|
- returned[0],
|
|
- &trust);
|
|
+ /* Import the certificate. */
|
|
+ returned = NULL;
|
|
+ error = CERT_ImportCerts(certdb,
|
|
+ certUsageUserCertImport,
|
|
+ 1, &item, &returned,
|
|
+ PR_TRUE,
|
|
+ PR_FALSE,
|
|
+ entry->cm_cert_nickname);
|
|
ec = PORT_GetError();
|
|
- if (error != SECSuccess) {
|
|
+ if (error == SECSuccess) {
|
|
+ /* If NSS uses SQL DB storage, CERT_ImportCerts creates
|
|
+ * an incomplete internal state (the cert isn't
|
|
+ * associated with the private key, and calling
|
|
+ * PK11_FindKeyByAnyCert returns no result).
|
|
+ * As a workaround, we import the cert again using
|
|
+ * PK11_ImportCert, which magically fixes the issue.
|
|
+ * See rhbz#1532188 */
|
|
+ error = PK11_ImportCert(sle->slot,
|
|
+ returned[0],
|
|
+ CK_INVALID_HANDLE,
|
|
+ returned[0]->nickname,
|
|
+ PR_FALSE);
|
|
+ }
|
|
+ if (error == SECSuccess) {
|
|
+ cm_log(1, "Imported certificate \"%s\", got "
|
|
+ "nickname \"%s\".\n",
|
|
+ entry->cm_cert_nickname,
|
|
+ returned[0]->nickname);
|
|
+ status = 0;
|
|
+ /* Set the trust on the new certificate,
|
|
+ * perhaps matching the trust on an
|
|
+ * already-present certificate with the same
|
|
+ * nickname. */
|
|
+ if (!have_trust) {
|
|
+ memset(&trust, 0, sizeof(trust));
|
|
+ trust.sslFlags = CERTDB_USER;
|
|
+ trust.emailFlags = CERTDB_USER;
|
|
+ trust.objectSigningFlags = CERTDB_USER;
|
|
+ }
|
|
+ error = CERT_ChangeCertTrust(certdb,
|
|
+ returned[0],
|
|
+ &trust);
|
|
+ ec = PORT_GetError();
|
|
+ if (error != SECSuccess) {
|
|
+ if (ec != 0) {
|
|
+ es = PR_ErrorToName(ec);
|
|
+ } else {
|
|
+ es = NULL;
|
|
+ }
|
|
+ if (es != NULL) {
|
|
+ cm_log(0, "Error setting trust "
|
|
+ "on certificate \"%s\": "
|
|
+ "%s.\n",
|
|
+ entry->cm_cert_nickname, es);
|
|
+ } else {
|
|
+ cm_log(0, "Error setting trust "
|
|
+ "on certificate \"%s\".\n",
|
|
+ entry->cm_cert_nickname);
|
|
+ }
|
|
+ }
|
|
+ /* Delete any other certificates that are there
|
|
+ * with the same nickname. While NSS's
|
|
+ * database allows duplicates so long as they
|
|
+ * have the same subject name and nickname,
|
|
+ * several APIs and many applications can't
|
|
+ * dependably find the right one among more
|
|
+ * than one. So bye-bye, old certificates. */
|
|
+ certlist = PK11_FindCertsFromNickname(entry->cm_cert_nickname,
|
|
+ NULL);
|
|
+ if (certlist != NULL) {
|
|
+ /* Look for certs with contents. */
|
|
+ for (node = CERT_LIST_HEAD(certlist);
|
|
+ (node != NULL) &&
|
|
+ !CERT_LIST_EMPTY(certlist) &&
|
|
+ !CERT_LIST_END(node, certlist);
|
|
+ node = CERT_LIST_NEXT(node)) {
|
|
+ if (!SECITEM_ItemsAreEqual(item,
|
|
+ &node->cert->derCert)) {
|
|
+ cm_log(3, "Found a "
|
|
+ "certificate "
|
|
+ "with the same "
|
|
+ "nickname and "
|
|
+ "subject, but "
|
|
+ "different "
|
|
+ "contents, "
|
|
+ "removing it.\n");
|
|
+ /* Get a handle for
|
|
+ * this certificate's
|
|
+ * private key, in case
|
|
+ * we need to remove
|
|
+ * it. */
|
|
+ privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
+ privkeys = add_privkey_to_list(privkeys, privkey);
|
|
+ SEC_DeletePermCertificate(node->cert);
|
|
+ }
|
|
+ }
|
|
+ CERT_DestroyCertList(certlist);
|
|
+ }
|
|
+ } else {
|
|
if (ec != 0) {
|
|
es = PR_ErrorToName(ec);
|
|
} else {
|
|
es = NULL;
|
|
}
|
|
if (es != NULL) {
|
|
- cm_log(0, "Error setting trust "
|
|
- "on certificate \"%s\": "
|
|
- "%s.\n",
|
|
- entry->cm_cert_nickname, es);
|
|
+ cm_log(0, "Error importing certificate "
|
|
+ "into NSSDB \"%s\": %s.\n",
|
|
+ entry->cm_cert_storage_location,
|
|
+ es);
|
|
} else {
|
|
- cm_log(0, "Error setting trust "
|
|
- "on certificate \"%s\".\n",
|
|
- entry->cm_cert_nickname);
|
|
+ cm_log(0, "Error importing certificate "
|
|
+ "into NSSDB \"%s\".\n",
|
|
+ entry->cm_cert_storage_location);
|
|
}
|
|
- }
|
|
- /* Delete any other certificates that are there
|
|
- * with the same nickname. While NSS's
|
|
- * database allows duplicates so long as they
|
|
- * have the same subject name and nickname,
|
|
- * several APIs and many applications can't
|
|
- * dependably find the right one among more
|
|
- * than one. So bye-bye, old certificates. */
|
|
- certlist = PK11_FindCertsFromNickname(entry->cm_cert_nickname,
|
|
- NULL);
|
|
- if (certlist != NULL) {
|
|
- /* Look for certs with contents. */
|
|
- for (node = CERT_LIST_HEAD(certlist);
|
|
- (node != NULL) &&
|
|
- !CERT_LIST_EMPTY(certlist) &&
|
|
- !CERT_LIST_END(node, certlist);
|
|
- node = CERT_LIST_NEXT(node)) {
|
|
- if (!SECITEM_ItemsAreEqual(item,
|
|
- &node->cert->derCert)) {
|
|
- cm_log(3, "Found a "
|
|
- "certificate "
|
|
- "with the same "
|
|
- "nickname and "
|
|
- "subject, but "
|
|
- "different "
|
|
- "contents, "
|
|
- "removing it.\n");
|
|
- /* Get a handle for
|
|
- * this certificate's
|
|
- * private key, in case
|
|
- * we need to remove
|
|
- * it. */
|
|
- privkey = PK11_FindKeyByAnyCert(node->cert, NULL);
|
|
- privkeys = add_privkey_to_list(privkeys, privkey);
|
|
- SEC_DeletePermCertificate(node->cert);
|
|
- }
|
|
+ switch (ec) {
|
|
+ case PR_NO_ACCESS_RIGHTS_ERROR: /* ACCES/PERM */
|
|
+ status = CM_CERTSAVE_STATUS_PERMS;
|
|
+ break;
|
|
+ default:
|
|
+ status = CM_CERTSAVE_STATUS_INTERNAL_ERROR;
|
|
+ break;
|
|
}
|
|
- CERT_DestroyCertList(certlist);
|
|
- }
|
|
- } else {
|
|
- if (ec != 0) {
|
|
- es = PR_ErrorToName(ec);
|
|
- } else {
|
|
- es = NULL;
|
|
}
|
|
- if (es != NULL) {
|
|
- cm_log(0, "Error importing certificate "
|
|
- "into NSSDB \"%s\": %s.\n",
|
|
- entry->cm_cert_storage_location,
|
|
- es);
|
|
- } else {
|
|
- cm_log(0, "Error importing certificate "
|
|
- "into NSSDB \"%s\".\n",
|
|
- entry->cm_cert_storage_location);
|
|
- }
|
|
- switch (ec) {
|
|
- case PR_NO_ACCESS_RIGHTS_ERROR: /* ACCES/PERM */
|
|
- status = CM_CERTSAVE_STATUS_PERMS;
|
|
- break;
|
|
- default:
|
|
- status = CM_CERTSAVE_STATUS_INTERNAL_ERROR;
|
|
- break;
|
|
+ /* If we managed to import the certificate, mark its
|
|
+ * key for having its nickname removed. */
|
|
+ if ((returned != NULL) && (returned[0] != NULL)) {
|
|
+ privkey = PK11_FindKeyByAnyCert(returned[0], NULL);
|
|
+ privkeys = add_privkey_to_list(privkeys, privkey);
|
|
+ CERT_DestroyCertArray(returned, 1);
|
|
}
|
|
- }
|
|
- /* If we managed to import the certificate, mark its
|
|
- * key for having its nickname removed. */
|
|
- if ((returned != NULL) && (returned[0] != NULL)) {
|
|
- privkey = PK11_FindKeyByAnyCert(returned[0], NULL);
|
|
- privkeys = add_privkey_to_list(privkeys, privkey);
|
|
- CERT_DestroyCertArray(returned, 1);
|
|
- }
|
|
- /* In case we're rekeying, but failed, mark the
|
|
- * candidate key for name-clearing or removal, too. */
|
|
- if ((entry->cm_key_next_marker != NULL) &&
|
|
- (strlen(entry->cm_key_next_marker) > 0)) {
|
|
- p = util_build_next_nickname(entry->cm_key_nickname,
|
|
- entry->cm_key_next_marker);
|
|
- privkeylist = PK11_ListPrivKeysInSlot(PK11_GetInternalKeySlot(), p, NULL);
|
|
- if (privkeylist != NULL) {
|
|
- for (knode = PRIVKEY_LIST_HEAD(privkeylist);
|
|
- !PRIVKEY_LIST_EMPTY(privkeylist) &&
|
|
- !PRIVKEY_LIST_END(knode, privkeylist);
|
|
- knode = PRIVKEY_LIST_NEXT(knode)) {
|
|
- q = PK11_GetPrivateKeyNickname(knode->key);
|
|
- if ((q != NULL) &&
|
|
- (strcmp(p, q) == 0)) {
|
|
- privkey = SECKEY_CopyPrivateKey(knode->key);
|
|
- privkeys = add_privkey_to_list(privkeys, privkey);
|
|
- break;
|
|
+ /* In case we're rekeying, but failed, mark the
|
|
+ * candidate key for name-clearing or removal, too. */
|
|
+ if ((entry->cm_key_next_marker != NULL) &&
|
|
+ (strlen(entry->cm_key_next_marker) > 0)) {
|
|
+ p = util_build_next_nickname(entry->cm_key_nickname,
|
|
+ entry->cm_key_next_marker);
|
|
+ privkeylist = PK11_ListPrivKeysInSlot(sle->slot, p, NULL);
|
|
+ if (privkeylist != NULL) {
|
|
+ for (knode = PRIVKEY_LIST_HEAD(privkeylist);
|
|
+ !PRIVKEY_LIST_EMPTY(privkeylist) &&
|
|
+ !PRIVKEY_LIST_END(knode, privkeylist);
|
|
+ knode = PRIVKEY_LIST_NEXT(knode)) {
|
|
+ q = PK11_GetPrivateKeyNickname(knode->key);
|
|
+ if ((q != NULL) &&
|
|
+ (strcmp(p, q) == 0)) {
|
|
+ privkey = SECKEY_CopyPrivateKey(knode->key);
|
|
+ privkeys = add_privkey_to_list(privkeys, privkey);
|
|
+ break;
|
|
+ }
|
|
}
|
|
+ SECKEY_DestroyPrivateKeyList(privkeylist);
|
|
}
|
|
- SECKEY_DestroyPrivateKeyList(privkeylist);
|
|
}
|
|
- }
|
|
- if (privkeys != NULL) {
|
|
- /* Check if any certificates are still using
|
|
- * the keys that correspond to certificates
|
|
- * that we removed. */
|
|
- for (i = 0; privkeys[i] != NULL; i++) {
|
|
- privkey = privkeys[i];
|
|
- oldcert = PK11_GetCertFromPrivateKey(privkey);
|
|
- if (!entry->cm_key_preserve && (oldcert == NULL)) {
|
|
- /* We're not preserving
|
|
- * orphaned keys, so remove
|
|
- * this one. No need to mess
|
|
- * with its nickname first. */
|
|
- PK11_DeleteTokenPrivateKey(privkey, PR_FALSE);
|
|
- if (error == SECSuccess) {
|
|
- cm_log(3, "Removed old key.\n");
|
|
- } else {
|
|
- ec = PORT_GetError();
|
|
- if (ec != 0) {
|
|
- es = PR_ErrorToName(ec);
|
|
+ if (privkeys != NULL) {
|
|
+ /* Check if any certificates are still using
|
|
+ * the keys that correspond to certificates
|
|
+ * that we removed. */
|
|
+ for (i = 0; privkeys[i] != NULL; i++) {
|
|
+ privkey = privkeys[i];
|
|
+ oldcert = PK11_GetCertFromPrivateKey(privkey);
|
|
+ if (!entry->cm_key_preserve && (oldcert == NULL)) {
|
|
+ /* We're not preserving
|
|
+ * orphaned keys, so remove
|
|
+ * this one. No need to mess
|
|
+ * with its nickname first. */
|
|
+ PK11_DeleteTokenPrivateKey(privkey, PR_FALSE);
|
|
+ if (error == SECSuccess) {
|
|
+ cm_log(3, "Removed old key.\n");
|
|
} else {
|
|
- es = NULL;
|
|
+ ec = PORT_GetError();
|
|
+ if (ec != 0) {
|
|
+ es = PR_ErrorToName(ec);
|
|
+ } else {
|
|
+ es = NULL;
|
|
+ }
|
|
+ if (es != NULL) {
|
|
+ cm_log(0, "Failed "
|
|
+ "to remove "
|
|
+ "old key: "
|
|
+ "%s.\n", es);
|
|
+ } else {
|
|
+ cm_log(0, "Failed "
|
|
+ "to remove "
|
|
+ "old key.\n");
|
|
+ }
|
|
}
|
|
- if (es != NULL) {
|
|
- cm_log(0, "Failed "
|
|
- "to remove "
|
|
- "old key: "
|
|
- "%s.\n", es);
|
|
- } else {
|
|
- cm_log(0, "Failed "
|
|
- "to remove "
|
|
- "old key.\n");
|
|
- }
|
|
- }
|
|
- } else {
|
|
- /* Remove the explicit
|
|
- * nickname, so that the key
|
|
- * will have to be found using
|
|
- * the certificate's nickname,
|
|
- * and certutil will display
|
|
- * the matching certificate's
|
|
- * nickname when it's asked to
|
|
- * list the keys in the
|
|
- * database. */
|
|
- error = PK11_SetPrivateKeyNickname(privkey, "");
|
|
- if (error == SECSuccess) {
|
|
- cm_log(3, "Removed "
|
|
- "name from old "
|
|
- "key.\n");
|
|
} else {
|
|
- ec = PORT_GetError();
|
|
- if (ec != 0) {
|
|
- es = PR_ErrorToName(ec);
|
|
+ /* Remove the explicit
|
|
+ * nickname, so that the key
|
|
+ * will have to be found using
|
|
+ * the certificate's nickname,
|
|
+ * and certutil will display
|
|
+ * the matching certificate's
|
|
+ * nickname when it's asked to
|
|
+ * list the keys in the
|
|
+ * database. */
|
|
+ error = PK11_SetPrivateKeyNickname(privkey, "");
|
|
+ if (error == SECSuccess) {
|
|
+ cm_log(3, "Removed "
|
|
+ "name from old "
|
|
+ "key.\n");
|
|
} else {
|
|
- es = NULL;
|
|
- }
|
|
- if (es != NULL) {
|
|
- cm_log(0, "Failed "
|
|
- "to unname "
|
|
- "old key: "
|
|
- "%s.\n", es);
|
|
- } else {
|
|
- cm_log(0, "Failed "
|
|
- "to unname "
|
|
- "old key.\n");
|
|
+ ec = PORT_GetError();
|
|
+ if (ec != 0) {
|
|
+ es = PR_ErrorToName(ec);
|
|
+ } else {
|
|
+ es = NULL;
|
|
+ }
|
|
+ if (es != NULL) {
|
|
+ cm_log(0, "Failed "
|
|
+ "to unname "
|
|
+ "old key: "
|
|
+ "%s.\n", es);
|
|
+ } else {
|
|
+ cm_log(0, "Failed "
|
|
+ "to unname "
|
|
+ "old key.\n");
|
|
+ }
|
|
}
|
|
+ SECKEY_DestroyPrivateKey(privkey);
|
|
+ }
|
|
+ if (oldcert != NULL) {
|
|
+ CERT_DestroyCertificate(oldcert);
|
|
}
|
|
- SECKEY_DestroyPrivateKey(privkey);
|
|
- }
|
|
- if (oldcert != NULL) {
|
|
- CERT_DestroyCertificate(oldcert);
|
|
}
|
|
+ free(privkeys);
|
|
}
|
|
- free(privkeys);
|
|
+ } else {
|
|
+ cm_log(1, "Error getting handle to default NSS DB.\n");
|
|
}
|
|
- } else {
|
|
- cm_log(1, "Error getting handle to default NSS DB.\n");
|
|
- }
|
|
- PORT_FreeArena(arena, PR_TRUE);
|
|
- if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
- cm_log(1, "Error shutting down NSS.\n");
|
|
- }
|
|
- /* Fixup the ownership and permissions on the key and
|
|
- * certificate databases. */
|
|
- util_set_db_entry_key_owner(entry->cm_key_storage_location, entry);
|
|
- util_set_db_entry_cert_owner(entry->cm_cert_storage_location, entry);
|
|
- }
|
|
+ PORT_FreeArena(arena, PR_TRUE);
|
|
+ if (NSS_ShutdownContext(ctx) != SECSuccess) {
|
|
+ cm_log(1, "Error shutting down NSS.\n");
|
|
+ }
|
|
+ /* Fixup the ownership and permissions on the key and
|
|
+ * certificate databases. */
|
|
+ util_set_db_entry_key_owner(entry->cm_key_storage_location, entry);
|
|
+ util_set_db_entry_cert_owner(entry->cm_cert_storage_location, entry);
|
|
+ break;
|
|
+next_slot:
|
|
+ if (sle == slotlist->tail) {
|
|
+ break;
|
|
+ }
|
|
+ } /* for slot loop */
|
|
+ } /* ctx == NULL */
|
|
+
|
|
if (status != 0) {
|
|
_exit(status);
|
|
}
|
|
--
|
|
2.14.4
|
|
|