Blame SOURCES/0067-PAM-Add-application-services.patch

ecf709
From 855201c70f69f2b1dbcb3faef780fbdb84354f18 Mon Sep 17 00:00:00 2001
ecf709
From: Jakub Hrozek <jhrozek@redhat.com>
ecf709
Date: Sun, 26 Mar 2017 18:28:41 +0200
ecf709
Subject: [PATCH 67/72] PAM: Add application services
ecf709
ecf709
Related to:
ecf709
https://pagure.io/SSSD/sssd/issue/3310
ecf709
ecf709
Adds a new PAM responder option 'pam_app_services'. This option can hold
ecf709
a list of PAM services that are allowed to contact the application
ecf709
non-POSIX domains. These services are NOT allowed to contact any of the
ecf709
POSIX domains.
ecf709
ecf709
Reviewed-by: Sumit Bose <sbose@redhat.com>
ecf709
---
ecf709
 src/confdb/confdb.h                  |   1 +
ecf709
 src/config/SSSDConfig/__init__.py.in |   1 +
ecf709
 src/config/cfg_rules.ini             |   1 +
ecf709
 src/config/etc/sssd.api.conf         |   1 +
ecf709
 src/man/sssd.conf.5.xml              |  12 +++
ecf709
 src/responder/pam/pamsrv.c           |  33 +++++++
ecf709
 src/responder/pam/pamsrv.h           |   5 ++
ecf709
 src/responder/pam/pamsrv_cmd.c       |  26 +++++-
ecf709
 src/tests/cmocka/test_pam_srv.c      | 167 ++++++++++++++++++++++++++++++++++-
ecf709
 9 files changed, 241 insertions(+), 6 deletions(-)
ecf709
ecf709
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
ecf709
index 5a8d377c312f641f544b1c7cf38826192462ea3c..8719c239362b371fcdb1b78956bcddde871f141b 100644
ecf709
--- a/src/confdb/confdb.h
ecf709
+++ b/src/confdb/confdb.h
ecf709
@@ -129,6 +129,7 @@
ecf709
 #define CONFDB_PAM_CERT_AUTH "pam_cert_auth"
ecf709
 #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path"
ecf709
 #define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout"
ecf709
+#define CONFDB_PAM_APP_SERVICES "pam_app_services"
ecf709
 
ecf709
 /* SUDO */
ecf709
 #define CONFDB_SUDO_CONF_ENTRY "config/sudo"
ecf709
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
ecf709
index 806611b6076048c08ce08c772dbd3cea5fdd656c..211338778e81c1c60ffb3cdbc67c9619343d7798 100644
ecf709
--- a/src/config/SSSDConfig/__init__.py.in
ecf709
+++ b/src/config/SSSDConfig/__init__.py.in
ecf709
@@ -102,6 +102,7 @@ option_strings = {
ecf709
     'pam_cert_auth' : _('Allow certificate based/Smartcard authentication.'),
ecf709
     'pam_cert_db_path' : _('Path to certificate databse with PKCS#11 modules.'),
ecf709
     'p11_child_timeout' : _('How many seconds will pam_sss wait for p11_child to finish'),
ecf709
+    'pam_app_services' : _('Which PAM services are permitted to contact application domains'),
ecf709
 
ecf709
     # [sudo]
ecf709
     'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'),
ecf709
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
ecf709
index 8fd2d2c5236246394353a88c50d1510bd6233f77..1a749db754cedd87f263f7ae596d6f8238bb4357 100644
ecf709
--- a/src/config/cfg_rules.ini
ecf709
+++ b/src/config/cfg_rules.ini
ecf709
@@ -119,6 +119,7 @@ option = pam_account_locked_message
ecf709
 option = pam_cert_auth
ecf709
 option = pam_cert_db_path
ecf709
 option = p11_child_timeout
ecf709
+option = pam_app_services
ecf709
 
ecf709
 [rule/allowed_sudo_options]
ecf709
 validator = ini_allowed_options
ecf709
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
ecf709
index a38b24208f89e4502e41625c540ea9958d5bbffe..a1a0c2992925a4c7df86832117eec2a0cf7894c9 100644
ecf709
--- a/src/config/etc/sssd.api.conf
ecf709
+++ b/src/config/etc/sssd.api.conf
ecf709
@@ -73,6 +73,7 @@ pam_account_locked_message = str, None, false
ecf709
 pam_cert_auth = bool, None, false
ecf709
 pam_cert_db_path = str, None, false
ecf709
 p11_child_timeout = int, None, false
ecf709
+pam_app_services = str, None, false
ecf709
 
ecf709
 [sudo]
ecf709
 # sudo service
ecf709
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
ecf709
index 8294793c765bfa6bf481693c7d7f206950454681..c4e30396f16c40db37af2f56ac218b6e37201ef7 100644
ecf709
--- a/src/man/sssd.conf.5.xml
ecf709
+++ b/src/man/sssd.conf.5.xml
ecf709
@@ -1325,6 +1325,18 @@ pam_account_locked_message = Account locked, please contact help desk.
ecf709
                         </para>
ecf709
                     </listitem>
ecf709
                 </varlistentry>
ecf709
+                <varlistentry>
ecf709
+                    <term>pam_app_services (string)</term>
ecf709
+                    <listitem>
ecf709
+                        <para>
ecf709
+                            Which PAM services are permitted to contact
ecf709
+                            domains of type <quote>application</quote>
ecf709
+                        </para>
ecf709
+                        <para>
ecf709
+                            Default: Not set
ecf709
+                        </para>
ecf709
+                    </listitem>
ecf709
+                </varlistentry>
ecf709
 
ecf709
             </variablelist>
ecf709
         </refsect2>
ecf709
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
ecf709
index ab3f4545520f3fcb2492a6089a039c46f0fb847f..79470823d18138da6ef9235e6336a3220ead1797 100644
ecf709
--- a/src/responder/pam/pamsrv.c
ecf709
+++ b/src/responder/pam/pamsrv.c
ecf709
@@ -166,6 +166,32 @@ done:
ecf709
     return ret;
ecf709
 }
ecf709
 
ecf709
+static errno_t get_app_services(struct pam_ctx *pctx)
ecf709
+{
ecf709
+    errno_t ret;
ecf709
+
ecf709
+    ret = confdb_get_string_as_list(pctx->rctx->cdb, pctx,
ecf709
+                                    CONFDB_PAM_CONF_ENTRY,
ecf709
+                                    CONFDB_PAM_APP_SERVICES,
ecf709
+                                    &pctx->app_services);
ecf709
+    if (ret == ENOENT) {
ecf709
+        pctx->app_services = talloc_zero_array(pctx, char *, 1);
ecf709
+        if (pctx->app_services == NULL) {
ecf709
+            return ENOMEM;
ecf709
+        }
ecf709
+        /* Allocating an empty array makes it easier for the consumer
ecf709
+         * to iterate over it
ecf709
+         */
ecf709
+    } else if (ret != EOK) {
ecf709
+        DEBUG(SSSDBG_CRIT_FAILURE,
ecf709
+              "Cannot read "CONFDB_PAM_APP_SERVICES" [%d]: %s\n",
ecf709
+              ret, sss_strerror(ret));
ecf709
+        return ret;
ecf709
+    }
ecf709
+
ecf709
+    return EOK;
ecf709
+}
ecf709
+
ecf709
 static int pam_process_init(TALLOC_CTX *mem_ctx,
ecf709
                             struct tevent_context *ev,
ecf709
                             struct confdb_ctx *cdb,
ecf709
@@ -219,6 +245,13 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
ecf709
         goto done;
ecf709
     }
ecf709
 
ecf709
+    ret = get_app_services(pctx);
ecf709
+    if (ret != EOK) {
ecf709
+        DEBUG(SSSDBG_FATAL_FAILURE, "get_app_services failed: %d:[%s].\n",
ecf709
+              ret, sss_strerror(ret));
ecf709
+        goto done;
ecf709
+    }
ecf709
+
ecf709
     /* Enable automatic reconnection to the Data Provider */
ecf709
 
ecf709
     /* FIXME: "retries" is too generic, either get it from a global config
ecf709
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
ecf709
index b3eb56441048ecdba82866a95f1d6d6d5e786c60..b569748fe2a2005cee5df34bef55e803175492a9 100644
ecf709
--- a/src/responder/pam/pamsrv.h
ecf709
+++ b/src/responder/pam/pamsrv.h
ecf709
@@ -26,6 +26,7 @@
ecf709
 #include "util/util.h"
ecf709
 #include "sbus/sssd_dbus.h"
ecf709
 #include "responder/common/responder.h"
ecf709
+#include "responder/common/cache_req/cache_req.h"
ecf709
 
ecf709
 struct pam_auth_req;
ecf709
 
ecf709
@@ -42,6 +43,9 @@ struct pam_ctx {
ecf709
     char **public_domains;
ecf709
     int public_domains_count;
ecf709
 
ecf709
+    /* What services are permitted to access application domains */
ecf709
+    char **app_services;
ecf709
+
ecf709
     bool cert_auth;
ecf709
     int p11_child_debug_fd;
ecf709
     char *nss_db;
ecf709
@@ -54,6 +58,7 @@ struct pam_auth_dp_req {
ecf709
 struct pam_auth_req {
ecf709
     struct cli_ctx *cctx;
ecf709
     struct sss_domain_info *domain;
ecf709
+    enum cache_req_dom_type req_dom_type;
ecf709
 
ecf709
     struct pam_data *pd;
ecf709
 
ecf709
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
ecf709
index fa6d2cc10fe1404196f9d9221a469d7a9a768211..f2b3c74b483e527932dda42279d14a9ac184b475 100644
ecf709
--- a/src/responder/pam/pamsrv_cmd.c
ecf709
+++ b/src/responder/pam/pamsrv_cmd.c
ecf709
@@ -1161,6 +1161,25 @@ static bool is_domain_public(char *name,
ecf709
     return false;
ecf709
 }
ecf709
 
ecf709
+static enum cache_req_dom_type
ecf709
+get_domain_request_type(struct pam_auth_req *preq,
ecf709
+                        struct pam_ctx *pctx)
ecf709
+{
ecf709
+    enum cache_req_dom_type req_dom_type;
ecf709
+
ecf709
+    /* By default, only POSIX domains are to be contacted */
ecf709
+    req_dom_type = CACHE_REQ_POSIX_DOM;
ecf709
+
ecf709
+    for (int i = 0; pctx->app_services[i]; i++) {
ecf709
+        if (strcmp(pctx->app_services[i], preq->pd->service) == 0) {
ecf709
+            req_dom_type = CACHE_REQ_APPLICATION_DOM;
ecf709
+            break;
ecf709
+        }
ecf709
+    }
ecf709
+
ecf709
+    return req_dom_type;
ecf709
+}
ecf709
+
ecf709
 static errno_t check_cert(TALLOC_CTX *mctx,
ecf709
                           struct tevent_context *ev,
ecf709
                           struct pam_ctx *pctx,
ecf709
@@ -1257,6 +1276,9 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
ecf709
         goto done;
ecf709
     }
ecf709
 
ecf709
+    /* Determine what domain type to contact */
ecf709
+    preq->req_dom_type = get_domain_request_type(preq, pctx);
ecf709
+
ecf709
     /* try backend first for authentication before doing local Smartcard
ecf709
      * authentication */
ecf709
     if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
ecf709
@@ -1316,7 +1338,7 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
ecf709
 
ecf709
     req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
ecf709
                                       pctx->rctx->ncache, 0,
ecf709
-                                      CACHE_REQ_POSIX_DOM, NULL,
ecf709
+                                      preq->req_dom_type, NULL,
ecf709
                                       cert);
ecf709
     if (req == NULL) {
ecf709
         DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
ecf709
@@ -1509,7 +1531,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
ecf709
                            preq->cctx->rctx,
ecf709
                            preq->cctx->rctx->ncache,
ecf709
                            0,
ecf709
-                           CACHE_REQ_POSIX_DOM,
ecf709
+                           preq->req_dom_type,
ecf709
                            preq->pd->domain,
ecf709
                            data);
ecf709
     if (!dpreq) {
ecf709
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
ecf709
index 847419658bb983e6548722d6fa6fb22c63ee86b8..d249b8f1ea48f1c17b461c3add9e8c63774e5f88 100644
ecf709
--- a/src/tests/cmocka/test_pam_srv.c
ecf709
+++ b/src/tests/cmocka/test_pam_srv.c
ecf709
@@ -186,6 +186,15 @@ struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx)
ecf709
     ret = sss_hash_create(pctx, 10, &pctx->id_table);
ecf709
     assert_int_equal(ret, EOK);
ecf709
 
ecf709
+    /* Two NULLs so that tests can just assign a const to the first slot
ecf709
+     * should they need it. The code iterates until first NULL anyway
ecf709
+     */
ecf709
+    pctx->app_services = talloc_zero_array(pctx, char *, 2);
ecf709
+    if (pctx->app_services == NULL) {
ecf709
+        talloc_free(pctx);
ecf709
+        return NULL;
ecf709
+    }
ecf709
+
ecf709
     return pctx;
ecf709
 }
ecf709
 
ecf709
@@ -495,8 +504,12 @@ int __wrap_pam_dp_send_req(struct pam_auth_req *preq, int timeout)
ecf709
     return EOK;
ecf709
 }
ecf709
 
ecf709
-static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
ecf709
-                           const char *pwd, const char *fa2)
ecf709
+static void mock_input_pam_ex(TALLOC_CTX *mem_ctx,
ecf709
+                              const char *name,
ecf709
+                              const char *pwd,
ecf709
+                              const char *fa2,
ecf709
+                              const char *svc,
ecf709
+                              bool contact_dp)
ecf709
 {
ecf709
     size_t buf_size;
ecf709
     uint8_t *m_buf;
ecf709
@@ -536,7 +549,10 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
ecf709
         }
ecf709
     }
ecf709
 
ecf709
-    pi.pam_service = "pam_test_service";
ecf709
+    if (svc == NULL) {
ecf709
+        svc = "pam_test_service";
ecf709
+    }
ecf709
+    pi.pam_service = svc;
ecf709
     pi.pam_service_size = strlen(pi.pam_service) + 1;
ecf709
     pi.pam_tty = "/dev/tty";
ecf709
     pi.pam_tty_size = strlen(pi.pam_tty) + 1;
ecf709
@@ -559,7 +575,17 @@ static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name,
ecf709
     will_return(__wrap_sss_packet_get_body, buf_size);
ecf709
 
ecf709
     mock_parse_inp(name, NULL, EOK);
ecf709
-    mock_account_recv_simple();
ecf709
+    if (contact_dp) {
ecf709
+        mock_account_recv_simple();
ecf709
+    }
ecf709
+}
ecf709
+
ecf709
+static void mock_input_pam(TALLOC_CTX *mem_ctx,
ecf709
+                           const char *name,
ecf709
+                           const char *pwd,
ecf709
+                           const char *fa2)
ecf709
+{
ecf709
+    return mock_input_pam_ex(mem_ctx, name, pwd, fa2, NULL, true);
ecf709
 }
ecf709
 
ecf709
 static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
ecf709
@@ -2097,6 +2123,127 @@ void test_filter_response(void **state)
ecf709
     talloc_free(pd);
ecf709
 }
ecf709
 
ecf709
+static int pam_test_setup_appsvc_posix_dom(void **state)
ecf709
+{
ecf709
+    int ret;
ecf709
+
ecf709
+    ret = pam_test_setup(state);
ecf709
+    if (ret != EOK) {
ecf709
+        return ret;
ecf709
+    }
ecf709
+
ecf709
+    /* This config option is only read on startup, which is not executed
ecf709
+     * in test, so we can't just pass in a param
ecf709
+     */
ecf709
+    pam_test_ctx->pctx->app_services[0] = discard_const("app_svc");
ecf709
+    return 0;
ecf709
+}
ecf709
+
ecf709
+void test_appsvc_posix_dom(void **state)
ecf709
+{
ecf709
+    int ret;
ecf709
+
ecf709
+    /* The domain is POSIX, the request will skip over it */
ecf709
+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", false);
ecf709
+    pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN;
ecf709
+
ecf709
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
ecf709
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
ecf709
+
ecf709
+    set_cmd_cb(test_pam_user_unknown_check);
ecf709
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
ecf709
+                          pam_test_ctx->pam_cmds);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+
ecf709
+    ret = test_ev_loop(pam_test_ctx->tctx);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+}
ecf709
+
ecf709
+void test_not_appsvc_posix_dom(void **state)
ecf709
+{
ecf709
+    int ret;
ecf709
+
ecf709
+    /* A different service than the app one can authenticate against a POSIX domain */
ecf709
+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", true);
ecf709
+
ecf709
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
ecf709
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
ecf709
+
ecf709
+    set_cmd_cb(test_pam_simple_check);
ecf709
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
ecf709
+                          pam_test_ctx->pam_cmds);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+
ecf709
+    /* Wait until the test finishes with EOK */
ecf709
+    ret = test_ev_loop(pam_test_ctx->tctx);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+}
ecf709
+
ecf709
+static int pam_test_setup_appsvc_app_dom(void **state)
ecf709
+{
ecf709
+    struct sss_test_conf_param dom_params[] = {
ecf709
+        { "domain_type", "application" },
ecf709
+        { NULL, NULL },             /* Sentinel */
ecf709
+    };
ecf709
+    struct sss_test_conf_param pam_params[] = {
ecf709
+        { NULL, NULL },             /* Sentinel */
ecf709
+    };
ecf709
+    struct sss_test_conf_param monitor_params[] = {
ecf709
+        { NULL, NULL },             /* Sentinel */
ecf709
+    };
ecf709
+
ecf709
+
ecf709
+    test_pam_setup(dom_params, pam_params, monitor_params, state);
ecf709
+    pam_test_setup_common();
ecf709
+
ecf709
+    /* This config option is only read on startup, which is not executed
ecf709
+     * in test, so we can't just pass in a param
ecf709
+     */
ecf709
+    pam_test_ctx->pctx->app_services[0] = discard_const("app_svc");
ecf709
+    return 0;
ecf709
+}
ecf709
+
ecf709
+void test_appsvc_app_dom(void **state)
ecf709
+{
ecf709
+    int ret;
ecf709
+
ecf709
+    /* The domain is POSIX, the request will skip over it */
ecf709
+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", true);
ecf709
+
ecf709
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
ecf709
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
ecf709
+
ecf709
+    set_cmd_cb(test_pam_simple_check);
ecf709
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
ecf709
+                          pam_test_ctx->pam_cmds);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+
ecf709
+    /* Wait until the test finishes with EOK */
ecf709
+    ret = test_ev_loop(pam_test_ctx->tctx);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+}
ecf709
+
ecf709
+void test_not_appsvc_app_dom(void **state)
ecf709
+{
ecf709
+    int ret;
ecf709
+
ecf709
+    /* A different service than the app one can authenticate against a POSIX domain */
ecf709
+    mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", false);
ecf709
+
ecf709
+    pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN;
ecf709
+
ecf709
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
ecf709
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
ecf709
+
ecf709
+    set_cmd_cb(test_pam_user_unknown_check);
ecf709
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
ecf709
+                          pam_test_ctx->pam_cmds);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+
ecf709
+    ret = test_ev_loop(pam_test_ctx->tctx);
ecf709
+    assert_int_equal(ret, EOK);
ecf709
+}
ecf709
+
ecf709
 int main(int argc, const char *argv[])
ecf709
 {
ecf709
     int rv;
ecf709
@@ -2216,6 +2363,18 @@ int main(int argc, const char *argv[])
ecf709
 
ecf709
         cmocka_unit_test_setup_teardown(test_filter_response,
ecf709
                                         pam_test_setup, pam_test_teardown),
ecf709
+        cmocka_unit_test_setup_teardown(test_appsvc_posix_dom,
ecf709
+                                        pam_test_setup_appsvc_posix_dom,
ecf709
+                                        pam_test_teardown),
ecf709
+        cmocka_unit_test_setup_teardown(test_not_appsvc_posix_dom,
ecf709
+                                        pam_test_setup_appsvc_posix_dom,
ecf709
+                                        pam_test_teardown),
ecf709
+        cmocka_unit_test_setup_teardown(test_appsvc_app_dom,
ecf709
+                                        pam_test_setup_appsvc_app_dom,
ecf709
+                                        pam_test_teardown),
ecf709
+        cmocka_unit_test_setup_teardown(test_not_appsvc_app_dom,
ecf709
+                                        pam_test_setup_appsvc_posix_dom,
ecf709
+                                        pam_test_teardown),
ecf709
     };
ecf709
 
ecf709
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
ecf709
-- 
ecf709
2.9.3
ecf709