summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/providers/krb5/krb5_auth.c331
-rw-r--r--src/providers/krb5/krb5_auth.h14
-rw-r--r--src/providers/krb5/krb5_child_handler.c409
3 files changed, 429 insertions, 325 deletions
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 80d8734a..9dc7a2c9 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)));
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index a5405864..130e85db 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -30,6 +30,7 @@
#include "util/sss_krb5.h"
#include "providers/dp_backend.h"
+#include "providers/child_common.h"
#include "providers/krb5/krb5_common.h"
#define CCACHE_ENV_NAME "KRB5CCNAME"
@@ -37,15 +38,9 @@
#define ILLEGAL_PATH_PATTERN "//|/\\./|/\\.\\./"
struct krb5child_req {
- pid_t child_pid;
- int read_from_child_fd;
- int write_to_child_fd;
-
struct pam_data *pd;
struct krb5_ctx *krb5_ctx;
- struct tevent_timer *timeout_handler;
-
const char *ccname;
const char *old_ccname;
const char *homedir;
@@ -57,6 +52,7 @@ struct krb5child_req {
struct fo_server *kpasswd_srv;
bool active_ccache_present;
bool valid_tgt_present;
+ bool run_as_user;
};
errno_t krb5_setup(TALLOC_CTX *mem_ctx, struct pam_data *pd,
@@ -71,6 +67,12 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
struct krb5_ctx *krb5_ctx);
int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err);
+struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct krb5child_req *kr);
+int handle_child_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **buf, ssize_t *len);
+
errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
struct pam_data *pd,
uid_t uid);
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
new file mode 100644
index 00000000..e708c50c
--- /dev/null
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -0,0 +1,409 @@
+/*
+ SSSD
+
+ Kerberos 5 Backend Module - Manage krb5_child
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2010 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 "util/util.h"
+#include "providers/child_common.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_auth.h"
+#include "src/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
+
+struct io {
+ int read_from_child_fd;
+ int write_to_child_fd;
+};
+
+struct handle_child_state {
+ struct tevent_context *ev;
+ struct krb5child_req *kr;
+ uint8_t *buf;
+ ssize_t len;
+
+ struct tevent_timer *timeout_handler;
+ pid_t child_pid;
+
+ struct io *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(1, ("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(1, ("close failed [%d][%s].\n", ret, strerror(ret)));
+ }
+ }
+
+ return EOK;
+}
+
+static 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;
+ size_t username_len;
+
+ 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 = 6*sizeof(uint32_t) + strlen(kr->upn);
+
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
+ kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
+ buf->size += 3*sizeof(uint32_t) + 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;
+ }
+
+ if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
+ username_len = strlen(kr->pd->user);
+ buf->size += sizeof(uint32_t) + username_len;
+ }
+
+ 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);
+
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
+ kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
+ 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);
+ }
+
+ if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
+ SAFEALIGN_SET_UINT32(&buf->data[rp], username_len, &rp);
+ safealign_memcpy(&buf->data[rp], kr->pd->user, username_len, &rp);
+ }
+
+ *io_buf = buf;
+
+ return EOK;
+}
+
+
+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);
+ int ret;
+
+ if (state->timeout_handler == NULL) {
+ return;
+ }
+
+ DEBUG(9, ("timeout for 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 activate_child_timeout_handler(struct tevent_req *req,
+ struct tevent_context *ev,
+ const uint32_t timeout_seconds)
+{
+ 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, timeout_seconds, 0);
+ state->timeout_handler = tevent_add_timer(ev, state, tv,
+ krb5_child_timeout, req);
+ if (state->timeout_handler == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t fork_child(struct tevent_req *req)
+{
+ int pipefd_to_child[2];
+ int pipefd_from_child[2];
+ pid_t pid;
+ int ret;
+ errno_t err;
+ struct handle_child_state *state = tevent_req_data(req,
+ struct handle_child_state);
+
+ 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 */
+ if (state->kr->run_as_user) {
+ ret = become_user(state->kr->uid, state->kr->gid);
+ if (ret != EOK) {
+ DEBUG(1, ("become_user failed.\n"));
+ return ret;
+ }
+ }
+
+ err = exec_child(state,
+ pipefd_to_child, pipefd_from_child,
+ KRB5_CHILD, state->kr->krb5_ctx->child_debug_fd);
+ if (err != EOK) {
+ DEBUG(1, ("Could not exec KRB5 child: [%d][%s].\n",
+ err, strerror(err)));
+ return err;
+ }
+ } else if (pid > 0) { /* parent */
+ state->child_pid = pid;
+ 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);
+ if (ret != EOK) {
+ DEBUG(1, ("Could not set up child signal handler\n"));
+ return ret;
+ }
+
+ err = activate_child_timeout_handler(req, state->ev,
+ dp_opt_get_int(state->kr->krb5_ctx->opts, KRB5_AUTH_TIMEOUT));
+ 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);
+
+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;
+ int ret;
+ struct io_buffer *buf;
+
+ 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;
+ state->child_pid = -1;
+ state->timeout_handler = NULL;
+
+ state->io = talloc(state, struct io);
+ if (state->io == NULL) {
+ DEBUG(1, ("talloc failed.\n"));
+ ret = ENOMEM;
+ goto fail;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ ret = create_send_buffer(kr, &buf);
+ if (ret != EOK) {
+ DEBUG(1, ("create_send_buffer failed.\n"));
+ goto fail;
+ }
+
+ ret = fork_child(req);
+ if (ret != EOK) {
+ DEBUG(1, ("fork_child failed.\n"));
+ goto fail;
+ }
+
+ subreq = write_pipe_send(state, ev, buf->data, buf->size,
+ state->io->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->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) {
+ 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;
+
+ talloc_zfree(state->timeout_handler);
+
+ ret = read_pipe_recv(subreq, state, &state->buf, &state->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;
+
+ tevent_req_done(req);
+ return;
+}
+
+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;
+}