summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/sdap_child_helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ldap/sdap_child_helpers.c')
-rw-r--r--src/providers/ldap/sdap_child_helpers.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
new file mode 100644
index 000000000..0a95c8a0d
--- /dev/null
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -0,0 +1,462 @@
+/*
+ SSSD
+
+ LDAP Backend Module -- child helpers
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2009 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 <sys/wait.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/child_common.h"
+
+#ifndef SSSD_LIBEXEC_PATH
+#error "SSSD_LIBEXEC_PATH not defined"
+#else
+#define LDAP_CHILD SSSD_LIBEXEC_PATH"/ldap_child"
+#endif
+
+#ifndef LDAP_CHILD_USER
+#define LDAP_CHILD_USER "nobody"
+#endif
+
+struct sdap_child {
+ /* child info */
+ pid_t pid;
+ int read_from_child_fd;
+ int write_to_child_fd;
+};
+
+static void sdap_close_fd(int *fd)
+{
+ int ret;
+
+ if (*fd == -1) {
+ DEBUG(6, ("fd already closed\n"));
+ return;
+ }
+
+ ret = close(*fd);
+ if (ret) {
+ ret = errno;
+ DEBUG(2, ("Closing fd %d, return error %d (%s)\n",
+ *fd, ret, strerror(ret)));
+ }
+
+ *fd = -1;
+}
+
+static int sdap_child_destructor(void *ptr)
+{
+ struct sdap_child *child = talloc_get_type(ptr, struct sdap_child);
+
+ child_cleanup(child->read_from_child_fd, child->write_to_child_fd);
+
+ return 0;
+}
+
+static errno_t sdap_fork_child(struct sdap_child *child)
+{
+ int pipefd_to_child[2];
+ int pipefd_from_child[2];
+ pid_t pid;
+ int ret;
+ errno_t err;
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+
+ pid = fork();
+
+ if (pid == 0) { /* child */
+ err = exec_child(child,
+ pipefd_to_child, pipefd_from_child,
+ LDAP_CHILD, ldap_child_debug_fd);
+ if (err != EOK) {
+ DEBUG(1, ("Could not exec LDAP child: [%d][%s].\n",
+ err, strerror(err)));
+ return err;
+ }
+ } else if (pid > 0) { /* parent */
+ child->pid = pid;
+ child->read_from_child_fd = pipefd_from_child[0];
+ close(pipefd_from_child[1]);
+ child->write_to_child_fd = pipefd_to_child[1];
+ close(pipefd_to_child[0]);
+ fd_nonblocking(child->read_from_child_fd);
+ fd_nonblocking(child->write_to_child_fd);
+
+ } else { /* error */
+ err = errno;
+ DEBUG(1, ("fork failed [%d][%s].\n", err, strerror(err)));
+ return err;
+ }
+
+ return EOK;
+}
+
+static errno_t create_tgt_req_send_buffer(TALLOC_CTX *mem_ctx,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ struct io_buffer **io_buf)
+{
+ struct io_buffer *buf;
+ size_t rp;
+
+ buf = talloc(mem_ctx, struct io_buffer);
+ if (buf == NULL) {
+ DEBUG(1, ("talloc failed.\n"));
+ return ENOMEM;
+ }
+
+ buf->size = 3 * sizeof(uint32_t);
+ if (realm_str) {
+ buf->size += strlen(realm_str);
+ }
+ if (princ_str) {
+ buf->size += strlen(princ_str);
+ }
+ if (keytab_name) {
+ buf->size += strlen(keytab_name);
+ }
+
+ DEBUG(7, ("buffer size: %d\n", buf->size));
+
+ buf->data = talloc_size(buf, buf->size);
+ if (buf->data == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ talloc_free(buf);
+ return ENOMEM;
+ }
+
+ rp = 0;
+
+ /* realm */
+ if (realm_str) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(realm_str), rp);
+ COPY_MEM(&buf->data[rp], realm_str, rp, strlen(realm_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ /* principal */
+ if (princ_str) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(princ_str), rp);
+ COPY_MEM(&buf->data[rp], princ_str, rp, strlen(princ_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ /* keytab */
+ if (keytab_name) {
+ COPY_UINT32_VALUE(&buf->data[rp], strlen(keytab_name), rp);
+ COPY_MEM(&buf->data[rp], keytab_name, rp, strlen(realm_str));
+ } else {
+ COPY_UINT32_VALUE(&buf->data[rp], 0, rp);
+ }
+
+ *io_buf = buf;
+ return EOK;
+}
+
+static int parse_child_response(TALLOC_CTX *mem_ctx,
+ uint8_t *buf, ssize_t size,
+ int *result, char **ccache)
+{
+ size_t p = 0;
+ uint32_t len;
+ uint32_t res;
+ char *ccn;
+
+ /* operation result code */
+ COPY_UINT32_CHECK(&res, buf + p, p, size);
+
+ /* ccache name size */
+ COPY_UINT32_CHECK(&len, buf + p, p, size);
+
+ if ((p + len ) > size) return EINVAL;
+
+ ccn = talloc_size(mem_ctx, sizeof(char) * (len + 1));
+ if (ccn == NULL) {
+ DEBUG(1, ("talloc_size failed.\n"));
+ return ENOMEM;
+ }
+ memcpy(ccn, buf+p, sizeof(char) * (len + 1));
+ ccn[len] = '\0';
+
+ *result = res;
+ *ccache = ccn;
+ return EOK;
+}
+
+/* ==The-public-async-interface============================================*/
+
+struct sdap_get_tgt_state {
+ struct tevent_context *ev;
+ struct sdap_child *child;
+ ssize_t len;
+ uint8_t *buf;
+};
+
+static errno_t set_tgt_child_timeout(struct tevent_req *req,
+ struct tevent_context *ev,
+ int timeout);
+static void sdap_get_tgt_step(struct tevent_req *subreq);
+static void sdap_get_tgt_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ int timeout)
+{
+ struct tevent_req *req, *subreq;
+ struct sdap_get_tgt_state *state;
+ struct io_buffer *buf;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_get_tgt_state);
+ if (!req) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ state->child = talloc_zero(state, struct sdap_child);
+ if (!state->child) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ state->child->read_from_child_fd = -1;
+ state->child->write_to_child_fd = -1;
+ talloc_set_destructor((TALLOC_CTX *)state->child, sdap_child_destructor);
+
+ /* prepare the data to pass to child */
+ ret = create_tgt_req_send_buffer(state,
+ realm_str, princ_str, keytab_name,
+ &buf);
+ if (ret != EOK) {
+ DEBUG(1, ("create_tgt_req_send_buffer failed.\n"));
+ goto fail;
+ }
+
+ ret = sdap_fork_child(state->child);
+ if (ret != EOK) {
+ DEBUG(1, ("sdap_fork_child failed.\n"));
+ goto fail;
+ }
+
+ ret = set_tgt_child_timeout(req, ev, timeout);
+ if (ret != EOK) {
+ DEBUG(1, ("activate_child_timeout_handler failed.\n"));
+ goto fail;
+ }
+
+ subreq = write_pipe_send(state, ev, buf->data, buf->size,
+ state->child->write_to_child_fd);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_get_tgt_step, req);
+
+ return req;
+
+fail:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void sdap_get_tgt_step(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ ret = write_pipe_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_close_fd(&state->child->write_to_child_fd);
+
+ subreq = read_pipe_send(state, state->ev,
+ state->child->read_from_child_fd);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_tgt_done, req);
+}
+
+static void sdap_get_tgt_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ ret = read_pipe_recv(subreq, state, &state->buf, &state->len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ sdap_close_fd(&state->child->read_from_child_fd);
+
+ tevent_req_done(req);
+}
+
+int sdap_get_tgt_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ int *result,
+ char **ccname)
+{
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ char *ccn;
+ int res;
+ int ret;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = parse_child_response(mem_ctx, state->buf, state->len, &res, &ccn);
+ if (ret != EOK) {
+ DEBUG(1, ("Cannot parse child response: [%d][%s]\n", ret, strerror(ret)));
+ return ret;
+ }
+
+ DEBUG(6, ("Child responded: %d [%s]\n", res, ccn));
+ *result = res;
+ *ccname = ccn;
+ return EOK;
+}
+
+
+
+static void get_tgt_timeout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct sdap_get_tgt_state *state = tevent_req_data(req,
+ struct sdap_get_tgt_state);
+ int ret;
+
+ DEBUG(9, ("timeout for tgt child [%d] reached.\n", state->child->pid));
+
+ ret = kill(state->child->pid, SIGKILL);
+ if (ret == -1) {
+ DEBUG(1, ("kill failed [%d][%s].\n", errno, strerror(errno)));
+ }
+
+ tevent_req_error(req, ETIMEDOUT);
+}
+
+static errno_t set_tgt_child_timeout(struct tevent_req *req,
+ struct tevent_context *ev,
+ int timeout)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ DEBUG(6, ("Setting %d seconds timeout for tgt child\n", timeout));
+
+ tv = tevent_timeval_current_ofs(timeout, 0);
+
+ te = tevent_add_timer(ev, req, tv, get_tgt_timeout_handler, req);
+ if (te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+
+
+/* Setup child logging */
+int setup_child(struct sdap_id_ctx *ctx)
+{
+ int ret;
+ const char *mech;
+ struct tevent_signal *sige;
+ unsigned v;
+ FILE *debug_filep;
+
+ mech = dp_opt_get_string(ctx->opts->basic,
+ SDAP_SASL_MECH);
+ if (!mech) {
+ return EOK;
+ }
+
+ sige = tevent_add_signal(ctx->be->ev, ctx, SIGCHLD, SA_SIGINFO,
+ child_sig_handler, NULL);
+ if (sige == NULL) {
+ DEBUG(1, ("tevent_add_signal failed.\n"));
+ return ENOMEM;
+ }
+
+ if (debug_to_file != 0 && ldap_child_debug_fd == -1) {
+ ret = open_debug_file_ex("ldap_child", &debug_filep);
+ if (ret != EOK) {
+ DEBUG(0, ("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(0, ("fileno failed [%d][%s]\n", errno, strerror(errno)));
+ ret = errno;
+ return ret;
+ }
+
+ v = fcntl(ldap_child_debug_fd, F_GETFD, 0);
+ fcntl(ldap_child_debug_fd, F_SETFD, v & ~FD_CLOEXEC);
+ }
+
+ return EOK;
+}