From e08eb3e3b0586b331a688a4eded2d7f13611016c Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 20 Oct 2014 23:16:40 +0200 Subject: [PATCH 71/71] IPA: Move setting the SELinux context to a child process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- 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 ea296c40f89c552d5825cbce8e95afdc517e8bb5..b85341f5845c3cffab8a2c95b1be1d32517316e8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -146,6 +146,9 @@ endif if BUILD_SAMBA sssdlibexec_PROGRAMS += gpo_child endif +if BUILD_SEMANAGE +sssdlibexec_PROGRAMS += selinux_child +endif if BUILD_PAC_RESPONDER @@ -2531,6 +2534,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 \ @@ -2849,6 +2872,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 d2e6cec2610e4c00cb376683cf7e64eb5cdafc5c..5bfb16707c22dc65376581c88b8eb898949e726f 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 #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 +/* 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 + + 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 . +*/ + + +#include +#include +#include +#include + +#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