From 29f9cac96134151c6b2d74a991e0379fefa32f43 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Wed, 25 Jan 2012 03:00:47 -0500
Subject: [PATCH 09/10] SELinux support in PAM responder

---
 src/responder/pam/pamsrv_cmd.c |  142 ++++++++++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h       |    4 +
 2 files changed, 146 insertions(+), 0 deletions(-)

diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index c98b4ac076ec3564cff6b4f3605fa488cb5cf422..847caa52fd8dc603dcaa064a2664dd82552dd01d 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -31,6 +31,7 @@
 #include "responder/pam/pamsrv.h"
 #include "responder/pam/pam_helpers.h"
 #include "db/sysdb.h"
+#include "db/sysdb_selinux.h"
 
 enum pam_verbosity {
     PAM_VERBOSITY_NO_MESSAGES = 0,
@@ -352,6 +353,138 @@ fail:
     return ret;
 }
 
+static errno_t get_selinux_string(struct pam_auth_req *preq)
+{
+    struct sysdb_ctx *sysdb;
+    TALLOC_CTX *tmp_ctx;
+    struct pam_data *pd = preq->pd;
+    char *file_content = NULL;
+    struct ldb_message **usermaps;
+    struct ldb_message *config;
+    const char *default_user;
+    char *tmp_str;
+    char *order;
+    char **order_array;
+    errno_t ret;
+    int i, j, k;
+    size_t order_count;
+    size_t len;
+    bool map_found;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
+                                  preq->domain, &sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_search_selinux_config(tmp_ctx, sysdb, NULL, &config);
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, ("No SELinux support found for the domain\n"));
+        ret = EOK;
+        goto done;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+    for (i = 0; i < config->num_elements; i++) {
+        if (strcasecmp(config->elements[i].name, SYSDB_SELINUX_DEFAULT_USER) == 0) {
+            default_user = (const char *)config->elements[i].values[0].data;
+        } else if (strcasecmp(config->elements[i].name, SYSDB_SELINUX_DEFAULT_ORDER) == 0) {
+            tmp_str = (char *)config->elements[i].values[0].data;
+            order = talloc_strdup(tmp_ctx, tmp_str);
+            if (order == NULL) {
+                goto done;
+            }
+        }
+    }
+
+    if (default_user == NULL || order == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("No default SELinux user "
+                                  "or map order given!\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    order_count = 1;
+    len = strlen(order);
+    for (i = 0; i < len; i++) {
+        if (order[i] == '$') order_count++;
+    }
+
+    order_array = talloc_array(tmp_ctx, char *, order_count);
+    if (order_array == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    order_array[0] = order;
+    order_count = 1;
+    for (i = 0; i < len; i++) {
+        if (order[i] == '$') {
+            order[i] = '\0';
+            order_array[order_count] = &order[i+1];
+            order_count++;
+        }
+    }
+
+    ret = sysdb_search_selinux_usermap_by_user(tmp_ctx, sysdb, pd->user,
+                                               &usermaps);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (usermaps[0] == NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No user maps found, using default!"));
+        file_content = talloc_strdup(tmp_ctx, default_user);
+        if (file_content == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        file_content = talloc_strdup(tmp_ctx, "");
+        if (file_content == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        for (i = 0; i < order_count; i++) {
+            map_found = false;
+            for (j = 0; usermaps[j] != NULL; j++) {
+                for (k = 0; k < usermaps[j]->num_elements; k++) {
+                    tmp_str = (char *)usermaps[j]->elements[k].values[0].data;
+                    if (strcasecmp(usermaps[j]->elements[k].name,
+                                   SYSDB_SELINUX_USER) == 0 &&
+                        strcasecmp(tmp_str, order_array[i]) == 0) {
+                        file_content = talloc_asprintf_append(file_content,
+                                                              "%s\n",
+                                                              order_array[i]);
+                        if (file_content == NULL) {
+                            ret = ENOMEM;
+                            goto done;
+                        }
+                        map_found = true;
+                        break;
+                    }
+                }
+                if (map_found) break;
+            }
+        }
+    }
+
+    ret = pam_add_response(pd, SSS_PAM_SELINUX_MAP, strlen(file_content),
+                           (uint8_t *)file_content);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 static errno_t filter_responses(struct confdb_ctx *cdb,
                                 struct response_data *resp_list)
 {
@@ -580,6 +713,15 @@ static void pam_reply(struct pam_auth_req *preq)
         return;
     }
 
+    if (pd->cmd == SSS_PAM_OPEN_SESSION) {
+        /* Try to fetch data from sysdb
+         * (auth already passed -> we should have them) */
+        ret = get_selinux_string(preq);
+        if (ret != EOK) {
+            pd->pam_status = PAM_SYSTEM_ERR;
+        }
+    }
+
     ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
                          &cctx->creq->out);
     if (ret != EOK) {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 30a238ec7eb9c8e38b1a724df60fea1b15a8831b..0a3bf70921603d8eb98cf2d19052b44fc4bd79ff 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -350,6 +350,10 @@ enum response_type {
                           * the user.This should only be used in the case where
                           * it is not possile to use SSS_PAM_USER_INFO.
                           * @param A zero terminated string. */
+    SSS_PAM_SELINUX_MAP, /**< A content of a SELinux user mapping file. This
+                          * file should be then written to a particular
+                          * subdir in /etc/selinux for pam_selinux to read
+                          * @param A zero terminated string. */
 };
 
 /**
-- 
1.7.6.4

