sssd/0049-pam-filter-certificate...

357 lines
13 KiB
Diff

From 1889bd761a16a67cfb22063a2b277b4def670aae Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Mon, 6 Nov 2017 15:26:38 +0100
Subject: [PATCH 49/79] pam: filter certificates in the responder not in the
child
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With the new selection option and the handling of multiple certificates
in the PAM responder it is not needed anymore to filter the certificates
in p11_child but the matching rules can be applied by the PAM responder
directly.
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 | 18 +-----
src/responder/pam/pamsrv.h | 6 ++
src/responder/pam/pamsrv_cmd.c | 10 ++-
src/responder/pam/pamsrv_p11.c | 135 +++++++++++++++++++++++++++++++++++++++-
src/tests/cmocka/test_pam_srv.c | 3 +
5 files changed, 152 insertions(+), 20 deletions(-)
diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c
index 5f289688e41f4ea610292b907036e05cf95eb29d..e59aba0d1561f58206252f7251ecd88315836b1b 100644
--- a/src/p11_child/p11_child_nss.c
+++ b/src/p11_child/p11_child_nss.c
@@ -264,22 +264,6 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
}
}
- rv = CERT_FilterCertListByUsage(cert_list, certUsageSSLClient, PR_FALSE);
- if (rv != SECSuccess) {
- DEBUG(SSSDBG_OP_FAILURE, "CERT_FilterCertListByUsage failed: [%d][%s].\n",
- PR_GetError(), PORT_ErrorToString(PR_GetError()));
- return EIO;
- }
-
- rv = CERT_FilterCertListForUserCerts(cert_list);
- if (rv != SECSuccess) {
- DEBUG(SSSDBG_OP_FAILURE,
- "CERT_FilterCertListForUserCerts failed: [%d][%s].\n",
- PR_GetError(), PORT_ErrorToString(PR_GetError()));
- return EIO;
- }
-
-
handle = CERT_GetDefaultCertDB();
if (handle == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "CERT_GetDefaultCertDB failed: [%d][%s].\n",
@@ -344,7 +328,7 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db,
if (cert_verify_opts->do_verification) {
rv = CERT_VerifyCertificateNow(handle, cert_list_node->cert,
PR_TRUE,
- certificateUsageSSLClient,
+ certificateUsageCheckAllUsages,
NULL, NULL);
if (rv != SECSuccess) {
DEBUG(SSSDBG_OP_FAILURE,
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index f15f7f19f1f38626288416c9f2038371c6f58b47..0bc229212844602ed461d1c7db48bf51ac2e2194 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -27,6 +27,7 @@
#include "sbus/sssd_dbus.h"
#include "responder/common/responder.h"
#include "responder/common/cache_req/cache_req.h"
+#include "lib/certmap/sss_certmap.h"
struct pam_auth_req;
@@ -49,6 +50,7 @@ struct pam_ctx {
bool cert_auth;
int p11_child_debug_fd;
char *nss_db;
+ struct sss_certmap_ctx *sss_certmap_ctx;
};
struct pam_auth_dp_req {
@@ -104,6 +106,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
const char *nss_db,
time_t timeout,
const char *verify_opts,
+ struct sss_certmap_ctx *sss_certmap_ctx,
struct pam_data *pd);
errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
struct cert_auth_info **cert_list);
@@ -114,6 +117,9 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username,
bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
+errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+ struct certmap_info **certmap_list);
+
errno_t
pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
const char *username,
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index caf6c99489b8378d2e850473191223709920cd79..0e76c9e772f1775635677f35b870e9613b2faf64 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1336,7 +1336,8 @@ static errno_t check_cert(TALLOC_CTX *mctx,
req = pam_check_cert_send(mctx, ev, pctx->p11_child_debug_fd,
pctx->nss_db, p11_child_timeout,
- cert_verification_opts, pd);
+ cert_verification_opts, pctx->sss_certmap_ctx,
+ pd);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n");
return ENOMEM;
@@ -1749,6 +1750,13 @@ static void pam_forwarder_cb(struct tevent_req *req)
goto done;
}
+ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains->certmaps);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "p11_refresh_certmap_ctx failed, "
+ "certificate matching might not work as expected");
+ }
+
pd = preq->pd;
ret = pam_forwarder_parse_data(cctx, pd);
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 5a3eeff0ec977829a9ad8c80b4fc6b2e06857097..ec52c5ae7163d41144fe082643a201b766a1e201 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -36,6 +36,7 @@
#define P11_CHILD_LOG_FILE "p11_child"
#define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
+#define CERT_AUTH_DEFAULT_MATCHING_RULE "KRB5:<EKU>clientAuth"
struct cert_auth_info {
char *cert;
@@ -116,8 +117,110 @@ void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
return;
}
+struct priv_sss_debug {
+ int level;
+};
+
+static void ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+ int level = SSSDBG_OP_FAILURE;
+
+ if (data != NULL) {
+ level = data->level;
+ }
+
+ if (DEBUG_IS_SET(level)) {
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+ format, ap);
+ va_end(ap);
+ }
+}
+
+errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+ struct certmap_info **certmap_list)
+{
+ int ret;
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ size_t c;
+
+ ret = sss_certmap_init(pctx, ext_debug, NULL, &sss_certmap_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
+ goto done;
+ }
+
+ if (certmap_list == NULL || *certmap_list == NULL) {
+ /* Try to add default matching rule */
+ ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
+ CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to add default matching rule.\n");
+ }
+
+ goto done;
+ }
+
+ for (c = 0; certmap_list[c] != NULL; c++) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Trying to add rule [%s][%d][%s][%s].\n",
+ certmap_list[c]->name, certmap_list[c]->priority,
+ certmap_list[c]->match_rule, certmap_list[c]->map_rule);
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority,
+ certmap_list[c]->match_rule,
+ certmap_list[c]->map_rule,
+ certmap_list[c]->domains);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_certmap_add_rule failed for rule [%s] "
+ "with error [%d][%s], skipping. "
+ "Please check for typos and if rule syntax is supported.\n",
+ certmap_list[c]->name, ret, sss_strerror(ret));
+ continue;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ sss_certmap_free_ctx(pctx->sss_certmap_ctx);
+ pctx->sss_certmap_ctx = sss_certmap_ctx;
+ } else {
+ sss_certmap_free_ctx(sss_certmap_ctx);
+ }
+
+ return ret;
+}
+
errno_t p11_child_init(struct pam_ctx *pctx)
{
+ int ret;
+ struct certmap_info **certmaps;
+ bool user_name_hint;
+ struct sss_domain_info *dom = pctx->rctx->domains;
+
+ ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
+ return ret;
+ }
+
+ dom->user_name_hint = user_name_hint;
+ talloc_free(dom->certmaps);
+ dom->certmaps = certmaps;
+
+ ret = p11_refresh_certmap_ctx(pctx, dom->certmaps);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n");
+ return ret;
+ }
+
return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
}
@@ -214,6 +317,7 @@ 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,
+ struct sss_certmap_ctx *sss_certmap_ctx,
struct cert_auth_info **_cert_list)
{
int ret;
@@ -222,6 +326,8 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
uint8_t *pn;
struct cert_auth_info *cert_list = NULL;
struct cert_auth_info *cert_auth_info;
+ unsigned char *der = NULL;
+ size_t der_size;
if (buf_len < 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
@@ -347,7 +453,22 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
}
DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert_auth_info->cert);
- DLIST_ADD(cert_list, cert_auth_info);
+ der = sss_base64_decode(tmp_ctx, cert_auth_info->cert, &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sss_certmap_match_cert(sss_certmap_ctx, der, der_size);
+ if (ret == 0) {
+ DLIST_ADD(cert_list, cert_auth_info);
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Cert [%s] does not match matching rules and is ignored.\n",
+ cert_auth_info->cert);
+ talloc_free(cert_auth_info);
+ }
p = ++pn;
} while ((pn - buf) < buf_len);
@@ -373,6 +494,7 @@ struct pam_check_cert_state {
struct sss_child_ctx_old *child_ctx;
struct tevent_timer *timeout_handler;
struct tevent_context *ev;
+ struct sss_certmap_ctx *sss_certmap_ctx;
struct child_io_fds *io;
@@ -391,6 +513,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
const char *nss_db,
time_t timeout,
const char *verify_opts,
+ struct sss_certmap_ctx *sss_certmap_ctx,
struct pam_data *pd)
{
errno_t ret;
@@ -420,6 +543,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
goto done;
}
+ if (sss_certmap_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate matching context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
/* extra_args are added in revers order */
arg_c = 0;
extra_args[arg_c++] = nss_db;
@@ -476,6 +605,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
}
state->ev = ev;
+ state->sss_certmap_ctx = sss_certmap_ctx;
state->child_status = EFAULT;
state->io = talloc(state, struct child_io_fds);
if (state->io == NULL) {
@@ -639,7 +769,8 @@ 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_list);
+ ret = parse_p11_child_response(state, buf, buf_len, state->sss_certmap_ctx,
+ &state->cert_list);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_response failed.\n");
tevent_req_error(req, ret);
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index f5ba131357b536adac4b3466ebbc257a838d1420..f86719f4fb154524fc9620aa8cb583690ca597f3 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -287,6 +287,9 @@ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
return NULL;
}
+ ret = p11_refresh_certmap_ctx(pctx, NULL);
+ assert_int_equal(ret, 0);
+
return pctx;
}
--
2.15.1