37cd032951
The updated NSS crypto-policy enables all tokens which broke requesting certificates due to the way that tokens were managed.
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
|
|
|