From b87233035e26cee919dcf46adaec29ba7fdaa51e Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Fri, 29 Oct 2010 14:48:20 +0200 Subject: Make handle_child_* request public I took the opportunity to move everything related to the handling of the krb5_child into a separate file and cleaned the interfaces and related structures a bit. --- src/providers/krb5/krb5_auth.c | 331 ++--------------------------------------- 1 file changed, 12 insertions(+), 319 deletions(-) (limited to 'src/providers/krb5/krb5_auth.c') diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 80d8734a7..9dc7a2c9e 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -39,12 +39,6 @@ #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" -#ifndef SSSD_LIBEXEC_PATH -#error "SSSD_LIBEXEC_PATH not defined" -#else -#define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child" -#endif - static errno_t safe_remove_old_ccache_file(const char *old_ccache_file, const char *new_ccache_file) { @@ -205,72 +199,6 @@ done: return ret; } -errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) -{ - struct io_buffer *buf; - size_t rp; - const char *keytab; - uint32_t validate; - - keytab = dp_opt_get_cstring(kr->krb5_ctx->opts, KRB5_KEYTAB); - if (keytab == NULL) { - DEBUG(1, ("Missing keytab option.\n")); - return EINVAL; - } - - validate = dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) ? 1 : 0; - - buf = talloc(kr, struct io_buffer); - if (buf == NULL) { - DEBUG(1, ("talloc failed.\n")); - return ENOMEM; - } - - buf->size = 9*sizeof(uint32_t) + strlen(kr->upn) + strlen(kr->ccname) + - strlen(keytab) + - kr->pd->authtok_size; - if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { - buf->size += sizeof(uint32_t) + kr->pd->newauthtok_size; - } - - buf->data = talloc_size(kr, buf->size); - if (buf->data == NULL) { - DEBUG(1, ("talloc_size failed.\n")); - talloc_free(buf); - return ENOMEM; - } - - rp = 0; - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->cmd, &rp); - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->uid, &rp); - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->gid, &rp); - SAFEALIGN_COPY_UINT32(&buf->data[rp], &validate, &rp); - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->is_offline, &rp); - - SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->upn), &rp); - safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp); - - SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->ccname), &rp); - safealign_memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname), &rp); - - SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(keytab), &rp); - safealign_memcpy(&buf->data[rp], keytab, strlen(keytab), &rp); - - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->authtok_size, &rp); - safealign_memcpy(&buf->data[rp], kr->pd->authtok, - kr->pd->authtok_size, &rp); - - if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { - SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->newauthtok_size, &rp); - safealign_memcpy(&buf->data[rp], kr->pd->newauthtok, - kr->pd->newauthtok_size, &rp); - } - - *io_buf = buf; - - return EOK; -} - static struct krb5_ctx *get_krb5_ctx(struct be_req *be_req) { struct pam_data *pd; @@ -305,7 +233,6 @@ static int krb5_cleanup(void *ptr) if (kr == NULL) return EOK; - child_cleanup(kr->read_from_child_fd, kr->write_to_child_fd); memset(kr, 0, sizeof(struct krb5child_req)); return EOK; @@ -321,10 +248,9 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, DEBUG(1, ("talloc failed.\n")); return ENOMEM; } - kr->read_from_child_fd = -1; - kr->write_to_child_fd = -1; kr->is_offline = false; kr->active_ccache_present = true; + kr->run_as_user = true; talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); kr->pd = pd; @@ -335,249 +261,6 @@ errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd, return EOK; } -struct handle_child_state { - struct tevent_context *ev; - struct krb5child_req *kr; - uint8_t *buf; - ssize_t len; -}; - -static void krb5_child_timeout(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 handle_child_state *state = tevent_req_data(req, - struct handle_child_state); - struct krb5child_req *kr = state->kr; - int ret; - - if (kr->timeout_handler == NULL) { - return; - } - - DEBUG(9, ("timeout for child [%d] reached.\n", kr->child_pid)); - - ret = kill(kr->child_pid, SIGKILL); - if (ret == -1) { - DEBUG(1, ("kill failed [%d][%s].\n", errno, strerror(errno))); - } - - tevent_req_error(req, ETIMEDOUT); -} - -static errno_t activate_child_timeout_handler(struct tevent_req *req, - struct tevent_context *ev, - struct krb5child_req *kr) -{ - struct timeval tv; - struct handle_child_state *state = tevent_req_data(req, - struct handle_child_state); - - tv = tevent_timeval_current(); - tv = tevent_timeval_add(&tv, - dp_opt_get_int(kr->krb5_ctx->opts, - KRB5_AUTH_TIMEOUT), - 0); - kr->timeout_handler = tevent_add_timer(ev, state, tv, - krb5_child_timeout, req); - if (kr->timeout_handler == NULL) { - DEBUG(1, ("tevent_add_timer failed.\n")); - return ENOMEM; - } - - return EOK; -} - -static errno_t fork_child(struct tevent_req *req, struct tevent_context *ev, - struct krb5child_req *kr) -{ - 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", errno, strerror(errno))); - return err; - } - ret = pipe(pipefd_to_child); - if (ret == -1) { - err = errno; - DEBUG(1, ("pipe failed [%d][%s].\n", errno, strerror(errno))); - return err; - } - - pid = fork(); - - if (pid == 0) { /* child */ - /* We need to keep the root privileges to read the keytab file if - * validation is enabled, otherwise we can drop them here and run - * krb5_child with user privileges. - * If we are offline and want to create an empty ccache file. In this - * case we can drop the privileges, too. */ - if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) || - kr->is_offline) { - ret = become_user(kr->uid, kr->gid); - if (ret != EOK) { - DEBUG(1, ("become_user failed.\n")); - return ret; - } - } - - err = exec_child(kr, - pipefd_to_child, pipefd_from_child, - KRB5_CHILD, kr->krb5_ctx->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 */ - kr->child_pid = pid; - kr->read_from_child_fd = pipefd_from_child[0]; - close(pipefd_from_child[1]); - kr->write_to_child_fd = pipefd_to_child[1]; - close(pipefd_to_child[0]); - fd_nonblocking(kr->read_from_child_fd); - fd_nonblocking(kr->write_to_child_fd); - - ret = child_handler_setup(ev, pid, NULL, NULL); - if (ret != EOK) { - DEBUG(1, ("Could not set up child signal handler\n")); - return ret; - } - - err = activate_child_timeout_handler(req, ev, kr); - if (err != EOK) { - DEBUG(1, ("activate_child_timeout_handler failed.\n")); - } - - } else { /* error */ - err = errno; - DEBUG(1, ("fork failed [%d][%s].\n", errno, strerror(errno))); - return err; - } - - return EOK; -} - -static void handle_child_step(struct tevent_req *subreq); -static void handle_child_done(struct tevent_req *subreq); - -static struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct krb5child_req *kr) -{ - struct tevent_req *req, *subreq; - struct handle_child_state *state; - struct io_buffer *buf; - int ret; - - req = tevent_req_create(mem_ctx, &state, struct handle_child_state); - if (req == NULL) { - return NULL; - } - - state->ev = ev; - state->kr = kr; - state->buf = NULL; - state->len = 0; - - ret = create_send_buffer(kr, &buf); - if (ret != EOK) { - DEBUG(1, ("create_send_buffer failed.\n")); - goto fail; - } - - ret = fork_child(req, ev, kr); - if (ret != EOK) { - DEBUG(1, ("fork_child failed.\n")); - goto fail; - } - - subreq = write_pipe_send(state, ev, buf->data, buf->size, - kr->write_to_child_fd); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - tevent_req_set_callback(subreq, handle_child_step, req); - - return req; - -fail: - tevent_req_error(req, ret); - tevent_req_post(req, ev); - return req; -} - -static void handle_child_step(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct handle_child_state *state = tevent_req_data(req, - struct handle_child_state); - int ret; - - ret = write_pipe_recv(subreq); - talloc_zfree(subreq); - if (ret != EOK) { - tevent_req_error(req, ret); - return; - } - - close(state->kr->write_to_child_fd); - state->kr->write_to_child_fd = -1; - - subreq = read_pipe_send(state, state->ev, state->kr->read_from_child_fd); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, handle_child_done, req); -} - -static void handle_child_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct handle_child_state *state = tevent_req_data(req, - struct handle_child_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; - } - - close(state->kr->read_from_child_fd); - state->kr->read_from_child_fd = -1; - - tevent_req_done(req); - return; -} - -static int handle_child_recv(struct tevent_req *req, - TALLOC_CTX *mem_ctx, - uint8_t **buf, ssize_t *len) -{ - struct handle_child_state *state = tevent_req_data(req, - struct handle_child_state); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - *buf = talloc_move(mem_ctx, &state->buf); - *len = state->len; - - return EOK; -} - static void krb5_resolve_kdc_done(struct tevent_req *subreq); static void krb5_resolve_kpasswd_done(struct tevent_req *subreq); static void krb5_find_ccache_step(struct tevent_req *req); @@ -957,6 +640,17 @@ static void krb5_find_ccache_step(struct tevent_req *req) } } + /* We need to keep the root privileges to read the keytab file if + * validation is enabled, otherwise we can drop them and run krb5_child + * with user privileges. + * If we are offline we want to create an empty ccache file. In this + * case we can drop the privileges, too. */ + if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) || kr->is_offline) { + kr->run_as_user = true; + } else { + kr->run_as_user = false; + } + subreq = handle_child_send(state, state->ev, kr); if (subreq == NULL) { DEBUG(1, ("handle_child_send failed.\n")); @@ -996,7 +690,6 @@ static void krb5_child_done(struct tevent_req *subreq) int32_t msg_len; ret = handle_child_recv(subreq, pd, &buf, &len); - talloc_zfree(kr->timeout_handler); talloc_zfree(subreq); if (ret != EOK) { DEBUG(1, ("child failed (%d [%s])\n", ret, strerror(ret))); -- cgit