summaryrefslogtreecommitdiffstats
path: root/src/providers
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-10-20 23:16:40 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-11-05 19:55:09 +0100
commitf3a25949de81f80c136bb073e4a8f504b080c20c (patch)
tree69523a939b65b371d7a95e16d1f69e237c77f048 /src/providers
parent77b13371c87702aee3f858f6b2b73826cf5a01bd (diff)
downloadsssd-f3a25949de81f80c136bb073e4a8f504b080c20c.tar.gz
sssd-f3a25949de81f80c136bb073e4a8f504b080c20c.tar.xz
sssd-f3a25949de81f80c136bb073e4a8f504b080c20c.zip
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. Reviewed-by: Michal Židek <mzidek@redhat.com>
Diffstat (limited to 'src/providers')
-rw-r--r--src/providers/ipa/ipa_selinux.c409
-rw-r--r--src/providers/ipa/selinux_child.c272
2 files changed, 661 insertions, 20 deletions
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index 7db1c6ed2..b392d82a6 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,21 +393,25 @@ 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;
}
- /* 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);
+ /* 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;
}
-
- pd->pam_status = PAM_SUCCESS;
- be_req_terminate(breq, DP_ERR_OK, EOK, "Success");
+ tevent_req_set_callback(child_req, ipa_selinux_child_done, op_ctx);
return;
fail:
@@ -391,6 +428,35 @@ fail:
}
}
+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);
+ }
+
+ pd->pam_status = PAM_SUCCESS;
+ be_req_terminate(breq, DP_ERR_OK, EOK, "Success");
+}
+
static errno_t
ipa_selinux_process_seealso_maps(struct sysdb_attrs *user,
struct sysdb_attrs *host,
@@ -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 000000000..a624cfd30
--- /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@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;
+}