Blob Blame History Raw
From 855201c70f69f2b1dbcb3faef780fbdb84354f18 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Sun, 26 Mar 2017 18:28:41 +0200
Subject: [PATCH 67/72] PAM: Add application services

Related to:
https://pagure.io/SSSD/sssd/issue/3310

Adds a new PAM responder option 'pam_app_services'. This option can hold
a list of PAM services that are allowed to contact the application
non-POSIX domains. These services are NOT allowed to contact any of the
POSIX domains.

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 src/confdb/confdb.h                  |   1 +
 src/config/SSSDConfig/__init__.py.in |   1 +
 src/config/cfg_rules.ini             |   1 +
 src/config/etc/sssd.api.conf         |   1 +
 src/man/sssd.conf.5.xml              |  12 +++
 src/responder/pam/pamsrv.c           |  33 +++++++
 src/responder/pam/pamsrv.h           |   5 ++
 src/responder/pam/pamsrv_cmd.c       |  26 +++++-
 src/tests/cmocka/test_pam_srv.c      | 167 ++++++++++++++++++++++++++++++++++-
 9 files changed, 241 insertions(+), 6 deletions(-)

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