diff options
Diffstat (limited to 'server/providers/krb5')
-rw-r--r-- | server/providers/krb5/krb5_auth.c | 74 | ||||
-rw-r--r-- | server/providers/krb5/krb5_become_user.c | 61 | ||||
-rw-r--r-- | server/providers/krb5/krb5_child.c | 148 | ||||
-rw-r--r-- | server/providers/krb5/krb5_common.c | 2 | ||||
-rw-r--r-- | server/providers/krb5/krb5_common.h | 2 | ||||
-rw-r--r-- | server/providers/krb5/krb5_utils.h | 2 |
6 files changed, 250 insertions, 39 deletions
diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c index a02147e4..d3e05e12 100644 --- a/server/providers/krb5/krb5_auth.c +++ b/server/providers/krb5/krb5_auth.c @@ -43,36 +43,6 @@ #define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child" #endif -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; @@ -82,6 +52,16 @@ 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) { @@ -89,10 +69,11 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) return ENOMEM; } - buf->size = 4*sizeof(int) + strlen(kr->pd->upn) + strlen(kr->ccname) + + buf->size = 8*sizeof(uint32_t) + strlen(kr->pd->upn) + strlen(kr->ccname) + + strlen(keytab) + kr->pd->authtok_size; if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) { - buf->size += sizeof(int) + kr->pd->newauthtok_size; + buf->size += sizeof(uint32_t) + kr->pd->newauthtok_size; } buf->data = talloc_size(kr, buf->size); @@ -106,6 +87,15 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->cmd; rp += sizeof(uint32_t); + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->pw_uid; + rp += sizeof(uint32_t); + + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->gr_gid; + rp += sizeof(uint32_t); + + ((uint32_t *)(&buf->data[rp]))[0] = validate; + rp += sizeof(uint32_t); + ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(kr->pd->upn); rp += sizeof(uint32_t); @@ -118,6 +108,12 @@ errno_t create_send_buffer(struct krb5child_req *kr, struct io_buffer **io_buf) memcpy(&buf->data[rp], kr->ccname, strlen(kr->ccname)); rp += strlen(kr->ccname); + ((uint32_t *)(&buf->data[rp]))[0] = (uint32_t) strlen(keytab); + rp += sizeof(uint32_t); + + memcpy(&buf->data[rp], keytab, strlen(keytab)); + rp += strlen(keytab); + ((uint32_t *)(&buf->data[rp]))[0] = kr->pd->authtok_size; rp += sizeof(uint32_t); @@ -436,13 +432,17 @@ static errno_t fork_child(struct krb5child_req *kr) return err; } - ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); - if (ret != EOK) { - DEBUG(1, ("become_user failed.\n")); - return ret; + /* 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 (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE)) { + 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) { diff --git a/server/providers/krb5/krb5_become_user.c b/server/providers/krb5/krb5_become_user.c new file mode 100644 index 00000000..351f539a --- /dev/null +++ b/server/providers/krb5/krb5_become_user.c @@ -0,0 +1,61 @@ +/* + SSSD + + Kerberos 5 Backend Module -- Utilities + + Authors: + Sumit Bose <sbose@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 <unistd.h> + +#include "util/util.h" + +errno_t become_user(uid_t uid, gid_t gid) +{ + int ret; + + DEBUG(9, ("Trying to become user [%d][%d].\n", uid, gid)); + 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; +} + diff --git a/server/providers/krb5/krb5_child.c b/server/providers/krb5/krb5_child.c index 2630e0f4..66fccf89 100644 --- a/server/providers/krb5/krb5_child.c +++ b/server/providers/krb5/krb5_child.c @@ -88,6 +88,8 @@ struct krb5_req { errno_t (*child_req)(int fd, struct krb5_req *kr); char *ccname; + char *keytab; + bool validate; }; static krb5_context krb5_error_ctx; @@ -193,10 +195,104 @@ static struct response *prepare_response_message(struct krb5_req *kr, return resp; } +static krb5_error_code validate_tgt(struct krb5_req *kr) +{ + krb5_error_code kerr; + krb5_error_code kt_err; + char *principal; + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_verify_init_creds_opt opt; + + memset(&keytab, 0, sizeof(keytab)); + kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab); + if (kerr != 0) { + DEBUG(1, ("error resolving keytab [%s], not verifying TGT.\n", + kr->keytab)); + return kerr; + } + + memset(&cursor, 0, sizeof(cursor)); + kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor); + if (kerr != 0) { + DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n", + kr->keytab)); + return kerr; + } + + /* We look for the first entry from our realm or take the last one */ + memset(&entry, 0, sizeof(entry)); + while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) { + if (krb5_realm_compare(kr->ctx, entry.principal, kr->princ)) { + DEBUG(9, ("Found keytab entry with the realm of the credential.\n")); + break; + } + + kerr = krb5_free_keytab_entry_contents(kr->ctx, &entry); + if (kerr != 0) { + DEBUG(1, ("Failed to free keytab entry.\n")); + } + memset(&entry, 0, sizeof(entry)); + } + + /* Close the keytab here. Even though we're using cursors, the file + * handle is stored in the krb5_keytab structure, and it gets + * overwritten when the verify_init_creds() call below creates its own + * cursor, creating a leak. */ + kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor); + if (kerr != 0) { + DEBUG(1, ("krb5_kt_end_seq_get failed, not verifying TGT.\n")); + goto done; + } + + /* check if we got any errors from krb5_kt_next_entry */ + if (kt_err != 0 && kt_err != KRB5_KT_END) { + DEBUG(1, ("error reading keytab [%s], not verifying TGT.\n", + kr->keytab)); + goto done; + } + + /* Get the principal to which the key belongs, for logging purposes. */ + principal = NULL; + kerr = krb5_unparse_name(kr->ctx, entry.principal, &principal); + if (kerr != 0) { + DEBUG(1, ("internal error parsing principal name, " + "not verifying TGT.\n")); + goto done; + } + + + krb5_verify_init_creds_opt_init(&opt); + kerr = krb5_verify_init_creds(kr->ctx, kr->creds, entry.principal, keytab, + NULL, &opt); + + if (kerr == 0) { + DEBUG(5, ("TGT verified using key for [%s].\n", principal)); + } else { + DEBUG(1 ,("TGT failed verification using key for [%s].\n", principal)); + } + +done: + if (krb5_kt_close(kr->ctx, keytab) != 0) { + DEBUG(1, ("krb5_kt_close failed")); + } + if (krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) { + DEBUG(1, ("Failed to free keytab entry.\n")); + } + if (principal != NULL) { + sss_krb5_free_unparsed_name(kr->ctx, principal); + } + + return kerr; + +} + static krb5_error_code get_and_save_tgt(struct krb5_req *kr, char *password) { krb5_error_code kerr = 0; + int ret; int fd = -1; size_t ccname_len = 0; size_t offset = 0; @@ -209,6 +305,25 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, return kerr; } + if (kr->validate) { + kerr = validate_tgt(kr); + if (kerr != 0) { + KRB5_DEBUG(1, kerr); + return kerr; + } + + /* We drop root privileges which were needed to read the keytab file + * for the validation validation of the credentials here to run the + * ccache I/O operations with user privileges. */ + ret = become_user(kr->pd->pw_uid, kr->pd->gr_gid); + if (ret != EOK) { + DEBUG(1, ("become_user failed.\n")); + return ret; + } + } else { + DEBUG(9, ("TGT validation is disabled.\n")); + } + if (kr->ccname[0] == '/' || strncmp(kr->ccname, "FILE:", 5) == 0) { offset = 0; if (kr->ccname[0] == 'F') { @@ -467,7 +582,7 @@ uint8_t *copy_buffer_and_add_zero(TALLOC_CTX *mem_ctx, const uint8_t *src, size_ } static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd, - char **ccname) + char **ccname, char **keytab, uint32_t *validate) { size_t p = 0; uint32_t *len; @@ -479,6 +594,21 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd, if ((p + sizeof(uint32_t)) > size) return EINVAL; len = ((uint32_t *)(buf+p)); + pd->pw_uid = *len; + p += sizeof(uint32_t); + + if ((p + sizeof(uint32_t)) > size) return EINVAL; + len = ((uint32_t *)(buf+p)); + pd->gr_gid = *len; + p += sizeof(uint32_t); + + if ((p + sizeof(uint32_t)) > size) return EINVAL; + len = ((uint32_t *)(buf+p)); + *validate = *len; + p += sizeof(uint32_t); + + if ((p + sizeof(uint32_t)) > size) return EINVAL; + len = ((uint32_t *)(buf+p)); p += sizeof(uint32_t); if ((p + *len ) > size) return EINVAL; @@ -501,6 +631,16 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd, len = ((uint32_t *)(buf+p)); p += sizeof(uint32_t); + if ((p + *len ) > size) return EINVAL; + *keytab = (char *) copy_buffer_and_add_zero(pd, buf+p, + sizeof(char) * (*len)); + if (*keytab == NULL) return ENOMEM; + p += *len; + + if ((p + sizeof(uint32_t)) > size) return EINVAL; + len = ((uint32_t *)(buf+p)); + p += sizeof(uint32_t); + if ((p + *len) > size) return EINVAL; pd->authtok = copy_buffer_and_add_zero(pd, buf+p, sizeof(char) * (*len)); if (pd->authtok == NULL) return ENOMEM; @@ -667,6 +807,8 @@ int main(int argc, const char *argv[]) struct pam_data *pd = NULL; struct krb5_req *kr = NULL; char *ccname; + char *keytab; + uint32_t validate; int opt; poptContext pc; int debug_fd = -1; @@ -738,7 +880,7 @@ int main(int argc, const char *argv[]) } close(STDIN_FILENO); - ret = unpack_buffer(buf, len, pd, &ccname); + ret = unpack_buffer(buf, len, pd, &ccname, &keytab, &validate); if (ret != EOK) { DEBUG(1, ("unpack_buffer failed.\n")); goto fail; @@ -750,6 +892,8 @@ int main(int argc, const char *argv[]) goto fail; } kr->ccname = ccname; + kr->keytab = keytab; + kr->validate = (validate == 0) ? false : true; ret = kr->child_req(STDOUT_FILENO, kr); if (ret != EOK) { diff --git a/server/providers/krb5/krb5_common.c b/server/providers/krb5/krb5_common.c index de069cd2..30878de3 100644 --- a/server/providers/krb5/krb5_common.c +++ b/server/providers/krb5/krb5_common.c @@ -35,6 +35,8 @@ struct dp_option default_krb5_opts[] = { { "krb5_ccname_template", DP_OPT_STRING, { "FILE:%d/krb5cc_%U_XXXXXX" }, NULL_STRING}, { "krb5_changepw_principal", DP_OPT_STRING, { "kadmin/changepw" }, NULL_STRING }, { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER }, + { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING }, + { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE } }; errno_t check_and_export_options(struct dp_option *opts, diff --git a/server/providers/krb5/krb5_common.h b/server/providers/krb5/krb5_common.h index 60f6a82f..cb60f425 100644 --- a/server/providers/krb5/krb5_common.h +++ b/server/providers/krb5/krb5_common.h @@ -44,6 +44,8 @@ enum krb5_opts { KRB5_CCNAME_TMPL, KRB5_CHANGEPW_PRINC, KRB5_AUTH_TIMEOUT, + KRB5_KEYTAB, + KRB5_VALIDATE, KRB5_OPTS }; diff --git a/server/providers/krb5/krb5_utils.h b/server/providers/krb5/krb5_utils.h index dec6f7f2..7637041a 100644 --- a/server/providers/krb5/krb5_utils.h +++ b/server/providers/krb5/krb5_utils.h @@ -34,4 +34,6 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr, const char *template); +errno_t become_user(uid_t uid, gid_t gid); + #endif /* __KRB5_UTILS_H__ */ |