Blame SOURCES/0043-pam-filter-certificates-in-the-responder-not-in-the-.patch

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