summaryrefslogtreecommitdiff
path: root/server/providers/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'server/providers/krb5')
-rw-r--r--server/providers/krb5/krb5_auth.c74
-rw-r--r--server/providers/krb5/krb5_become_user.c61
-rw-r--r--server/providers/krb5/krb5_child.c148
-rw-r--r--server/providers/krb5/krb5_common.c2
-rw-r--r--server/providers/krb5/krb5_common.h2
-rw-r--r--server/providers/krb5/krb5_utils.h2
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__ */