Blame SOURCES/0071-IPA-Move-setting-the-SELinux-context-to-a-child-proc.patch

905b4d
From e08eb3e3b0586b331a688a4eded2d7f13611016c Mon Sep 17 00:00:00 2001
905b4d
From: Jakub Hrozek <jhrozek@redhat.com>
905b4d
Date: Mon, 20 Oct 2014 23:16:40 +0200
905b4d
Subject: [PATCH 71/71] IPA: Move setting the SELinux context to a child
905b4d
 process
905b4d
MIME-Version: 1.0
905b4d
Content-Type: text/plain; charset=UTF-8
905b4d
Content-Transfer-Encoding: 8bit
905b4d
905b4d
In order for the sssd_be process to run as unprivileged user, we need to
905b4d
move the semanage processing to a process that runs as the root user
905b4d
using setuid privileges.
905b4d
905b4d
Reviewed-by: Michal Židek <mzidek@redhat.com>
905b4d
---
905b4d
 Makefile.am                       |  27 +++
905b4d
 contrib/sssd.spec.in              |   1 +
905b4d
 src/providers/ipa/ipa_selinux.c   | 425 +++++++++++++++++++++++++++++++++++---
905b4d
 src/providers/ipa/selinux_child.c | 272 ++++++++++++++++++++++++
905b4d
 src/util/util_errors.c            |   1 +
905b4d
 src/util/util_errors.h            |   1 +
905b4d
 6 files changed, 699 insertions(+), 28 deletions(-)
905b4d
 create mode 100644 src/providers/ipa/selinux_child.c
905b4d
905b4d
diff --git a/Makefile.am b/Makefile.am
905b4d
index ea296c40f89c552d5825cbce8e95afdc517e8bb5..b85341f5845c3cffab8a2c95b1be1d32517316e8 100644
905b4d
--- a/Makefile.am
905b4d
+++ b/Makefile.am
905b4d
@@ -146,6 +146,9 @@ endif
905b4d
 if BUILD_SAMBA
905b4d
 sssdlibexec_PROGRAMS += gpo_child
905b4d
 endif
905b4d
+if BUILD_SEMANAGE
905b4d
+sssdlibexec_PROGRAMS += selinux_child
905b4d
+endif
905b4d
 
905b4d
 
905b4d
 if BUILD_PAC_RESPONDER
905b4d
@@ -2531,6 +2534,26 @@ ldap_child_LDADD = \
905b4d
     $(DHASH_LIBS) \
905b4d
     $(KRB5_LIBS)
905b4d
 
905b4d
+if BUILD_SEMANAGE
905b4d
+selinux_child_SOURCES = \
905b4d
+    src/providers/ipa/selinux_child.c \
905b4d
+    src/util/sss_semanage.c \
905b4d
+    src/util/atomic_io.c \
905b4d
+    src/util/util.c \
905b4d
+    $(NULL)
905b4d
+selinux_child_CFLAGS = \
905b4d
+    $(AM_CFLAGS) \
905b4d
+    $(POPT_CFLAGS) \
905b4d
+    $(NULL)
905b4d
+selinux_child_LDADD = \
905b4d
+    libsss_debug.la \
905b4d
+    $(TALLOC_LIBS) \
905b4d
+    $(POPT_LIBS) \
905b4d
+    $(DHASH_LIBS) \
905b4d
+    $(SEMANAGE_LIBS) \
905b4d
+    $(NULL)
905b4d
+endif
905b4d
+
905b4d
 gpo_child_SOURCES = \
905b4d
     src/providers/ad/ad_gpo_child.c \
905b4d
     src/util/atomic_io.c \
905b4d
@@ -2849,6 +2872,10 @@ endif
905b4d
 if SSSD_USER
905b4d
 	chgrp $(SSSD_USER) $(sssdlibexecdir)/ldap_child
905b4d
 	chmod 4750 $(sssdlibexecdir)/ldap_child
905b4d
+if BUILD_SEMANAGE
905b4d
+	chgrp $(SSSD_USER) $(sssdlibexecdir)/selinux_child
905b4d
+	chmod 4750 $(sssdlibexecdir)/selinux_child
905b4d
+endif
905b4d
 endif
905b4d
 
905b4d
 install-data-hook:
905b4d
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
905b4d
index d2e6cec2610e4c00cb376683cf7e64eb5cdafc5c..5bfb16707c22dc65376581c88b8eb898949e726f 100644
905b4d
--- a/contrib/sssd.spec.in
905b4d
+++ b/contrib/sssd.spec.in
905b4d
@@ -665,6 +665,7 @@ rm -rf $RPM_BUILD_ROOT
905b4d
 %doc COPYING
905b4d
 %attr(755,root,root) %dir %{pubconfpath}/krb5.include.d
905b4d
 %{_libdir}/%{name}/libsss_ipa.so
905b4d
+%attr(4750,root,sssd) %{_libexecdir}/%{servicename}/selinux_child
905b4d
 %{_mandir}/man5/sssd-ipa.5*
905b4d
 
905b4d
 %files ad -f sssd_ad.lang
905b4d
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
905b4d
index 7db1c6ed2981e4c4a85d892171bbfa60b006980e..b392d82a61cd523cd5e73e5246013bdbc448ddd5 100644
905b4d
--- a/src/providers/ipa/ipa_selinux.c
905b4d
+++ b/src/providers/ipa/ipa_selinux.c
905b4d
@@ -24,6 +24,7 @@
905b4d
 #include <security/pam_modules.h>
905b4d
 
905b4d
 #include "db/sysdb_selinux.h"
905b4d
+#include "util/child_common.h"
905b4d
 #include "util/sss_selinux.h"
905b4d
 #include "providers/ldap/sdap_async.h"
905b4d
 #include "providers/ipa/ipa_common.h"
905b4d
@@ -37,8 +38,23 @@
905b4d
 #include "providers/ipa/ipa_subdomains.h"
905b4d
 
905b4d
 #if defined HAVE_SELINUX && defined HAVE_SELINUX_LOGIN_DIR
905b4d
+
905b4d
+#ifndef SELINUX_CHILD_DIR
905b4d
+#ifndef SSSD_LIBEXEC_PATH
905b4d
+#error "SSSD_LIBEXEC_PATH not defined"
905b4d
+#endif  /* SSSD_LIBEXEC_PATH */
905b4d
+
905b4d
+#define SELINUX_CHILD_DIR SSSD_LIBEXEC_PATH
905b4d
+#endif /* SELINUX_CHILD_DIR */
905b4d
+
905b4d
+#define SELINUX_CHILD SELINUX_CHILD_DIR"/selinux_child"
905b4d
+#define SELINUX_CHILD_LOG_FILE "selinux_child"
905b4d
+
905b4d
 #include <selinux/selinux.h>
905b4d
 
905b4d
+/* fd used by the selinux_child process for logging */
905b4d
+int selinux_child_debug_fd = -1;
905b4d
+
905b4d
 static struct tevent_req *
905b4d
 ipa_get_selinux_send(TALLOC_CTX *mem_ctx,
905b4d
                      struct be_ctx *be_ctx,
905b4d
@@ -274,12 +290,27 @@ struct map_order_ctx {
905b4d
 
905b4d
 static errno_t init_map_order_ctx(TALLOC_CTX *mem_ctx, const char *map_order,
905b4d
                                   struct map_order_ctx **_mo_ctx);
905b4d
-static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
905b4d
+
905b4d
+struct selinux_child_input {
905b4d
+    const char *seuser;
905b4d
+    const char *mls_range;
905b4d
+    const char *username;
905b4d
+};
905b4d
+
905b4d
+static errno_t choose_best_seuser(TALLOC_CTX *mem_ctx,
905b4d
+                                  struct sysdb_attrs **usermaps,
905b4d
                                   struct pam_data *pd,
905b4d
                                   struct sss_domain_info *user_domain,
905b4d
                                   struct map_order_ctx *mo_ctx,
905b4d
-                                  const char *default_user);
905b4d
+                                  const char *default_user,
905b4d
+                                  struct selinux_child_input **_sci);
905b4d
 
905b4d
+static struct tevent_req *selinux_child_send(TALLOC_CTX *mem_ctx,
905b4d
+                                             struct tevent_context *ev,
905b4d
+                                             struct selinux_child_input *sci);
905b4d
+static errno_t selinux_child_recv(struct tevent_req *req);
905b4d
+
905b4d
+static void ipa_selinux_child_done(struct tevent_req *child_req);
905b4d
 
905b4d
 static void ipa_selinux_handler_done(struct tevent_req *req)
905b4d
 {
905b4d
@@ -299,6 +330,8 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
905b4d
     struct sysdb_attrs **hbac_rules = 0;
905b4d
     struct sysdb_attrs **best_match_maps;
905b4d
     struct map_order_ctx *map_order_ctx;
905b4d
+    struct selinux_child_input *sci;
905b4d
+    struct tevent_req *child_req;
905b4d
 
905b4d
     ret = ipa_get_selinux_recv(req, breq, &map_count, &maps,
905b4d
                                &hbac_count, &hbac_rules,
905b4d
@@ -360,14 +393,61 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
905b4d
         goto fail;
905b4d
     }
905b4d
 
905b4d
-    ret = choose_best_seuser(best_match_maps, pd, op_ctx->user_domain,
905b4d
-                             map_order_ctx, default_user);
905b4d
+    ret = choose_best_seuser(breq,
905b4d
+                             best_match_maps, pd, op_ctx->user_domain,
905b4d
+                             map_order_ctx, default_user, &sci);
905b4d
     if (ret != EOK) {
905b4d
         DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
               "Failed to evaluate ordered SELinux users array.\n");
905b4d
         goto fail;
905b4d
     }
905b4d
 
905b4d
+    /* Update the SELinux context in a privileged child as the back end is
905b4d
+     * running unprivileged
905b4d
+     */
905b4d
+    child_req = selinux_child_send(breq, be_ctx->ev, sci);
905b4d
+    if (child_req == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "selinux_child_send() failed\n");
905b4d
+        ret = ENOMEM;
905b4d
+        goto fail;
905b4d
+    }
905b4d
+    tevent_req_set_callback(child_req, ipa_selinux_child_done, op_ctx);
905b4d
+    return;
905b4d
+
905b4d
+fail:
905b4d
+    if (in_transaction) {
905b4d
+        sret = sysdb_transaction_cancel(sysdb);
905b4d
+        if (sret != EOK) {
905b4d
+            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
905b4d
+        }
905b4d
+    }
905b4d
+    if (ret == EAGAIN) {
905b4d
+        be_req_terminate(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
905b4d
+    } else {
905b4d
+        be_req_terminate(breq, DP_ERR_FATAL, ret, NULL);
905b4d
+    }
905b4d
+}
905b4d
+
905b4d
+static void ipa_selinux_child_done(struct tevent_req *child_req)
905b4d
+{
905b4d
+    errno_t ret;
905b4d
+    struct ipa_selinux_op_ctx *op_ctx;
905b4d
+    struct be_req *breq;
905b4d
+    struct pam_data *pd;
905b4d
+    struct be_ctx *be_ctx;
905b4d
+
905b4d
+    op_ctx = tevent_req_callback_data(child_req, struct ipa_selinux_op_ctx);
905b4d
+    breq = op_ctx->be_req;
905b4d
+    pd = talloc_get_type(be_req_get_data(breq), struct pam_data);
905b4d
+    be_ctx = be_req_get_be_ctx(breq);
905b4d
+
905b4d
+    ret = selinux_child_recv(child_req);
905b4d
+    talloc_free(child_req);
905b4d
+    if (ret != EOK) {
905b4d
+        be_req_terminate(breq, DP_ERR_FATAL, ret, NULL);
905b4d
+        return;
905b4d
+    }
905b4d
+
905b4d
     /* If we got here in online mode, set last_update to current time */
905b4d
     if (!be_is_offline(be_ctx)) {
905b4d
         op_ctx->selinux_ctx->last_update = time(NULL);
905b4d
@@ -375,20 +455,6 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
905b4d
 
905b4d
     pd->pam_status = PAM_SUCCESS;
905b4d
     be_req_terminate(breq, DP_ERR_OK, EOK, "Success");
905b4d
-    return;
905b4d
-
905b4d
-fail:
905b4d
-    if (in_transaction) {
905b4d
-        sret = sysdb_transaction_cancel(sysdb);
905b4d
-        if (sret != EOK) {
905b4d
-            DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n");
905b4d
-        }
905b4d
-    }
905b4d
-    if (ret == EAGAIN) {
905b4d
-        be_req_terminate(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
905b4d
-    } else {
905b4d
-        be_req_terminate(breq, DP_ERR_FATAL, ret, NULL);
905b4d
-    }
905b4d
 }
905b4d
 
905b4d
 static errno_t
905b4d
@@ -652,24 +718,28 @@ done:
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
-static errno_t
905b4d
-set_seuser_helper(const char *orig_name, struct sss_domain_info *dom,
905b4d
-                  const char *seuser_mls_string);
905b4d
-
905b4d
+static errno_t selinux_child_setup(TALLOC_CTX *mem_ctx,
905b4d
+                                   const char *orig_name,
905b4d
+                                   struct sss_domain_info *dom,
905b4d
+                                   const char *seuser_mls_string,
905b4d
+                                   struct selinux_child_input **_sci);
905b4d
 
905b4d
 /* Choose best selinux user based on given order and write
905b4d
  * the user to selinux login file. */
905b4d
-static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
905b4d
+static errno_t choose_best_seuser(TALLOC_CTX *mem_ctx,
905b4d
+                                  struct sysdb_attrs **usermaps,
905b4d
                                   struct pam_data *pd,
905b4d
                                   struct sss_domain_info *user_domain,
905b4d
                                   struct map_order_ctx *mo_ctx,
905b4d
-                                  const char *default_user)
905b4d
+                                  const char *default_user,
905b4d
+                                  struct selinux_child_input **_sci)
905b4d
 {
905b4d
     TALLOC_CTX *tmp_ctx;
905b4d
     char *seuser_mls_str = NULL;
905b4d
     const char *tmp_str;
905b4d
     errno_t ret;
905b4d
     int i, j;
905b4d
+    struct selinux_child_input *sci;
905b4d
 
905b4d
     tmp_ctx = talloc_new(NULL);
905b4d
     if (tmp_ctx == NULL) {
905b4d
@@ -716,15 +786,25 @@ static errno_t choose_best_seuser(struct sysdb_attrs **usermaps,
905b4d
         }
905b4d
     }
905b4d
 
905b4d
-    ret = set_seuser_helper(pd->user, user_domain, seuser_mls_str);
905b4d
+    ret = selinux_child_setup(tmp_ctx, pd->user, user_domain, seuser_mls_str, &sci);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set up child input buffer");
905b4d
+        goto done;
905b4d
+    }
905b4d
+
905b4d
+    *_sci = talloc_steal(mem_ctx, sci);
905b4d
+    ret = EOK;
905b4d
 done:
905b4d
     talloc_free(tmp_ctx);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
 static errno_t
905b4d
-set_seuser_helper(const char *orig_name, struct sss_domain_info *dom,
905b4d
-                  const char *seuser_mls_string)
905b4d
+selinux_child_setup(TALLOC_CTX *mem_ctx,
905b4d
+                    const char *orig_name,
905b4d
+                    struct sss_domain_info *dom,
905b4d
+                    const char *seuser_mls_string,
905b4d
+                    struct selinux_child_input **_sci)
905b4d
 {
905b4d
     errno_t ret;
905b4d
     char *seuser;
905b4d
@@ -733,6 +813,7 @@ set_seuser_helper(const char *orig_name, struct sss_domain_info *dom,
905b4d
     char *username;
905b4d
     char *username_final;
905b4d
     TALLOC_CTX *tmp_ctx;
905b4d
+    struct selinux_child_input *sci;
905b4d
 
905b4d
     tmp_ctx = talloc_new(NULL);
905b4d
     if (tmp_ctx == NULL) {
905b4d
@@ -778,12 +859,300 @@ set_seuser_helper(const char *orig_name, struct sss_domain_info *dom,
905b4d
         username_final = username;
905b4d
     }
905b4d
 
905b4d
-    ret = set_seuser(username_final, seuser, mls_range);
905b4d
+    sci = talloc(tmp_ctx, struct selinux_child_input);
905b4d
+    if (sci == NULL) {
905b4d
+        ret = ENOMEM;
905b4d
+        goto done;
905b4d
+    }
905b4d
+
905b4d
+    sci->seuser = talloc_strdup(sci, seuser);
905b4d
+    sci->mls_range = talloc_strdup(sci, mls_range);
905b4d
+    sci->username = talloc_strdup(sci, username);
905b4d
+    if (sci->seuser == NULL || sci->mls_range == NULL
905b4d
+        || sci->username == NULL) {
905b4d
+        ret = ENOMEM;
905b4d
+        goto done;
905b4d
+    }
905b4d
+
905b4d
+    *_sci = talloc_steal(mem_ctx, sci);
905b4d
+    ret = EOK;
905b4d
 done:
905b4d
     talloc_free(tmp_ctx);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
+struct selinux_child_state {
905b4d
+    struct selinux_child_input *sci;
905b4d
+    struct tevent_context *ev;
905b4d
+    struct io_buffer *buf;
905b4d
+    struct child_io_fds *io;
905b4d
+};
905b4d
+
905b4d
+static errno_t selinux_child_init(void);
905b4d
+static errno_t selinux_child_create_buffer(struct selinux_child_state *state);
905b4d
+static errno_t selinux_fork_child(struct selinux_child_state *state);
905b4d
+static void selinux_child_step(struct tevent_req *subreq);
905b4d
+static void selinux_child_done(struct tevent_req *subreq);
905b4d
+static errno_t selinux_child_parse_response(uint8_t *buf, ssize_t len,
905b4d
+                                            uint32_t *_child_result);
905b4d
+
905b4d
+static struct tevent_req *selinux_child_send(TALLOC_CTX *mem_ctx,
905b4d
+                                             struct tevent_context *ev,
905b4d
+                                             struct selinux_child_input *sci)
905b4d
+{
905b4d
+    struct tevent_req *req;
905b4d
+    struct tevent_req *subreq;
905b4d
+    struct selinux_child_state *state;
905b4d
+    errno_t ret;
905b4d
+
905b4d
+    req = tevent_req_create(mem_ctx, &state, struct selinux_child_state);
905b4d
+    if (req == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
905b4d
+        return NULL;
905b4d
+    }
905b4d
+
905b4d
+    state->sci = sci;
905b4d
+    state->ev = ev;
905b4d
+    state->io = talloc(state, struct child_io_fds);
905b4d
+    state->buf = talloc(state, struct io_buffer);
905b4d
+    if (state->io == NULL || state->buf == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
905b4d
+        ret = ENOMEM;
905b4d
+        goto immediately;
905b4d
+    }
905b4d
+
905b4d
+    state->io->write_to_child_fd = -1;
905b4d
+    state->io->read_from_child_fd = -1;
905b4d
+    talloc_set_destructor((void *) state->io, child_io_destructor);
905b4d
+
905b4d
+    ret = selinux_child_init();
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to init the child\n");
905b4d
+        goto immediately;
905b4d
+    }
905b4d
+
905b4d
+    ret = selinux_child_create_buffer(state);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to create the send buffer\n");
905b4d
+        ret = ENOMEM;
905b4d
+        goto immediately;
905b4d
+    }
905b4d
+
905b4d
+    ret = selinux_fork_child(state);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to fork the child\n");
905b4d
+        goto immediately;
905b4d
+    }
905b4d
+
905b4d
+    subreq = write_pipe_send(state, ev, state->buf->data, state->buf->size,
905b4d
+                             state->io->write_to_child_fd);
905b4d
+    if (subreq == NULL) {
905b4d
+        ret = ENOMEM;
905b4d
+        goto immediately;
905b4d
+    }
905b4d
+    tevent_req_set_callback(subreq, selinux_child_step, req);
905b4d
+
905b4d
+    ret = EOK;
905b4d
+immediately:
905b4d
+    if (ret != EOK) {
905b4d
+        tevent_req_error(req, ret);
905b4d
+        tevent_req_post(req, ev);
905b4d
+    }
905b4d
+    return req;
905b4d
+}
905b4d
+
905b4d
+static errno_t selinux_child_init(void)
905b4d
+{
905b4d
+    return child_debug_init(SELINUX_CHILD_LOG_FILE, &selinux_child_debug_fd);
905b4d
+}
905b4d
+
905b4d
+static errno_t selinux_child_create_buffer(struct selinux_child_state *state)
905b4d
+{
905b4d
+    size_t rp;
905b4d
+    size_t seuser_len;
905b4d
+    size_t mls_range_len;
905b4d
+    size_t username_len;
905b4d
+
905b4d
+    seuser_len = strlen(state->sci->seuser);
905b4d
+    mls_range_len = strlen(state->sci->mls_range);
905b4d
+    username_len = strlen(state->sci->username);
905b4d
+
905b4d
+    state->buf->size = 3 * sizeof(uint32_t);
905b4d
+    state->buf->size += seuser_len + mls_range_len + username_len;
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_ALL, "buffer size: %zu\n", state->buf->size);
905b4d
+
905b4d
+    state->buf->data = talloc_size(state->buf, state->buf->size);
905b4d
+    if (state->buf->data == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
905b4d
+        return ENOMEM;
905b4d
+    }
905b4d
+
905b4d
+    rp = 0;
905b4d
+
905b4d
+    /* seuser */
905b4d
+    SAFEALIGN_SET_UINT32(&state->buf->data[rp], seuser_len, &rp);
905b4d
+    safealign_memcpy(&state->buf->data[rp], state->sci->seuser,
905b4d
+                     seuser_len, &rp);
905b4d
+
905b4d
+    /* mls_range */
905b4d
+    SAFEALIGN_SET_UINT32(&state->buf->data[rp], mls_range_len, &rp);
905b4d
+    safealign_memcpy(&state->buf->data[rp], state->sci->mls_range,
905b4d
+                     mls_range_len, &rp);
905b4d
+
905b4d
+    /* username */
905b4d
+    SAFEALIGN_SET_UINT32(&state->buf->data[rp], username_len, &rp);
905b4d
+    safealign_memcpy(&state->buf->data[rp], state->sci->username,
905b4d
+                     username_len, &rp);
905b4d
+
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+static errno_t selinux_fork_child(struct selinux_child_state *state)
905b4d
+{
905b4d
+    int pipefd_to_child[2];
905b4d
+    int pipefd_from_child[2];
905b4d
+    pid_t pid;
905b4d
+    errno_t ret;
905b4d
+
905b4d
+    ret = pipe(pipefd_from_child);
905b4d
+    if (ret == -1) {
905b4d
+        ret = errno;
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "pipe failed [%d][%s].\n", errno, sss_strerror(errno));
905b4d
+        return ret;
905b4d
+    }
905b4d
+
905b4d
+    ret = pipe(pipefd_to_child);
905b4d
+    if (ret == -1) {
905b4d
+        ret = errno;
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "pipe failed [%d][%s].\n", errno, sss_strerror(errno));
905b4d
+        return ret;
905b4d
+    }
905b4d
+
905b4d
+    pid = fork();
905b4d
+
905b4d
+    if (pid == 0) { /* child */
905b4d
+        ret = exec_child(state,
905b4d
+                         pipefd_to_child, pipefd_from_child,
905b4d
+                         SELINUX_CHILD, selinux_child_debug_fd);
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec selinux_child: [%d][%s].\n",
905b4d
+              ret, sss_strerror(ret));
905b4d
+        return ret;
905b4d
+    } else if (pid > 0) { /* parent */
905b4d
+        state->io->read_from_child_fd = pipefd_from_child[0];
905b4d
+        close(pipefd_from_child[1]);
905b4d
+        state->io->write_to_child_fd = pipefd_to_child[1];
905b4d
+        close(pipefd_to_child[0]);
905b4d
+        fd_nonblocking(state->io->read_from_child_fd);
905b4d
+        fd_nonblocking(state->io->write_to_child_fd);
905b4d
+
905b4d
+        ret = child_handler_setup(state->ev, pid, NULL, NULL, NULL);
905b4d
+        if (ret != EOK) {
905b4d
+            DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+                  "Could not set up child signal handler\n");
905b4d
+            return ret;
905b4d
+        }
905b4d
+    } else { /* error */
905b4d
+        ret = errno;
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "fork failed [%d][%s].\n", errno, sss_strerror(errno));
905b4d
+        return ret;
905b4d
+    }
905b4d
+
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+static void selinux_child_step(struct tevent_req *subreq)
905b4d
+{
905b4d
+    struct tevent_req *req;
905b4d
+    errno_t ret;
905b4d
+    struct selinux_child_state *state;
905b4d
+
905b4d
+
905b4d
+    req = tevent_req_callback_data(subreq, struct tevent_req);
905b4d
+    state = tevent_req_data(req, struct selinux_child_state);
905b4d
+
905b4d
+    ret = write_pipe_recv(subreq);
905b4d
+    talloc_zfree(subreq);
905b4d
+    if (ret != EOK) {
905b4d
+        tevent_req_error(req, ret);
905b4d
+        return;
905b4d
+    }
905b4d
+
905b4d
+    close(state->io->write_to_child_fd);
905b4d
+    state->io->write_to_child_fd = -1;
905b4d
+
905b4d
+    subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd);
905b4d
+    if (subreq == NULL) {
905b4d
+        tevent_req_error(req, ENOMEM);
905b4d
+        return;
905b4d
+    }
905b4d
+    tevent_req_set_callback(subreq, selinux_child_done, req);
905b4d
+}
905b4d
+
905b4d
+static void selinux_child_done(struct tevent_req *subreq)
905b4d
+{
905b4d
+    struct tevent_req *req;
905b4d
+    struct selinux_child_state *state;
905b4d
+    uint32_t child_result;
905b4d
+    errno_t ret;
905b4d
+    ssize_t len;
905b4d
+    uint8_t *buf;
905b4d
+
905b4d
+    req = tevent_req_callback_data(subreq, struct tevent_req);
905b4d
+    state = tevent_req_data(req, struct selinux_child_state);
905b4d
+
905b4d
+    ret = read_pipe_recv(subreq, state, &buf, &len;;
905b4d
+    talloc_zfree(subreq);
905b4d
+    if (ret != EOK) {
905b4d
+        tevent_req_error(req, ret);
905b4d
+        return;
905b4d
+    }
905b4d
+
905b4d
+    close(state->io->read_from_child_fd);
905b4d
+    state->io->read_from_child_fd = -1;
905b4d
+
905b4d
+    ret = selinux_child_parse_response(buf, len, &child_result);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "selinux_child_parse_response failed: [%d][%s]\n",
905b4d
+              ret, strerror(ret));
905b4d
+        tevent_req_error(req, ret);
905b4d
+        return;
905b4d
+    } else if (child_result != 0){
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "Error in selinux_child: [%d][%s]\n",
905b4d
+              child_result, strerror(child_result));
905b4d
+        tevent_req_error(req, ERR_SELINUX_CONTEXT);
905b4d
+        return;
905b4d
+    }
905b4d
+
905b4d
+    tevent_req_done(req);
905b4d
+    return;
905b4d
+}
905b4d
+
905b4d
+static errno_t selinux_child_parse_response(uint8_t *buf,
905b4d
+                                            ssize_t len,
905b4d
+                                            uint32_t *_child_result)
905b4d
+{
905b4d
+    size_t p = 0;
905b4d
+    uint32_t child_result;
905b4d
+
905b4d
+    /* semanage retval */
905b4d
+    SAFEALIGN_COPY_UINT32_CHECK(&child_result, buf + p, len, &p);
905b4d
+
905b4d
+    *_child_result = child_result;
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+static errno_t selinux_child_recv(struct tevent_req *req)
905b4d
+{
905b4d
+    TEVENT_REQ_RETURN_ON_ERROR(req);
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
 /* A more generic request to gather all SELinux and HBAC rules. Updates
905b4d
  * cache if necessary
905b4d
  */
905b4d
diff --git a/src/providers/ipa/selinux_child.c b/src/providers/ipa/selinux_child.c
905b4d
new file mode 100644
905b4d
index 0000000000000000000000000000000000000000..a624cfd30d6795c305b7673d849213c74de95cd8
905b4d
--- /dev/null
905b4d
+++ b/src/providers/ipa/selinux_child.c
905b4d
@@ -0,0 +1,272 @@
905b4d
+/*
905b4d
+    SSSD
905b4d
+
905b4d
+    IPA back end -- set SELinux context in a child module
905b4d
+
905b4d
+    Authors:
905b4d
+        Jakub Hrozek <jhrozek@redhat.com>
905b4d
+
905b4d
+    Copyright (C) 2014 Red Hat
905b4d
+
905b4d
+    This program is free software; you can redistribute it and/or modify
905b4d
+    it under the terms of the GNU General Public License as published by
905b4d
+    the Free Software Foundation; either version 3 of the License, or
905b4d
+    (at your option) any later version.
905b4d
+
905b4d
+    This program is distributed in the hope that it will be useful,
905b4d
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
905b4d
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
905b4d
+    GNU General Public License for more details.
905b4d
+
905b4d
+    You should have received a copy of the GNU General Public License
905b4d
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
905b4d
+*/
905b4d
+
905b4d
+
905b4d
+#include <sys/types.h>
905b4d
+#include <unistd.h>
905b4d
+#include <sys/stat.h>
905b4d
+#include <popt.h>
905b4d
+
905b4d
+#include "util/util.h"
905b4d
+#include "util/child_common.h"
905b4d
+#include "providers/dp_backend.h"
905b4d
+
905b4d
+struct input_buffer {
905b4d
+    const char *seuser;
905b4d
+    const char *mls_range;
905b4d
+    const char *username;
905b4d
+};
905b4d
+
905b4d
+static errno_t unpack_buffer(uint8_t *buf,
905b4d
+                             size_t size,
905b4d
+                             struct input_buffer *ibuf)
905b4d
+{
905b4d
+    size_t p = 0;
905b4d
+    uint32_t len;
905b4d
+
905b4d
+    /* seuser */
905b4d
+    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
905b4d
+    DEBUG(SSSDBG_TRACE_INTERNAL, "seuser length: %d\n", len);
905b4d
+    if (len == 0) {
905b4d
+        return EINVAL;
905b4d
+    } else {
905b4d
+        if ((p + len ) > size) return EINVAL;
905b4d
+        ibuf->seuser = talloc_strndup(ibuf, (char *)(buf + p), len);
905b4d
+        if (ibuf->seuser == NULL) return ENOMEM;
905b4d
+        DEBUG(SSSDBG_TRACE_INTERNAL, "seuser: %s\n", ibuf->seuser);
905b4d
+        p += len;
905b4d
+    }
905b4d
+
905b4d
+    /* MLS range */
905b4d
+    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
905b4d
+    DEBUG(SSSDBG_TRACE_INTERNAL, "mls_range length: %d\n", len);
905b4d
+    if (len == 0) {
905b4d
+        return EINVAL;
905b4d
+    } else {
905b4d
+        if ((p + len ) > size) return EINVAL;
905b4d
+        ibuf->mls_range = talloc_strndup(ibuf, (char *)(buf + p), len);
905b4d
+        if (ibuf->mls_range == NULL) return ENOMEM;
905b4d
+        DEBUG(SSSDBG_TRACE_INTERNAL, "mls_range: %s\n", ibuf->mls_range);
905b4d
+        p += len;
905b4d
+    }
905b4d
+
905b4d
+    /* username */
905b4d
+    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
905b4d
+    DEBUG(SSSDBG_TRACE_INTERNAL, "username length: %d\n", len);
905b4d
+    if (len == 0) {
905b4d
+        return EINVAL;
905b4d
+    } else {
905b4d
+        if ((p + len ) > size) return EINVAL;
905b4d
+        ibuf->username = talloc_strndup(ibuf, (char *)(buf + p), len);
905b4d
+        if (ibuf->username == NULL) return ENOMEM;
905b4d
+        DEBUG(SSSDBG_TRACE_INTERNAL, "username: %s\n", ibuf->username);
905b4d
+        p += len;
905b4d
+    }
905b4d
+
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+static errno_t pack_buffer(struct response *r, int result)
905b4d
+{
905b4d
+    size_t p = 0;
905b4d
+
905b4d
+    /* A buffer with the following structure must be created:
905b4d
+     *   uint32_t status of the request (required)
905b4d
+     */
905b4d
+    r->size =  sizeof(uint32_t);
905b4d
+
905b4d
+    r->buf = talloc_array(r, uint8_t, r->size);
905b4d
+    if(r->buf == NULL) {
905b4d
+        return ENOMEM;
905b4d
+    }
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
905b4d
+
905b4d
+    /* result */
905b4d
+    SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
905b4d
+
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+static errno_t prepare_response(TALLOC_CTX *mem_ctx,
905b4d
+                                int result,
905b4d
+                                struct response **rsp)
905b4d
+{
905b4d
+    int ret;
905b4d
+    struct response *r = NULL;
905b4d
+
905b4d
+    r = talloc_zero(mem_ctx, struct response);
905b4d
+    if (r == NULL) {
905b4d
+        return ENOMEM;
905b4d
+    }
905b4d
+
905b4d
+    r->buf = NULL;
905b4d
+    r->size = 0;
905b4d
+
905b4d
+    ret = pack_buffer(r, result);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n");
905b4d
+        return ret;
905b4d
+    }
905b4d
+
905b4d
+    *rsp = r;
905b4d
+    DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size);
905b4d
+    return EOK;
905b4d
+}
905b4d
+
905b4d
+int main(int argc, const char *argv[])
905b4d
+{
905b4d
+    int opt;
905b4d
+    poptContext pc;
905b4d
+    int debug_fd = -1;
905b4d
+    errno_t ret;
905b4d
+    TALLOC_CTX *main_ctx = NULL;
905b4d
+    uint8_t *buf = NULL;
905b4d
+    ssize_t len = 0;
905b4d
+    struct input_buffer *ibuf = NULL;
905b4d
+    struct response *resp = NULL;
905b4d
+    size_t written;
905b4d
+
905b4d
+    struct poptOption long_options[] = {
905b4d
+        POPT_AUTOHELP
905b4d
+        {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
905b4d
+         _("Debug level"), NULL},
905b4d
+        {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
905b4d
+         _("Add debug timestamps"), NULL},
905b4d
+        {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0,
905b4d
+         _("Show timestamps with microseconds"), NULL},
905b4d
+        {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
905b4d
+         _("An open file descriptor for the debug logs"), NULL},
905b4d
+        {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
905b4d
+         &debug_to_stderr, 0,
905b4d
+         _("Send the debug output to stderr directly."), NULL },
905b4d
+        POPT_TABLEEND
905b4d
+    };
905b4d
+
905b4d
+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
905b4d
+    debug_level = SSSDBG_INVALID;
905b4d
+
905b4d
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
905b4d
+    while((opt = poptGetNextOpt(pc)) != -1) {
905b4d
+        switch(opt) {
905b4d
+        default:
905b4d
+        fprintf(stderr, "\nInvalid option %s: %s\n\n",
905b4d
+                  poptBadOption(pc, 0), poptStrerror(opt));
905b4d
+            poptPrintUsage(pc, stderr, 0);
905b4d
+            _exit(-1);
905b4d
+        }
905b4d
+    }
905b4d
+
905b4d
+    poptFreeContext(pc);
905b4d
+
905b4d
+    DEBUG_INIT(debug_level);
905b4d
+
905b4d
+    debug_prg_name = talloc_asprintf(NULL, "[sssd[selinux_child[%d]]]", getpid());
905b4d
+    if (debug_prg_name == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    if (debug_fd != -1) {
905b4d
+        ret = set_debug_file_from_fd(debug_fd);
905b4d
+        if (ret != EOK) {
905b4d
+            DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
905b4d
+        }
905b4d
+    }
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_FUNC, "selinux_child started.\n");
905b4d
+    DEBUG(SSSDBG_TRACE_INTERNAL,
905b4d
+          "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
905b4d
+
905b4d
+    main_ctx = talloc_new(NULL);
905b4d
+    if (main_ctx == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
905b4d
+        talloc_free(discard_const(debug_prg_name));
905b4d
+        goto fail;
905b4d
+    }
905b4d
+    talloc_steal(main_ctx, debug_prg_name);
905b4d
+
905b4d
+    buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE);
905b4d
+    if (buf == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    ibuf = talloc_zero(main_ctx, struct input_buffer);
905b4d
+    if (ibuf == NULL) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_FUNC, "context initialized\n");
905b4d
+
905b4d
+    errno = 0;
905b4d
+    len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE);
905b4d
+    if (len == -1) {
905b4d
+        ret = errno;
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret));
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    close(STDIN_FILENO);
905b4d
+
905b4d
+    ret = unpack_buffer(buf, len, ibuf);
905b4d
+    if (ret != EOK) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE,
905b4d
+              "unpack_buffer failed.[%d][%s].\n", ret, strerror(ret));
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_FUNC, "performing selinux operations\n");
905b4d
+
905b4d
+    ret = set_seuser(ibuf->username, ibuf->seuser, ibuf->mls_range);
905b4d
+
905b4d
+    ret = prepare_response(main_ctx, ret, &resp);
905b4d
+
905b4d
+    errno = 0;
905b4d
+
905b4d
+    written = sss_atomic_write_s(STDOUT_FILENO, resp->buf, resp->size);
905b4d
+    if (written == -1) {
905b4d
+        ret = errno;
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret,
905b4d
+                    strerror(ret));
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    if (written != resp->size) {
905b4d
+        DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n",
905b4d
+              resp->size, written);
905b4d
+        goto fail;
905b4d
+    }
905b4d
+
905b4d
+    DEBUG(SSSDBG_TRACE_FUNC, "selinux_child completed successfully\n");
905b4d
+    close(STDOUT_FILENO);
905b4d
+    talloc_free(main_ctx);
905b4d
+    return EXIT_SUCCESS;
905b4d
+fail:
905b4d
+    DEBUG(SSSDBG_CRIT_FAILURE, "selinux_child failed!\n");
905b4d
+    close(STDOUT_FILENO);
905b4d
+    talloc_free(main_ctx);
905b4d
+    return EXIT_FAILURE;
905b4d
+}
905b4d
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
905b4d
index 5b36780ffcdc6733241cdb942865ecdf38da3bca..d5da64622eebe7f779816c7f2090da5b9a9b13f0 100644
905b4d
--- a/src/util/util_errors.c
905b4d
+++ b/src/util/util_errors.c
905b4d
@@ -62,6 +62,7 @@ struct err_string error_to_str[] = {
905b4d
     { "Bus method not supported" }, /* ERR_SBUS_NOSUP */
905b4d
     { "Cannot connect to system bus" }, /* ERR_NO_SYSBUS */
905b4d
     { "LDAP search returned a referral" }, /* ERR_REFERRAL */
905b4d
+    { "Error setting SELinux user context" }, /* ERR_SELINUX_CONTEXT */
905b4d
 };
905b4d
 
905b4d
 
905b4d
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
905b4d
index e040ba903b27d06ec75cea31485d2f3111ca5302..2bc576605e613d674d38b54aae1604c0b044635f 100644
905b4d
--- a/src/util/util_errors.h
905b4d
+++ b/src/util/util_errors.h
905b4d
@@ -84,6 +84,7 @@ enum sssd_errors {
905b4d
     ERR_SBUS_NOSUP,
905b4d
     ERR_NO_SYSBUS,
905b4d
     ERR_REFERRAL,
905b4d
+    ERR_SELINUX_CONTEXT,
905b4d
     ERR_LAST            /* ALWAYS LAST */
905b4d
 };
905b4d
 
905b4d
-- 
905b4d
1.9.3
905b4d