>From b593c526918c471425ad62f972469bfb71d629d4 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 7 Sep 2009 18:07:26 +0200 Subject: [PATCH 2/2] use fork+exec for kerberos helper --- server/Makefile.am | 14 ++- server/providers/data_provider.h | 2 + server/providers/krb5/krb5_auth.c | 315 ++++++++++++++++++++++++------------ server/providers/krb5/krb5_auth.h | 28 +--- server/providers/krb5/krb5_child.c | 313 +++++++++++++++++++++++++++++------- 5 files changed, 477 insertions(+), 195 deletions(-) diff --git a/server/Makefile.am b/server/Makefile.am index 42e037a..159c07e 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -51,6 +51,7 @@ sssdlibexec_PROGRAMS = \ sssd_pam \ sssd_dp \ sssd_be \ + krb5_child \ $(sssd_pk) \ $(sssd_info) @@ -389,8 +390,7 @@ libsss_proxy_la_LDFLAGS = \ -module libsss_krb5_la_SOURCES = \ - providers/krb5/krb5_auth.c \ - providers/krb5/krb5_child.c + providers/krb5/krb5_auth.c libsss_krb5_la_CFLAGS = \ $(AM_CFLAGS) \ $(KRB5_CFLAGS) @@ -401,6 +401,16 @@ libsss_krb5_la_LDFLAGS = \ -version-info 1:0:0 \ -module +krb5_child_SOURCES = \ + $(SSSD_DEBUG_OBJ) \ + providers/krb5/krb5_child.c +krb5_child_CFLAGS = \ + $(AM_CFLAGS) \ + $(KRB5_CFLAGS) +krb5_child_LDADD = \ + $(TALLOC_LIBS) \ + $(KRB5_LIBS) + memberof_la_SOURCES = \ ldb_modules/memberof.c memberof_la_CFLAGS = \ diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 91adecb..1886340 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -119,6 +119,8 @@ struct pam_data { int priv; uid_t pw_uid; gid_t gr_gid; + + const char *upn; }; /* from dp_auth_util.c */ diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c index b957466..73d3ccd 100644 --- a/server/providers/krb5/krb5_auth.c +++ b/server/providers/krb5/krb5_auth.c @@ -40,6 +40,109 @@ #include "krb5_plugin/sssd_krb5_locator_plugin.h" #include "providers/krb5/krb5_auth.h" +#ifndef SSSD_LIBEXEC_PATH +#error "SSSD_LIBEXEC_PATH not defined" +#else +#define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child" +#endif + +struct krb5child_req { + pid_t child_pid; + int read_from_child_fd; + int write_to_child_fd; + + struct be_req *req; + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; +}; + +static errno_t become_user(uid_t uid, gid_t gid) +{ + int ret; + ret = setgid(gid); + if (ret == -1) { + DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + ret = setuid(uid); + if (ret == -1) { + DEBUG(1, ("setuid failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + ret = setegid(gid); + if (ret == -1) { + DEBUG(1, ("setegid failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + ret = seteuid(uid); + if (ret == -1) { + DEBUG(1, ("seteuid failed [%d][%s].\n", errno, strerror(errno))); + return errno; + } + + return EOK; +} + +struct io_buffer { + uint8_t *data; + size_t size; +}; + +errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) +{ + struct io_buffer *buf; + size_t rp; + + buf = talloc(kr, struct io_buffer); + if (buf == NULL) { + DEBUG(1, ("talloc failed.\n")); + return ENOMEM; + } + + buf->size = 3*sizeof(int) + strlen(kr->pd->upn) + kr->pd->authtok_size; + if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { + buf->size += sizeof(int) + 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; + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->cmd; + rp += sizeof(uint32_t); + + ((uint32_t *)(&buf->data[rp]))[0] = strlen(kr->pd->upn); + rp += sizeof(uint32_t); + + memcpy(&buf->data[rp], kr->pd->upn, strlen(kr->pd->upn)); + rp += strlen(kr->pd->upn); + + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->authtok_size; + rp += sizeof(uint32_t); + + memcpy(&buf->data[rp], kr->pd->authtok, kr->pd->authtok_size); + rp += kr->pd->authtok_size; + + if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->newauthtok_size; + rp += sizeof(uint32_t); + + memcpy(&buf->data[rp], kr->pd->newauthtok, kr->pd->newauthtok_size); + rp += kr->pd->newauthtok_size; + } + + *io_buf = buf; + + return EOK; +} + static void fd_nonblocking(int fd) { int flags; @@ -56,45 +159,31 @@ static void fd_nonblocking(int fd) { return; } -static void krb5_cleanup(struct krb5_req *kr) +static void krb5_cleanup(struct krb5child_req *kr) { if (kr == NULL) return; - /* FIXME: is it safe to drop the "!= NULL" checks? */ - if (kr->options != NULL) - krb5_get_init_creds_opt_free(kr->ctx, kr->options); - if (kr->creds != NULL) - krb5_free_cred_contents(kr->ctx, kr->creds); - if (kr->name != NULL) - krb5_free_unparsed_name(kr->ctx, kr->name); - if (kr->princ != NULL) - krb5_free_principal(kr->ctx, kr->princ); - if (kr->cc != NULL) - krb5_cc_close(kr->ctx, kr->cc); - if (kr->ctx != NULL) - krb5_free_context(kr->ctx); - - memset(kr, 0, sizeof(struct krb5_req)); + memset(kr, 0, sizeof(struct krb5child_req)); talloc_zfree(kr); } -static int krb5_setup(struct be_req *req, const char *user_princ_str, - struct krb5_req **krb5_req) +static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req) { - struct krb5_req *kr = NULL; + struct krb5child_req *kr = NULL; struct krb5_ctx *krb5_ctx; struct pam_data *pd; - krb5_error_code kerr = 0; + errno_t err; pd = talloc_get_type(req->req_data, struct pam_data); - krb5_ctx = talloc_get_type(req->be_ctx->bet_info[BET_AUTH].pvt_bet_data, struct krb5_ctx); + krb5_ctx = talloc_get_type(req->be_ctx->bet_info[BET_AUTH].pvt_bet_data, + struct krb5_ctx); - kr = talloc_zero(req, struct krb5_req); + kr = talloc_zero(req, struct krb5child_req); if (kr == NULL) { DEBUG(1, ("talloc failed.\n")); - kerr = ENOMEM; + err = ENOMEM; goto failed; } @@ -102,70 +191,14 @@ static int krb5_setup(struct be_req *req, const char *user_princ_str, kr->req = req; kr->krb5_ctx = krb5_ctx; - switch(pd->cmd) { - case SSS_PAM_AUTHENTICATE: - kr->client = tgt_req_child; - break; - case SSS_PAM_CHAUTHTOK: - kr->client = changepw_child; - break; - default: - DEBUG(1, ("PAM command [%d] not supported.\n", pd->cmd)); - kerr = EINVAL; - goto failed; - } - - kerr = krb5_init_context(&kr->ctx); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto failed; - } - - kerr = krb5_parse_name(kr->ctx, user_princ_str, &kr->princ); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto failed; - } - - kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto failed; - } - - kr->creds = talloc_zero(kr, krb5_creds); - if (kr->creds == NULL) { - DEBUG(1, ("talloc_zero failed.\n")); - kerr = ENOMEM; - goto failed; - } - - kerr = krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options); - if (kerr != 0) { - KRB5_DEBUG(1, kerr); - goto failed; - } - -/* TODO: set options, e.g. - * krb5_get_init_creds_opt_set_tkt_life - * krb5_get_init_creds_opt_set_renew_life - * krb5_get_init_creds_opt_set_forwardable - * krb5_get_init_creds_opt_set_proxiable - * krb5_get_init_creds_opt_set_etype_list - * krb5_get_init_creds_opt_set_address_list - * krb5_get_init_creds_opt_set_preauth_list - * krb5_get_init_creds_opt_set_salt - * krb5_get_init_creds_opt_set_change_password_prompt - * krb5_get_init_creds_opt_set_pa - */ - *krb5_req = kr; + return EOK; failed: krb5_cleanup(kr); - return kerr; + return err; } static void wait_for_child_handler(struct tevent_context *ev, @@ -198,32 +231,81 @@ static void wait_for_child_handler(struct tevent_context *ev, return; } -static int fork_child(struct krb5_req *kr) +static errno_t fork_child(struct krb5child_req *kr) { - int pipefd[2]; + int pipefd_to_child[2]; + int pipefd_from_child[2]; pid_t pid; int ret; + errno_t err; - ret = pipe(pipefd); + ret = pipe(pipefd_from_child); if (ret == -1) { + err = errno; DEBUG(1, ("pipe failed [%d][%s].\n", errno, strerror(errno))); - return -1; + 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 */ - close(pipefd[0]); - kr->client(pipefd[1], kr); + //talloc_free(kr->req->be_ctx->ev); + + ret = chdir("/tmp"); + if (ret == -1) { + err = errno; + DEBUG(1, ("chdir failed [%d][%s].\n", errno, strerror(errno))); + return err; + } + + ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); + if (ret != EOK) { + DEBUG(1, ("become_user failed.\n")); + return ret; + } + + + close(pipefd_to_child[1]); + ret = dup2(pipefd_to_child[0],STDIN_FILENO); + if (ret == -1) { + err = errno; + DEBUG(1, ("dup2 failed [%d][%s].\n", errno, strerror(errno))); + return err; + } + + close(pipefd_from_child[0]); + ret = dup2(pipefd_from_child[1],STDOUT_FILENO); + if (ret == -1) { + err = errno; + DEBUG(1, ("dup2 failed [%d][%s].\n", errno, strerror(errno))); + return err; + } + + ret = execl(KRB5_CHILD, KRB5_CHILD, NULL); + if (ret == -1) { + err = errno; + DEBUG(1, ("execl failed [%d][%s].\n", errno, strerror(errno))); + return err; + } } else if (pid > 0) { /* parent */ kr->child_pid = pid; - kr->fd = pipefd[0]; - close(pipefd[1]); - - fd_nonblocking(kr->fd); + 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); } else { /* error */ + err = errno; DEBUG(1, ("fork failed [%d][%s].\n", errno, strerror(errno))); - return -1; + return err; } return EOK; @@ -310,7 +392,7 @@ static ssize_t read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, } struct handle_child_state { - struct krb5_req *kr; + struct krb5child_req *kr; ssize_t len; uint8_t *buf; }; @@ -318,12 +400,19 @@ struct handle_child_state { 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 krb5_req *kr) + struct krb5child_req *kr) { int ret; struct tevent_req *req; struct tevent_req *subreq; struct handle_child_state *state; + struct io_buffer *buf; + + ret = create_send_buffer(kr, &buf); + if (ret != EOK) { + DEBUG(1, ("create_send_buffer failed.\n")); + return NULL; + } ret = fork_child(kr); if (ret != EOK) { @@ -331,6 +420,13 @@ static struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx, struct tevent_c return NULL; } + ret = write(kr->write_to_child_fd, buf->data, buf->size); + close(kr->write_to_child_fd); + if (ret == -1) { + DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); + return NULL; + } + req = tevent_req_create(mem_ctx, &state, struct handle_child_state); if (req == NULL) { return NULL; @@ -338,7 +434,7 @@ static struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx, struct tevent_c state->kr = kr; - subreq = read_pipe_send(state, ev, kr->fd); + subreq = read_pipe_send(state, ev, kr->read_from_child_fd); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -356,7 +452,7 @@ static void handle_child_done(struct tevent_req *subreq) state->len = read_pipe_recv(subreq, state, &state->buf, &error); talloc_zfree(subreq); - close(state->kr->fd); + close(state->kr->read_from_child_fd); if (state->len == -1) { tevent_req_error(req, error); return; @@ -427,12 +523,12 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) { struct be_req *be_req = talloc_get_type(pvt, struct be_req); struct krb5_ctx *krb5_ctx; - struct krb5_req *kr = NULL; + struct krb5child_req *kr = NULL; struct tevent_req *req; int ret; struct pam_data *pd; int pam_status=PAM_SYSTEM_ERR; - const char *upn = NULL; + //const char *upn = NULL; pd = talloc_get_type(be_req->req_data, struct pam_data); krb5_ctx = talloc_get_type(be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data, @@ -449,16 +545,16 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) break; case 1: - upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL); - if (upn == NULL && krb5_ctx->try_simple_upn) { + pd->upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL); + if (pd->upn == NULL && krb5_ctx->try_simple_upn) { /* NOTE: this is a hack, works only in some environments */ if (krb5_ctx->realm != NULL) { - upn = talloc_asprintf(be_req, "%s@%s", pd->user, + pd->upn = talloc_asprintf(be_req, "%s@%s", pd->user, krb5_ctx->realm); - if (upn == NULL) { + if (pd->upn == NULL) { DEBUG(1, ("failed to build simple upn.\n")); } - DEBUG(9, ("Using simple UPN [%s].\n", upn)); + DEBUG(9, ("Using simple UPN [%s].\n", pd->upn)); } } break; @@ -469,12 +565,12 @@ static void get_user_upn_done(void *pvt, int err, struct ldb_result *res) break; } - if (upn == NULL) { + if (pd->upn == NULL) { DEBUG(1, ("Cannot set UPN.\n")); goto failed; } - ret = krb5_setup(be_req, upn, &kr); + ret = krb5_setup(be_req, &kr); if (ret != EOK) { DEBUG(1, ("krb5_setup failed.\n")); goto failed; @@ -499,7 +595,8 @@ failed: static void krb5_pam_handler_done(struct tevent_req *req) { - struct krb5_req *kr = tevent_req_callback_data(req, struct krb5_req); + struct krb5child_req *kr = tevent_req_callback_data(req, + struct krb5child_req); struct pam_data *pd = kr->pd; struct be_req *be_req = kr->req; struct krb5_ctx *krb5_ctx = kr->krb5_ctx; @@ -719,6 +816,12 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, struct bet_ops **ops, } ctx->changepw_principle = value; + ret = setenv(SSSD_KRB5_CHANGEPW_PRINCIPLE, ctx->changepw_principle, 1); + if (ret != EOK) { + DEBUG(2, ("setenv %s failed, password change might fail.\n", + SSSD_KRB5_CHANGEPW_PRINCIPLE)); + } + /* TODO: set options */ sige = tevent_add_signal(bectx->ev, ctx, SIGCHLD, SA_SIGINFO, diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h index 0db3ef0..123a189 100644 --- a/server/providers/krb5/krb5_auth.h +++ b/server/providers/krb5/krb5_auth.h @@ -28,6 +28,7 @@ #define MAX_CHILD_MSG_SIZE 255 #define CCACHE_ENV_NAME "KRB5CCNAME" +#define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE" typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; @@ -65,31 +66,4 @@ struct krb5_ctx { char *changepw_principle; }; -struct krb5_req { - krb5_context ctx; - krb5_ccache cc; - krb5_principal princ; - char* name; - krb5_creds *creds; - krb5_get_init_creds_opt *options; - pid_t child_pid; - int fd; - - struct be_req *req; - struct pam_data *pd; - struct krb5_ctx *krb5_ctx; - void (*client)(int fd, struct krb5_req *kr); -}; - -static krb5_context krb5_error_ctx; -static const char *__krb5_error_msg; -#define KRB5_DEBUG(level, krb5_error) do { \ - __krb5_error_msg = krb5_get_error_message(krb5_error_ctx, krb5_error); \ - DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \ - krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \ -} while(0); - -void tgt_req_child(int fd, struct krb5_req *kr); -void changepw_child(int fd, struct krb5_req *kr); - #endif /* __KRB5_AUTH_H__ */ diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c index daa2f71..e272a7f 100644 --- a/server/providers/krb5/krb5_child.c +++ b/server/providers/krb5/krb5_child.c @@ -21,6 +21,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ + #include #include @@ -30,6 +31,31 @@ #include "providers/dp_backend.h" #include "providers/krb5/krb5_auth.h" +struct krb5_req { + krb5_context ctx; + krb5_ccache cc; + krb5_principal princ; + char* name; + krb5_creds *creds; + krb5_get_init_creds_opt *options; + pid_t child_pid; + int read_from_child_fd; + int write_to_child_fd; + + struct be_req *req; + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; + errno_t (*child_req)(int fd, struct krb5_req *kr); +}; + +static krb5_context krb5_error_ctx; +static const char *__krb5_error_msg; +#define KRB5_DEBUG(level, krb5_error) do { \ + __krb5_error_msg = krb5_get_error_message(krb5_error_ctx, krb5_error); \ + DEBUG(level, ("%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg)); \ + krb5_free_error_message(krb5_error_ctx, __krb5_error_msg); \ +} while(0); + struct response { size_t max_size; size_t size; @@ -78,7 +104,7 @@ static errno_t pack_response_packet(struct response *resp, int status, int type, return EOK; } -static struct response * prepare_response_message(struct krb5_req *kr, +static struct response *prepare_response_message(struct krb5_req *kr, krb5_error_code kerr, int pam_status) { const char *cc_name = NULL; @@ -127,36 +153,6 @@ static struct response * prepare_response_message(struct krb5_req *kr, return resp; } -static errno_t become_user(uid_t uid, gid_t gid) -{ - int ret; - ret = setgid(gid); - if (ret == -1) { - DEBUG(1, ("setgid failed [%d][%s].\n", errno, strerror(errno))); - return errno; - } - - ret = setuid(uid); - if (ret == -1) { - DEBUG(1, ("setuid failed [%d][%s].\n", errno, strerror(errno))); - return errno; - } - - ret = setegid(gid); - if (ret == -1) { - DEBUG(1, ("setegid failed [%d][%s].\n", errno, strerror(errno))); - return errno; - } - - ret = seteuid(uid); - if (ret == -1) { - DEBUG(1, ("seteuid failed [%d][%s].\n", errno, strerror(errno))); - return errno; - } - - return EOK; -} - static krb5_error_code get_and_save_tgt(struct krb5_req *kr, char *password) { @@ -186,17 +182,21 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, if (kerr != 0) { KRB5_DEBUG(1, kerr); krb5_cc_destroy(kr->ctx, kr->cc); + kr->cc = NULL; return kerr; } + krb5_free_cred_contents(kr->ctx, kr->creds); + return 0; } -void changepw_child(int fd, struct krb5_req *kr) +static errno_t changepw_child(int fd, struct krb5_req *kr) { int ret; krb5_error_code kerr = 0; + errno_t err; char *pass_str = NULL; char *newpass_str = NULL; struct response *resp = NULL; @@ -205,16 +205,11 @@ void changepw_child(int fd, struct krb5_req *kr) krb5_data result_code_string; krb5_data result_string; - if (kr->pd->priv != 1) { - ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); - if (ret != EOK) { - DEBUG(1, ("become_user failed.\n")); - kerr = KRB5KRB_ERR_GENERIC; - goto sendresponse; - } - } else { -/* TODO: implement password reset by root */ - DEBUG(1, ("Password reset not implemented.\n")); + char *changepw_principle = NULL; + + changepw_principle = getenv(SSSD_KRB5_CHANGEPW_PRINCIPLE); + if (changepw_principle == NULL) { + DEBUG(1, ("Change password principle not available.\n")); kerr = KRB5KRB_ERR_GENERIC; goto sendresponse; } @@ -229,7 +224,7 @@ void changepw_child(int fd, struct krb5_req *kr) kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, pass_str, NULL, NULL, 0, - kr->krb5_ctx->changepw_principle, + changepw_principle, kr->options); if (kerr != 0) { KRB5_DEBUG(1, kerr); @@ -272,6 +267,7 @@ void changepw_child(int fd, struct krb5_req *kr) goto sendresponse; } + krb5_free_cred_contents(kr->ctx, kr->creds); kerr = get_and_save_tgt(kr, newpass_str); memset(newpass_str, 0, kr->pd->newauthtok_size); @@ -290,37 +286,36 @@ sendresponse: if (resp == NULL) { DEBUG(1, ("prepare_response_message failed.\n")); krb5_cc_destroy(kr->ctx, kr->cc); - _exit(-1); + kr->cc = NULL; + return ENOMEM; } ret = write(fd, resp->buf, resp->size); if (ret == -1) { + err = errno; DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); krb5_cc_destroy(kr->ctx, kr->cc); - _exit(ret); + kr->cc = NULL; + return err; } - krb5_cc_close(kr->ctx, kr->cc); - + if (kr->cc != NULL) { + krb5_cc_close(kr->ctx, kr->cc); + kr->cc = NULL; + } - _exit(0); + return EOK; } -void tgt_req_child(int fd, struct krb5_req *kr) +static errno_t tgt_req_child(int fd, struct krb5_req *kr) { int ret; krb5_error_code kerr = 0; + errno_t err; char *pass_str = NULL; int pam_status = PAM_SYSTEM_ERR; struct response *resp = NULL; - ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); - if (ret != EOK) { - DEBUG(1, ("become_user failed.\n")); - kerr = KRB5KRB_ERR_GENERIC; - goto sendresponse; - } - pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok, kr->pd->authtok_size); if (pass_str == NULL) { @@ -346,18 +341,216 @@ sendresponse: if (resp == NULL) { DEBUG(1, ("prepare_response_message failed.\n")); krb5_cc_destroy(kr->ctx, kr->cc); - _exit(-1); + kr->cc = NULL; + return ENOMEM; } ret = write(fd, resp->buf, resp->size); if (ret == -1) { + err = errno; DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); krb5_cc_destroy(kr->ctx, kr->cc); - _exit(ret); + kr->cc = NULL; + return err; } - krb5_cc_close(kr->ctx, kr->cc); + if (kr->cc != NULL) { + krb5_cc_close(kr->ctx, kr->cc); + kr->cc = NULL; + } + return EOK; +} + +static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd) +{ + size_t p = 0; + uint32_t *len; + uint8_t *str; + + len = ((uint32_t *)(buf+p)); + pd->cmd = *len; + p += sizeof(uint32_t); + + len = ((uint32_t *)(buf+p)); + p += sizeof(uint32_t); + str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1)); + if (str == NULL) return ENOMEM; + str[*len] = '\0'; + pd->upn = (char *) str; + p += *len; + + len = ((uint32_t *)(buf+p)); + p += sizeof(uint32_t); + str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1)); + if (str == NULL) return ENOMEM; + str[*len] = '\0'; + pd->authtok = str; + pd->authtok_size = *len + 1; + p += *len; + + if (pd->cmd == SSS_PAM_CHAUTHTOK) { + len = ((uint32_t *)(buf+p)); + p += sizeof(uint32_t); + str = talloc_memdup(pd, buf+p, sizeof(char) * (*len + 1)); + if (str == NULL) return ENOMEM; + str[*len] = '\0'; + pd->newauthtok = str; + pd->newauthtok_size = *len + 1; + p += *len; + } else { + pd->newauthtok = NULL; + pd->newauthtok_size = 0; + } - _exit(0); + return EOK; +} + +static int krb5_cleanup(void *ptr) +{ + struct krb5_req *kr = talloc_get_type(ptr, struct krb5_req); + if (kr == NULL) return EOK; + + /* FIXME: is it safe to drop the "!= NULL" checks? */ + if (kr->options != NULL) + krb5_get_init_creds_opt_free(kr->ctx, kr->options); + if (kr->creds != NULL) + krb5_free_cred_contents(kr->ctx, kr->creds); + krb5_free_creds(kr->ctx, kr->creds); + if (kr->name != NULL) + krb5_free_unparsed_name(kr->ctx, kr->name); + if (kr->princ != NULL) + krb5_free_principal(kr->ctx, kr->princ); + if (kr->cc != NULL) + krb5_cc_close(kr->ctx, kr->cc); + if (kr->ctx != NULL) + krb5_free_context(kr->ctx); + + memset(kr, 0, sizeof(struct krb5_req)); + + return EOK; +} + +static int krb5_setup(struct pam_data *pd, const char *user_princ_str, + struct krb5_req **krb5_req) +{ + struct krb5_req *kr = NULL; + krb5_error_code kerr = 0; + + kr = talloc_zero(pd, struct krb5_req); + if (kr == NULL) { + DEBUG(1, ("talloc failed.\n")); + kerr = ENOMEM; + goto failed; + } + talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup); + + kr->pd = pd; + + switch(pd->cmd) { + case SSS_PAM_AUTHENTICATE: + kr->child_req = tgt_req_child; + break; + case SSS_PAM_CHAUTHTOK: + kr->child_req = changepw_child; + break; + default: + DEBUG(1, ("PAM command [%d] not supported.\n", pd->cmd)); + kerr = EINVAL; + goto failed; + } + + kerr = krb5_init_context(&kr->ctx); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + + kerr = krb5_parse_name(kr->ctx, user_princ_str, &kr->princ); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + + kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + + kr->creds = calloc(1, sizeof(krb5_creds)); + if (kr->creds == NULL) { + DEBUG(1, ("talloc_zero failed.\n")); + kerr = ENOMEM; + goto failed; + } + + kerr = krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + goto failed; + } + +/* TODO: set options, e.g. + * krb5_get_init_creds_opt_set_tkt_life + * krb5_get_init_creds_opt_set_renew_life + * krb5_get_init_creds_opt_set_forwardable + * krb5_get_init_creds_opt_set_proxiable + * krb5_get_init_creds_opt_set_etype_list + * krb5_get_init_creds_opt_set_address_list + * krb5_get_init_creds_opt_set_preauth_list + * krb5_get_init_creds_opt_set_salt + * krb5_get_init_creds_opt_set_change_password_prompt + * krb5_get_init_creds_opt_set_pa + */ + + *krb5_req = kr; + return EOK; + +failed: + talloc_free(kr); + + return kerr; +} + +int main(void) +{ + uint8_t *buf = NULL; + int ret; + struct pam_data *pd = NULL; + struct krb5_req *kr = NULL; + + pd = talloc(NULL, struct pam_data); + + buf = talloc_size(pd, sizeof(uint8_t)*512); + if (buf == NULL) { + DEBUG(1, ("malloc failed.\n")); + _exit(-1); + } + + ret = read(STDIN_FILENO, buf, 512); + if (ret == -1) { + DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno))); + talloc_free(pd); + exit(-1); + } + close(STDIN_FILENO); + + ret = unpack_buffer(buf, ret, pd); + if (ret != EOK) { + DEBUG(1, ("unpack_buffer failed.\n")); + talloc_free(pd); + exit(-1); + } + + krb5_setup(pd, pd->upn, &kr); + ret = kr->child_req(STDOUT_FILENO, kr); + if (ret != EOK) { + DEBUG(1, ("Child request failed.\n")); + } + + close(STDOUT_FILENO); + talloc_free(pd); + + return 0; } -- 1.6.2.5