>From 841b390c2540f6fb251cc76ca37bc0051b3c31dd Mon Sep 17 00:00:00 2001
From: Daniel Gollub <dgollub at brocade.com>
Date: Sat, 27 Sep 2014 12:06:44 +0100
Subject: [PATCH 2/2] PAM: Add domains= option to pam_sss

Design document:
https://fedorahosted.org/sssd/wiki/DesignDocs/RestrictDomainsInPAM

Fixes:
https://fedorahosted.org/sssd/ticket/1021

Signed-off-by: Pavel Reichl <preichl@redhat.com>
Reviewed-by: Sven-Thorsten Dietrich <sven at brocade.com>
---
 src/providers/data_provider.h  |  1 +
 src/responder/pam/pamsrv_cmd.c | 51 +++++++++++++++++++++++++++++++++++++++++-
 src/sss_client/pam_sss.c       | 29 ++++++++++++++++++++++--
 src/sss_client/sss_cli.h       |  1 +
 4 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index b1279852e0078c9d13d320aa5dcf941be023a880..ec5b79d83145ec71f0ea93d6ecfa06386cd7f5cc 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -172,6 +172,7 @@ struct pam_data {
     char *tty;
     char *ruser;
     char *rhost;
+    char **requested_domains;
     struct sss_auth_token *authtok;
     struct sss_auth_token *newauthtok;
     uint32_t cli_pid;
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index eb6953a74a539a102b7f64dfab1c8134dc77faa6..c135e3c4963a9e8bc9241875f35ea0df564750ae 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -44,6 +44,28 @@ enum pam_verbosity {
 
 static void pam_reply(struct pam_auth_req *preq);
 
+static bool is_domain_requested(struct pam_data *pd, const char *domain_name)
+{
+    int i;
+
+    /* If none specific domains got requested via pam, all domains are allowed.
+     * Which mimics the default/original behaviour.
+     */
+    if (!pd->requested_domains) {
+        return true;
+    }
+
+    for (i = 0; pd->requested_domains[i]; i++) {
+        if (strcmp(domain_name, pd->requested_domains[i])) {
+            continue;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
 static int extract_authtok_v2(struct sss_auth_token *tok,
                               size_t data_size, uint8_t *body, size_t blen,
                               size_t *c)
@@ -143,6 +165,7 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
     int ret;
     uint32_t start;
     uint32_t terminator;
+    char *requested_domains;
 
     if (blen < 4*sizeof(uint32_t)+2) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Received data is invalid.\n");
@@ -194,6 +217,20 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
                     ret = extract_string(&pd->rhost, size, body, blen, &c);
                     if (ret != EOK) return ret;
                     break;
+                case SSS_PAM_ITEM_REQUESTED_DOMAINS:
+                    ret = extract_string(&requested_domains, size, body, blen,
+                                         &c);
+                    if (ret != EOK) return ret;
+
+                    ret = split_on_separator(pd, requested_domains, ',', true,
+                                             true, &pd->requested_domains,
+                                             NULL);
+                    if (ret != EOK) {
+                        DEBUG(SSSDBG_CRIT_FAILURE,
+                              "Failed to parse requested_domains list!\n");
+                        return ret;
+                    }
+                    break;
                 case SSS_PAM_ITEM_CLI_PID:
                     ret = extract_uint32_t(&pd->cli_pid, size,
                                            body, blen, &c);
@@ -879,6 +916,12 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
             ret = ENOENT;
             goto done;
         }
+
+        /* skip this domain if not requested */
+        if (!is_domain_requested(pd, pd->domain)) {
+            ret = ENOENT;
+            goto done;
+        }
     } else {
         for (dom = preq->cctx->rctx->domains;
              dom;
@@ -896,6 +939,11 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
                 continue;
             }
 
+            /* skip this domain if not requested */
+            if (!is_domain_requested(pd, dom->name)) {
+                continue;
+            }
+
             ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
                                           dom, pd->user);
             if (ncret == ENOENT) {
@@ -910,7 +958,8 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
                   "User [%s@%s] filtered out (negative cache). "
                    "Trying next domain.\n", pd->user, dom->name);
         }
-        if (!dom) {
+
+        if (!dom || !is_domain_requested(pd, dom->name)) {
             ret = ENOENT;
             goto done;
         }
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index d2502d13d3f7fe63f32f2b648fce38ca1c92f582..abe9b05478cbf480b3430dccd1951e9bfb0e29c1 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -58,6 +58,7 @@
 #define PW_RESET_MSG_MAX_SIZE 4096
 
 #define OPT_RETRY_KEY "retry="
+#define OPT_DOMAINS_KEY "domains="
 
 struct pam_items {
     const char* pam_service;
@@ -81,6 +82,8 @@ struct pam_items {
     pid_t cli_pid;
     const char *login_name;
     char *domain_name;
+    const char *requested_domains;
+    size_t requested_domains_size;
 };
 
 #define DEBUG_MGS_LEN 1024
@@ -246,6 +249,9 @@ static int pack_message_v3(struct pam_items *pi, size_t *size,
     len += pi->pam_newauthtok != NULL ?
                 3*sizeof(uint32_t) + pi->pam_newauthtok_size : 0;
     len += 3*sizeof(uint32_t); /* cli_pid */
+    len += *pi->requested_domains != '\0' ?
+                2*sizeof(uint32_t) + pi->requested_domains_size : 0;
+
 
     buf = malloc(len);
     if (buf == NULL) {
@@ -271,6 +277,9 @@ static int pack_message_v3(struct pam_items *pi, size_t *size,
     rp += add_string_item(SSS_PAM_ITEM_RHOST, pi->pam_rhost, pi->pam_rhost_size,
                           &buf[rp]);
 
+    rp += add_string_item(SSS_PAM_ITEM_REQUESTED_DOMAINS, pi->requested_domains, pi->requested_domains_size,
+                          &buf[rp]);
+
     rp += add_uint32_t_item(SSS_PAM_ITEM_CLI_PID, (uint32_t) pi->cli_pid,
                             &buf[rp]);
 
@@ -1061,6 +1070,9 @@ static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi)
 
     pi->domain_name = NULL;
 
+    if (pi->requested_domains == NULL) pi->requested_domains = "";
+    pi->requested_domains_size = strlen(pi->requested_domains) + 1;
+
     return PAM_SUCCESS;
 }
 
@@ -1080,6 +1092,7 @@ static void print_pam_items(struct pam_items *pi)
     D(("Authtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_authtok)));
     D(("Newauthtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_newauthtok)));
     D(("Cli_PID: %d", pi->cli_pid));
+    D(("Requested domains: %s", pi->requested_domains));
 }
 
 static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi,
@@ -1271,7 +1284,8 @@ static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi)
 }
 
 static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
-                      uint32_t *flags, int *retries, bool *quiet_mode)
+                      uint32_t *flags, int *retries, bool *quiet_mode,
+                      const char **domains)
 {
     char *ep;
 
@@ -1284,6 +1298,14 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
             *flags |= FLAGS_USE_FIRST_PASS;
         } else if (strcmp(*argv, "use_authtok") == 0) {
             *flags |= FLAGS_USE_AUTHTOK;
+        } else if (strncmp(*argv, OPT_DOMAINS_KEY, strlen(OPT_DOMAINS_KEY)) == 0) {
+            if (*(*argv+strlen(OPT_DOMAINS_KEY)) == '\0') {
+                logger(pamh, LOG_ERR, "Missing argument to option domains.");
+                *domains = "";
+            } else {
+                *domains = *argv+strlen(OPT_DOMAINS_KEY);
+            }
+
         } else if (strncmp(*argv, OPT_RETRY_KEY, strlen(OPT_RETRY_KEY)) == 0) {
             if (*(*argv+6) == '\0') {
                 logger(pamh, LOG_ERR, "Missing argument to option retry.");
@@ -1443,12 +1465,15 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
     bool retry = false;
     bool quiet_mode = false;
     int retries = 0;
+    const char *domains = NULL;
 
     bindtextdomain(PACKAGE, LOCALEDIR);
 
     D(("Hello pam_sssd: %d", task));
 
-    eval_argv(pamh, argc, argv, &flags, &retries, &quiet_mode);
+    eval_argv(pamh, argc, argv, &flags, &retries, &quiet_mode, &domains);
+
+    pi.requested_domains = domains;
 
     ret = get_pam_items(pamh, &pi);
     if (ret != PAM_SUCCESS) {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index a0c635f727b0e4a42570f99c4f6f44ed4260503d..edb520c8e7c22b871c88d75743feb67281627a3d 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -314,6 +314,7 @@ enum pam_item_type {
     SSS_PAM_ITEM_NEWAUTHTOK,
     SSS_PAM_ITEM_CLI_LOCALE,
     SSS_PAM_ITEM_CLI_PID,
+    SSS_PAM_ITEM_REQUESTED_DOMAINS,
 };
 
 #define SSS_NSS_MAX_ENTRIES 256
-- 
1.9.3

