[SSSD] [PATCH] Move settig the SELinux context into a privileged child process

Jakub Hrozek jhrozek at redhat.com
Wed Nov 5 17:30:55 UTC 2014


On Fri, Oct 31, 2014 at 10:48:09PM +0100, Michal Židek wrote:
> On 10/31/2014 05:59 PM, Jakub Hrozek wrote:
> >On Fri, Oct 24, 2014 at 10:57:02PM +0200, Jakub Hrozek wrote:
> >>>Hi,
> >>>
> >>>this patchset moves setting the SELinux context from the sssd_be process
> >>>to a privileges child process in preparation for making the sssd_be process
> >>>running as the sssd user.
> >>>
> >>>The first two patches just reduce code duplication between the child
> >>>processes we already have, the last one implements setting the context.
> >>>
> >>>Please note that the Fedora and RHEL SELinux policy must be tweaked, so
> >>>currently the patches must be tested with SELinux set to Permissive.
> >>>
> >>>Additionally, I wasn't able to run Coverity scan on these patches, because
> >>>our Coverity server was returning errors again for me. I'll run the tests
> >>>later, or, if the reviewer would find any issues, I'll be happy to fix them.
> >Hi,
> >
> >attached patches change the packaging a bit -- make distcheck didn't
> >pass previously and I forgot to package selinux_child in the RPM.
> >
> >
> >0001-UTIL-Remove-code-duplication-of-struct-io.patch
> 
> Ack.
> 
> >
> >0002-UTIL-Remove-more-code-duplication-setting-up-child-p.patch
> 
> Ack.
> 
> >
> >0003-IPA-Move-setting-the-SELinux-context-to-a-child-proc.patch
> 
> The chgrp and chmod in Makefile.am should probably be called
> only if the semanage is available:
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -2852,10 +2852,12 @@ endif
> 
>  if SSSD_USER
> 	chgrp $(SSSD_USER) $(sssdlibexecdir)/ldap_child
> -       chgrp $(SSSD_USER) $(sssdlibexecdir)/selinux_child
>         chmod 4750 $(sssdlibexecdir)/ldap_child
> +if BUILD_SEMANAGE
> +       chgrp $(SSSD_USER) $(sssdlibexecdir)/selinux_child
>         chmod 4750 $(sssdlibexecdir)/selinux_child
>  endif
> +endif

Fixed 

> 
> ---
> In src/providers/ipa/ipa_selinux.c, the line 405 is too long:
>  405     /* Update the SELinux context ...
>  406      * unprivileged
>  407      */
> 
> ---

Fixed

> Also in src/providers/ipa/ipa_selinux.c, the indentation
> on line 872 should be aligned with the line above:
>  871     if (sci->seuser == NULL || sci->mls_range == NULL
>  872             || sci->username == NULL) {
> 
> Should be:
>  871     if (sci->seuser == NULL || sci->mls_range == NULL
>  872         || sci->username == NULL) {
> (Or at least I am doing it like this, and it is style that
> was proposed IIRC by Stephen in some old discussion).

Fixed. I thought the leading tabs were also proposed in that thread..

> 
> ---
> 
> Otherwise the patch looks good to me and the selinux
> context is retrieved properly.
> 
> Michal

Thank you for the review, new patches are attached.
-------------- next part --------------
>From 97bcc4166307c374c7f3f110f8c0bee37e1c3320 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 21 Oct 2014 12:29:55 +0200
Subject: [PATCH 1/3] UTIL: Remove code duplication of struct io

We had struct io and the associated destructor copied twice in the code
already and need it again in the SELinux provider. Instead of adding
another copy, move the code to a shared subtree under util/
---
 src/providers/ad/ad_gpo.c               | 43 +++------------------------------
 src/providers/krb5/krb5_child_handler.c | 38 ++---------------------------
 src/util/child_common.c                 | 29 ++++++++++++++++++++++
 src/util/child_common.h                 |  7 ++++++
 4 files changed, 41 insertions(+), 76 deletions(-)

diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 4dfbd4b6943b477bd93fdd730dfa5b1c5828a10a..80b0d45c2861a64f01fe7835cccee4348084c7da 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -3756,46 +3756,9 @@ struct ad_gpo_process_cse_state {
     pid_t child_pid;
     uint8_t *buf;
     ssize_t len;
-    struct io *io;
+    struct child_io_fds *io;
 };
 
-struct io {
-    int read_from_child_fd;
-    int write_to_child_fd;
-};
-
-static errno_t
-gpo_child_io_destructor(void *ptr)
-{
-    int ret;
-    struct io *io;
-
-    io = talloc_get_type(ptr, struct io);
-    if (io == NULL) return EOK;
-
-    if (io->write_to_child_fd != -1) {
-        ret = close(io->write_to_child_fd);
-        io->write_to_child_fd = -1;
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "close failed [%d][%s].\n", ret, strerror(ret));
-        }
-    }
-
-    if (io->read_from_child_fd != -1) {
-        ret = close(io->read_from_child_fd);
-        io->read_from_child_fd = -1;
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "close failed [%d][%s].\n", ret, strerror(ret));
-        }
-    }
-
-    return EOK;
-}
-
 static errno_t gpo_fork_child(struct tevent_req *req);
 static void gpo_cse_step(struct tevent_req *subreq);
 static void gpo_cse_done(struct tevent_req *subreq);
@@ -3849,7 +3812,7 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
     state->gpo_guid = gpo_guid;
     state->smb_path = smb_path;
     state->smb_cse_suffix = smb_cse_suffix;
-    state->io = talloc(state, struct io);
+    state->io = talloc(state, struct child_io_fds);
     if (state->io == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
         ret = ENOMEM;
@@ -3858,7 +3821,7 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
 
     state->io->write_to_child_fd = -1;
     state->io->read_from_child_fd = -1;
-    talloc_set_destructor((void *) state->io, gpo_child_io_destructor);
+    talloc_set_destructor((void *) state->io, child_io_destructor);
 
     /* prepare the data to pass to child */
     ret = create_cse_send_buffer(state, smb_server, smb_share, smb_path,
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 114e72a33e48da299647c2fe3096b9bf61cf294a..4ba939deb3e0e282b3ca9b2b21226ea7e64e311b 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -41,11 +41,6 @@
 #define TIME_T_MAX LONG_MAX
 #define int64_to_time_t(val) ((time_t)((val) < TIME_T_MAX ? val : TIME_T_MAX))
 
-struct io {
-    int read_from_child_fd;
-    int write_to_child_fd;
-};
-
 struct handle_child_state {
     struct tevent_context *ev;
     struct krb5child_req *kr;
@@ -55,38 +50,9 @@ struct handle_child_state {
     struct tevent_timer *timeout_handler;
     pid_t child_pid;
 
-    struct io *io;
+    struct child_io_fds *io;
 };
 
-static int child_io_destructor(void *ptr)
-{
-    int ret;
-    struct io *io = talloc_get_type(ptr, struct io);
-    if (io == NULL) return EOK;
-
-    if (io->write_to_child_fd != -1) {
-        ret = close(io->write_to_child_fd);
-        io->write_to_child_fd = -1;
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "close failed [%d][%s].\n", ret, strerror(ret));
-        }
-    }
-
-    if (io->read_from_child_fd != -1) {
-        ret = close(io->read_from_child_fd);
-        io->read_from_child_fd = -1;
-        if (ret != EOK) {
-            ret = errno;
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "close failed [%d][%s].\n", ret, strerror(ret));
-        }
-    }
-
-    return EOK;
-}
-
 static errno_t pack_authtok(struct io_buffer *buf, size_t *rp,
                             struct sss_auth_token *tok)
 {
@@ -391,7 +357,7 @@ struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx,
     state->child_pid = -1;
     state->timeout_handler = NULL;
 
-    state->io = talloc(state, struct io);
+    state->io = talloc(state, struct child_io_fds);
     if (state->io == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
         ret = ENOMEM;
diff --git a/src/util/child_common.c b/src/util/child_common.c
index 81bbab70ed44a6c0a4410909924aec195c643bea..e4a885b6e6e4a1a8a0cabd12ba1544a7c8f0f160 100644
--- a/src/util/child_common.c
+++ b/src/util/child_common.c
@@ -772,3 +772,32 @@ void child_cleanup(int readfd, int writefd)
         }
     }
 }
+
+int child_io_destructor(void *ptr)
+{
+    int ret;
+    struct child_io_fds *io = talloc_get_type(ptr, struct child_io_fds);
+    if (io == NULL) return EOK;
+
+    if (io->write_to_child_fd != -1) {
+        ret = close(io->write_to_child_fd);
+        io->write_to_child_fd = -1;
+        if (ret != EOK) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "close failed [%d][%s].\n", ret, strerror(ret));
+        }
+    }
+
+    if (io->read_from_child_fd != -1) {
+        ret = close(io->read_from_child_fd);
+        io->read_from_child_fd = -1;
+        if (ret != EOK) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "close failed [%d][%s].\n", ret, strerror(ret));
+        }
+    }
+
+    return EOK;
+}
diff --git a/src/util/child_common.h b/src/util/child_common.h
index 95865bb529b6b282a57b138d7a85ce93649faa2e..261da7f9c546ddfdb38506e5285024ad201bdd4d 100644
--- a/src/util/child_common.h
+++ b/src/util/child_common.h
@@ -45,6 +45,11 @@ struct io_buffer {
     size_t size;
 };
 
+struct child_io_fds {
+    int read_from_child_fd;
+    int write_to_child_fd;
+};
+
 /* COMMON SIGCHLD HANDLING */
 typedef void (*sss_child_fn_t)(int pid, int wait_status, void *pvt);
 
@@ -113,4 +118,6 @@ errno_t exec_child(TALLOC_CTX *mem_ctx,
 
 void child_cleanup(int readfd, int writefd);
 
+int child_io_destructor(void *ptr);
+
 #endif /* __CHILD_COMMON_H__ */
-- 
1.9.3

-------------- next part --------------
>From 6636ae0a359634192a8afbd9c676f7c24af3c775 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 21 Oct 2014 22:36:59 +0200
Subject: [PATCH 2/3] UTIL: Remove more code duplication setting up child
 processes

All our child processes duplicated the same code that set up the
debugging all around. Instead of adding yet another copy for the
selinux_child, add a common utility function.
---
 src/providers/ad/ad_gpo.c               | 23 ++---------------------
 src/providers/krb5/krb5_init_shared.c   | 24 ++++++++----------------
 src/providers/ldap/sdap_child_helpers.c | 22 +---------------------
 src/util/child_common.c                 | 29 +++++++++++++++++++++++++++++
 src/util/child_common.h                 |  2 ++
 5 files changed, 42 insertions(+), 58 deletions(-)

diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 80b0d45c2861a64f01fe7835cccee4348084c7da..83edbe4fb5a7e617cab95c0330ceae31392b18b2 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -1313,29 +1313,10 @@ ad_gpo_access_check(TALLOC_CTX *mem_ctx,
 }
 
 #define GPO_CHILD_LOG_FILE "gpo_child"
+
 static errno_t gpo_child_init(void)
 {
-    int ret;
-    FILE *debug_filep;
-
-    if (debug_to_file != 0 && gpo_child_debug_fd == -1) {
-        ret = open_debug_file_ex(GPO_CHILD_LOG_FILE, &debug_filep, false);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
-                        ret, strerror(ret));
-            return ret;
-        }
-
-        gpo_child_debug_fd = fileno(debug_filep);
-        if (gpo_child_debug_fd == -1) {
-            DEBUG(SSSDBG_FATAL_FAILURE,
-                  "fileno failed [%d][%s]\n", errno, strerror(errno));
-            ret = errno;
-            return ret;
-        }
-    }
-
-    return EOK;
+    return child_debug_init(GPO_CHILD_LOG_FILE, &gpo_child_debug_fd);
 }
 
 /*
diff --git a/src/providers/krb5/krb5_init_shared.c b/src/providers/krb5/krb5_init_shared.c
index 340eab1f0d432d411e2b807f66b03bffcbd14d1d..3b4bf096ef49d0f2d369fda20462b7fd56d61127 100644
--- a/src/providers/krb5/krb5_init_shared.c
+++ b/src/providers/krb5/krb5_init_shared.c
@@ -30,7 +30,6 @@ errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx,
                         struct be_ctx *bectx)
 {
     errno_t ret;
-    FILE *debug_filep;
     time_t renew_intv = 0;
     krb5_deltat renew_interval_delta;
     char *renew_interval_str;
@@ -83,23 +82,16 @@ errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx,
         goto done;
     }
 
-    if (debug_to_file != 0) {
-        ret = open_debug_file_ex(KRB5_CHILD_LOG_FILE, &debug_filep, false);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
-                    ret, strerror(ret));
-            goto done;
-        }
-
-        krb5_auth_ctx->child_debug_fd = fileno(debug_filep);
-        if (krb5_auth_ctx->child_debug_fd == -1) {
-            DEBUG(SSSDBG_FATAL_FAILURE,
-                  "fileno failed [%d][%s]\n", errno, strerror(errno));
-            ret = errno;
-            goto done;
-        }
+    krb5_auth_ctx->child_debug_fd = -1; /* -1 means not initialized */
+    ret = child_debug_init(KRB5_CHILD_LOG_FILE,
+                           &krb5_auth_ctx->child_debug_fd);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Could not set krb5_child debugging!\n");
+        goto done;
     }
 
+    ret = EOK;
+
 done:
     return ret;
 }
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
index e5d46b9b756cd50fadb212da72ad1cc9bdd93330..40010989021eb7cf77b96876b2d1c4119ed39163 100644
--- a/src/providers/ldap/sdap_child_helpers.c
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -466,25 +466,5 @@ static errno_t set_tgt_child_timeout(struct tevent_req *req,
 /* Setup child logging */
 int sdap_setup_child(void)
 {
-    int ret;
-    FILE *debug_filep;
-
-    if (debug_to_file != 0 && ldap_child_debug_fd == -1) {
-        ret = open_debug_file_ex(LDAP_CHILD_LOG_FILE, &debug_filep, false);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
-                        ret, strerror(ret));
-            return ret;
-        }
-
-        ldap_child_debug_fd = fileno(debug_filep);
-        if (ldap_child_debug_fd == -1) {
-            DEBUG(SSSDBG_FATAL_FAILURE,
-                  "fileno failed [%d][%s]\n", errno, strerror(errno));
-            ret = errno;
-            return ret;
-        }
-    }
-
-    return EOK;
+    return child_debug_init(LDAP_CHILD_LOG_FILE, &ldap_child_debug_fd);
 }
diff --git a/src/util/child_common.c b/src/util/child_common.c
index e4a885b6e6e4a1a8a0cabd12ba1544a7c8f0f160..cc6a8fa758bfa6efa511c28cd9121e759b590342 100644
--- a/src/util/child_common.c
+++ b/src/util/child_common.c
@@ -801,3 +801,32 @@ int child_io_destructor(void *ptr)
 
     return EOK;
 }
+
+errno_t child_debug_init(const char *logfile, int *debug_fd)
+{
+    int ret;
+    FILE *debug_filep;
+
+    if (debug_fd == NULL) {
+        return EOK;
+    }
+
+    if (debug_to_file != 0 && *debug_fd == -1) {
+        ret = open_debug_file_ex(logfile, &debug_filep, false);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
+                        ret, sss_strerror(ret));
+            return ret;
+        }
+
+        *debug_fd = fileno(debug_filep);
+        if (*debug_fd == -1) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "fileno failed [%d][%s]\n", errno, strerror(errno));
+            ret = errno;
+            return ret;
+        }
+    }
+
+    return EOK;
+}
diff --git a/src/util/child_common.h b/src/util/child_common.h
index 261da7f9c546ddfdb38506e5285024ad201bdd4d..e159719a2fca70a4ea044d79530a0d21cb3577e8 100644
--- a/src/util/child_common.h
+++ b/src/util/child_common.h
@@ -120,4 +120,6 @@ void child_cleanup(int readfd, int writefd);
 
 int child_io_destructor(void *ptr);
 
+errno_t child_debug_init(const char *logfile, int *debug_fd);
+
 #endif /* __CHILD_COMMON_H__ */
-- 
1.9.3

-------------- next part --------------
>From 0c4dd8767998d0fbf09be3a0e6d5ec96a50443aa Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 20 Oct 2014 23:16:40 +0200
Subject: [PATCH 3/3] IPA: Move setting the SELinux context to a child process

In order for the sssd_be process to run as unprivileged user, we need to
move the semanage processing to a process that runs as the root user
using setuid privileges.
---
 Makefile.am                       |  27 +++
 contrib/sssd.spec.in              |   1 +
 src/providers/ipa/ipa_selinux.c   | 425 +++++++++++++++++++++++++++++++++++---
 src/providers/ipa/selinux_child.c | 272 ++++++++++++++++++++++++
 src/util/util_errors.c            |   1 +
 src/util/util_errors.h            |   1 +
 6 files changed, 699 insertions(+), 28 deletions(-)
 create mode 100644 src/providers/ipa/selinux_child.c

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



More information about the sssd-devel mailing list