sssd/0042-p11_child-return-multi...

598 lines
20 KiB
Diff

From 78fd324fd1eb82319248987639376e85ad7204b1 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 17 Jan 2017 15:55:18 +0100
Subject: [PATCH 42/79] p11_child: return multiple certs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch refactors the handling of certificates in p11_child. Not only
the first but all certificates suitable for authentication are returned.
The PAM responder component calling p11_child is refactored to handle
multiple certificate returned by p11_child but so far only returns the
first one to its callers.
Related to https://pagure.io/SSSD/sssd/issue/3560
Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
Tested-by: Scott Poore <spoore@redhat.com>
---
src/p11_child/p11_child_nss.c | 131 +++++++++++++---------
src/responder/pam/pamsrv.h | 2 +
src/responder/pam/pamsrv_p11.c | 242 +++++++++++++++++++++++------------------
3 files changed, 219 insertions(+), 156 deletions(-)
diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index b0ec69be321c4b4186ce851c07bfcc3e1afe9694..50bde2f4f91f6c00260b0db383d0962112686ebc 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -72,8 +72,7 @@ static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg)
int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
enum op_mode mode, const char *pin,
struct cert_verify_opts *cert_verify_opts,
- char **cert, char **token_name_out, char **module_name_out,
- char **key_id_out)
+ char **_multi)
{
int ret;
SECStatus rv;
@@ -110,7 +109,10 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
PK11SlotListElement *le;
SECItem *key_id = NULL;
char *key_id_str = NULL;
-
+ CERTCertList *valid_certs = NULL;
+ char *cert_b64 = NULL;
+ char *multi = NULL;
+ PRCList *node;
nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, &parameters, flags);
if (nss_ctx == NULL) {
@@ -303,6 +305,14 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
}
found_cert = NULL;
+ valid_certs = CERT_NewCertList();
+ if (valid_certs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "CERT_NewCertList failed [%d].\n",
+ PR_GetError());
+ ret = ENOMEM;
+ goto done;
+ }
+
DEBUG(SSSDBG_TRACE_ALL, "Filtered certificates:\n");
for (cert_list_node = CERT_LIST_HEAD(cert_list);
!CERT_LIST_END(cert_list_node, cert_list);
@@ -326,6 +336,13 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
}
}
+ rv = CERT_AddCertToListTail(valid_certs, cert_list_node->cert);
+ if (rv != SECSuccess) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "CERT_AddCertToListTail failed [%d].\n", PR_GetError());
+ ret = EIO;
+ goto done;
+ }
if (found_cert == NULL) {
found_cert = cert_list_node->cert;
@@ -352,9 +369,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
if (found_cert == NULL) {
DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n");
- *cert = NULL;
- *token_name_out = NULL;
- *module_name_out = NULL;
+ *_multi = NULL;
ret = EOK;
goto done;
}
@@ -421,51 +436,55 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in,
"Certificate verified and validated.\n");
}
- key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
- if (key_id == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
- PR_GetError());
- ret = EINVAL;
- goto done;
- }
-
- key_id_str = CERT_Hexify(key_id, PR_FALSE);
- if (key_id_str == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n", PR_GetError());
+ multi = talloc_strdup(mem_ctx, "");
+ if (multi == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create output string.\n");
ret = ENOMEM;
goto done;
}
- DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n", key_id_str);
+ for (cert_list_node = CERT_LIST_HEAD(valid_certs);
+ !CERT_LIST_END(cert_list_node, valid_certs);
+ cert_list_node = CERT_LIST_NEXT(cert_list_node)) {
- *key_id_out = talloc_strdup(mem_ctx, key_id_str);
- if (*key_id_out == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy key id.\n");
- ret = ENOMEM;
- goto done;
- }
+ found_cert = cert_list_node->cert;
- *cert = sss_base64_encode(mem_ctx, found_cert->derCert.data,
- found_cert->derCert.len);
- if (*cert == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
- ret = ENOMEM;
- goto done;
- }
+ SECITEM_FreeItem(key_id, PR_TRUE);
+ PORT_Free(key_id_str);
+ key_id = PK11_GetLowLevelKeyIDForCert(slot, found_cert, NULL);
+ if (key_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "PK11_GetLowLevelKeyIDForCert failed [%d].\n",
+ PR_GetError());
+ ret = EINVAL;
+ goto done;
+ }
- *token_name_out = talloc_strdup(mem_ctx, token_name);
- if (*token_name_out == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy slot name.\n");
- ret = ENOMEM;
- goto done;
- }
+ key_id_str = CERT_Hexify(key_id, PR_FALSE);
+ if (key_id_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "CERT_Hexify failed [%d].\n",
+ PR_GetError());
+ ret = ENOMEM;
+ goto done;
+ }
- *module_name_out = talloc_strdup(mem_ctx, module_name);
- if (*module_name_out == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy module_name_out name.\n");
- ret = ENOMEM;
- goto done;
+ talloc_free(cert_b64);
+ cert_b64 = sss_base64_encode(mem_ctx, found_cert->derCert.data,
+ found_cert->derCert.len);
+ if (cert_b64 == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n",
+ key_id_str);
+
+ multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n",
+ token_name, module_name, key_id_str,
+ cert_b64);
}
+ *_multi = multi;
ret = EOK;
@@ -474,6 +493,18 @@ done:
PK11_FreeSlot(slot);
}
+ if (valid_certs != NULL) {
+ /* The certificates can be found in valid_certs and cert_list and
+ * CERT_DestroyCertList() will free the certificates as well. To avoid
+ * a double free the nodes from valid_certs are removed first because
+ * valid_certs will only have a sub-set of the certificates. */
+ while (!PR_CLIST_IS_EMPTY(&valid_certs->list)) {
+ node = PR_LIST_HEAD(&valid_certs->list);
+ PR_REMOVE_LINK(node);
+ }
+ CERT_DestroyCertList(valid_certs);
+ }
+
if (cert_list != NULL) {
CERT_DestroyCertList(cert_list);
}
@@ -483,6 +514,8 @@ done:
PORT_Free(signed_random_value.data);
+ talloc_free(cert_b64);
+
rv = NSS_ShutdownContext(nss_ctx);
if (rv != SECSuccess) {
DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d].\n",
@@ -540,17 +573,14 @@ int main(int argc, const char *argv[])
const char *opt_logger = NULL;
errno_t ret;
TALLOC_CTX *main_ctx = NULL;
- char *cert;
enum op_mode mode = OP_NONE;
enum pin_mode pin_mode = PIN_NONE;
char *pin = NULL;
char *slot_name_in = NULL;
- char *token_name_out = NULL;
- char *module_name_out = NULL;
- char *key_id_out = NULL;
char *nss_db = NULL;
struct cert_verify_opts *cert_verify_opts;
char *verify_opts = NULL;
+ char *multi = NULL;
struct poptOption long_options[] = {
POPT_AUTOHELP
@@ -715,17 +745,14 @@ int main(int argc, const char *argv[])
}
ret = do_work(main_ctx, nss_db, slot_name_in, mode, pin, cert_verify_opts,
- &cert, &token_name_out, &module_name_out, &key_id_out);
+ &multi);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n");
goto fail;
}
- if (cert != NULL) {
- fprintf(stdout, "%s\n", token_name_out);
- fprintf(stdout, "%s\n", module_name_out);
- fprintf(stdout, "%s\n", key_id_out);
- fprintf(stdout, "%s\n", cert);
+ if (multi != NULL) {
+ fprintf(stdout, "%s", multi);
}
talloc_free(main_ctx);
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 57a37b72594f030995f5e22255eb7a8fcd63d10e..896f71befbc9947a53b5eb20cba0bb3d104c4cf2 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -88,6 +88,8 @@ int LOCAL_pam_handler(struct pam_auth_req *preq);
errno_t p11_child_init(struct pam_ctx *pctx);
+struct cert_auth_info;
+
struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
int child_debug_fd,
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 4dce43800c3c6b026c545df35c846269cbb49610..ff32d1e726808caa36ca7cca557220866ef1a9ab 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -35,6 +35,15 @@
#define P11_CHILD_LOG_FILE "p11_child"
#define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
+struct cert_auth_info {
+ char *cert;
+ char *token_name;
+ char *module_name;
+ char *key_id;
+ struct cert_auth_info *prev;
+ struct cert_auth_info *next;
+};
+
errno_t p11_child_init(struct pam_ctx *pctx)
{
return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
@@ -132,18 +141,15 @@ static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
}
static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
- ssize_t buf_len, char **_cert,
- char **_token_name, char **_module_name,
- char **_key_id)
+ ssize_t buf_len,
+ struct cert_auth_info **_cert_list)
{
int ret;
TALLOC_CTX *tmp_ctx = NULL;
uint8_t *p;
uint8_t *pn;
- char *cert = NULL;
- char *token_name = NULL;
- char *module_name = NULL;
- char *key_id = NULL;
+ struct cert_auth_info *cert_list = NULL;
+ struct cert_auth_info *cert_auth_info;
if (buf_len < 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
@@ -157,108 +163,132 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
goto done;
}
- p = memchr(buf, '\n', buf_len);
- if (p == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "Missing new-line in p11_child response.\n");
- return EINVAL;
- }
- if (p == buf) {
- DEBUG(SSSDBG_OP_FAILURE, "Missing counter in p11_child response.\n");
- return EINVAL;
- }
-
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
return ENOMEM;
}
- token_name = talloc_strndup(tmp_ctx, (char*) buf, (p - buf));
- if (token_name == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
- ret = ENOMEM;
- goto done;
- }
+ p = buf;
- p++;
- pn = memchr(p, '\n', buf_len - (p - buf));
- if (pn == NULL) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Missing new-line in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ do {
+ cert_auth_info = talloc_zero(tmp_ctx, struct cert_auth_info);
+ if (cert_auth_info == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ return ENOMEM;
+ }
- if (pn == p) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Missing module name in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ return EINVAL;
+ }
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing counter in p11_child response.\n");
+ return EINVAL;
+ }
- module_name = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
- if (module_name == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
- ret = ENOMEM;
- goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n", module_name);
+ cert_auth_info->token_name = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->token_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found token name [%s].\n",
+ cert_auth_info->token_name);
- p = ++pn;
- pn = memchr(p, '\n', buf_len - (p - buf));
- if (pn == NULL) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Missing new-line in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
- if (pn == p) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Missing key id in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing module name in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
- key_id = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
- if (key_id == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
- ret = ENOMEM;
- goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", key_id);
+ cert_auth_info->module_name = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->module_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n",
+ cert_auth_info->module_name);
- p = pn + 1;
- pn = memchr(p, '\n', buf_len - (p - buf));
- if (pn == NULL) {
- DEBUG(SSSDBG_OP_FAILURE,
- "Missing new-line in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
- if (pn == p) {
- DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
- ret = EINVAL;
- goto done;
- }
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing key id in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
- cert = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
- if(cert == NULL) {
- DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
- ret = ENOMEM;
- goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert);
+ cert_auth_info->key_id = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->key_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", cert_auth_info->key_id);
+
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->cert = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->cert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert_auth_info->cert);
+
+ DLIST_ADD(cert_list, cert_auth_info);
+
+ p = ++pn;
+ } while ((pn - buf) < buf_len);
ret = EOK;
done:
if (ret == EOK) {
- *_token_name = talloc_steal(mem_ctx, token_name);
- *_cert = talloc_steal(mem_ctx, cert);
- *_module_name = talloc_steal(mem_ctx, module_name);
- *_key_id = talloc_steal(mem_ctx, key_id);
+ DLIST_FOR_EACH(cert_auth_info, cert_list) {
+ talloc_steal(mem_ctx, cert_auth_info);
+ }
+
+ *_cert_list = cert_list;
}
talloc_free(tmp_ctx);
@@ -273,10 +303,8 @@ struct pam_check_cert_state {
struct tevent_context *ev;
struct child_io_fds *io;
- char *cert;
- char *token_name;
- char *module_name;
- char *key_id;
+
+ struct cert_auth_info *cert_list;
};
static void p11_child_write_done(struct tevent_req *subreq);
@@ -349,9 +377,6 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
state->ev = ev;
state->child_status = EFAULT;
- state->cert = NULL;
- state->token_name = NULL;
- state->module_name = NULL;
state->io = talloc(state, struct child_io_fds);
if (state->io == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
@@ -514,11 +539,9 @@ static void p11_child_done(struct tevent_req *subreq)
PIPE_FD_CLOSE(state->io->read_from_child_fd);
- ret = parse_p11_child_response(state, buf, buf_len, &state->cert,
- &state->token_name, &state->module_name,
- &state->key_id);
+ ret = parse_p11_child_response(state, buf, buf_len, &state->cert_list);
if (ret != EOK) {
- DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_respose failed.\n");
+ DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_response failed.\n");
tevent_req_error(req, ret);
return;
}
@@ -551,20 +574,31 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
TEVENT_REQ_RETURN_ON_ERROR(req);
+ if (state->cert_list == NULL) {
+ *token_name = NULL;
+ *cert = NULL;
+ *module_name = NULL;
+ *key_id = NULL;
+ }
+
if (cert != NULL) {
- *cert = talloc_steal(mem_ctx, state->cert);
+ *cert = (state->cert_list == NULL) ? NULL
+ : talloc_steal(mem_ctx, state->cert_list->cert);
}
if (token_name != NULL) {
- *token_name = talloc_steal(mem_ctx, state->token_name);
+ *token_name = (state->cert_list == NULL) ? NULL
+ : talloc_steal(mem_ctx, state->cert_list->token_name);
}
if (module_name != NULL) {
- *module_name = talloc_steal(mem_ctx, state->module_name);
+ *module_name = (state->cert_list == NULL) ? NULL
+ : talloc_steal(mem_ctx, state->cert_list->module_name);
}
if (key_id != NULL) {
- *key_id = talloc_steal(mem_ctx, state->key_id);
+ *key_id = (state->cert_list == NULL) ? NULL
+ : talloc_steal(mem_ctx, state->cert_list->key_id);
}
return EOK;
--
2.15.1