From 3de9e4ec7a182b1fcaa6d0e2486d21fab3a7cbf3 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Dec 18 2009 14:43:27 +0000 Subject: Do not overwrite valid TGTs when offline --- diff --git a/server/Makefile.am b/server/Makefile.am index a7fd8c4..9d17ee7 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -618,7 +618,8 @@ libsss_krb5_la_SOURCES = \ providers/krb5/krb5_become_user.c \ providers/krb5/krb5_auth.c \ providers/krb5/krb5_common.c \ - providers/krb5/krb5_init.c + providers/krb5/krb5_init.c \ + util/sss_krb5.c libsss_krb5_la_CFLAGS = \ $(AM_CFLAGS) \ $(DHASH_CFLAGS) diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c index 7301841..6e63c30 100644 --- a/server/providers/krb5/krb5_auth.c +++ b/server/providers/krb5/krb5_auth.c @@ -113,7 +113,8 @@ done: return ret; } -static errno_t check_if_ccache_file_is_used(uid_t uid, const char **ccname) +static errno_t check_if_ccache_file_is_used(uid_t uid, const char *ccname, + bool *result) { int ret; size_t offset = 0; @@ -121,15 +122,17 @@ static errno_t check_if_ccache_file_is_used(uid_t uid, const char **ccname) const char *filename; bool active; - if (ccname == NULL || *ccname == NULL) { + *result = false; + + if (ccname == NULL || *ccname == '\0') { return EINVAL; } - if (strncmp(*ccname, "FILE:", 5) == 0) { + if (strncmp(ccname, "FILE:", 5) == 0) { offset = 5; } - filename = *ccname + offset; + filename = ccname + offset; if (filename[0] != '/') { DEBUG(1, ("Only absolute path names are allowed")); @@ -142,7 +145,6 @@ static errno_t check_if_ccache_file_is_used(uid_t uid, const char **ccname) DEBUG(1, ("stat failed [%d][%s].\n", errno, strerror(errno))); return errno; } else if (ret == -1 && errno == ENOENT) { - *ccname = NULL; return EOK; } @@ -165,17 +167,11 @@ static errno_t check_if_ccache_file_is_used(uid_t uid, const char **ccname) } if (!active) { - DEBUG(5, ("User [%d] is not active, deleting old ccache file [%s].\n", - uid, filename)); - ret = unlink(filename); - if (ret == -1) { - DEBUG(1, ("unlink failed [%d][%s].\n", errno, strerror(errno))); - return errno; - } - *ccname = NULL; + DEBUG(5, ("User [%d] is not active\n", uid)); } else { DEBUG(9, ("User [%d] is still active, reusing ccache file [%s].\n", uid, filename)); + *result = true; } return EOK; } @@ -734,12 +730,13 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) struct krb5_ctx *krb5_ctx; struct krb5child_req *kr = NULL; struct tevent_req *req; + krb5_error_code kerr; int ret; struct pam_data *pd; int pam_status=PAM_SYSTEM_ERR; int dp_err = DP_ERR_FATAL; const char *ccache_file = NULL; - const char *dummy; + const char *realm; ret = krb5_setup(be_req, &kr); if (ret != EOK) { @@ -755,6 +752,12 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) goto failed; } + realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM); + if (realm == NULL) { + DEBUG(1, ("Missing Kerberos realm.\n")); + goto failed; + } + switch (res->count) { case 0: DEBUG(5, ("No attributes for user [%s] found.\n", pd->user)); @@ -765,19 +768,12 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) pd->upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL); if (pd->upn == NULL) { /* NOTE: this is a hack, works only in some environments */ - dummy = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM); - if (dummy != NULL) { - pd->upn = talloc_asprintf(be_req, "%s@%s", pd->user, dummy); - if (pd->upn == NULL) { - DEBUG(1, ("failed to build simple upn.\n")); - } - DEBUG(9, ("Using simple UPN [%s].\n", pd->upn)); + pd->upn = talloc_asprintf(be_req, "%s@%s", pd->user, realm); + if (pd->upn == NULL) { + DEBUG(1, ("failed to build simple upn.\n")); + goto failed; } - } - - if (pd->upn == NULL) { - DEBUG(1, ("Cannot set UPN.\n")); - goto failed; + DEBUG(9, ("Using simple UPN [%s].\n", pd->upn)); } kr->homedir = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_HOMEDIR, @@ -790,31 +786,28 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) SYSDB_CCACHE_FILE, NULL); if (ccache_file != NULL) { - ret = check_if_ccache_file_is_used(pd->pw_uid, &ccache_file); + ret = check_if_ccache_file_is_used(pd->pw_uid, ccache_file, + &kr->active_ccache_present); if (ret != EOK) { DEBUG(1, ("check_if_ccache_file_is_used failed.\n")); goto failed; } - } - if (ccache_file == NULL) { - kr->active_ccache_present = false; - DEBUG(4, ("No active ccache file for user [%s] found.\n", - pd->user)); - ccache_file = expand_ccname_template(kr, kr, - dp_opt_get_cstring(kr->krb5_ctx->opts, - KRB5_CCNAME_TMPL) - ); - if (ccache_file == NULL) { - DEBUG(1, ("expand_ccname_template failed.\n")); + kerr = check_for_valid_tgt(ccache_file, realm, pd->upn, + &kr->valid_tgt_present); + if (kerr != 0) { + DEBUG(1, ("check_for_valid_tgt failed.\n")); goto failed; } } else { - kr->active_ccache_present = true; + kr->active_ccache_present = false; + kr->valid_tgt_present = false; + DEBUG(4, ("No ccache file for user [%s] found.\n", pd->user)); } - DEBUG(9, ("Ccache_file is [%s] and %s.\n", ccache_file, - kr->active_ccache_present ? "will be kept/renewed" : - "will be generated")); + DEBUG(9, ("Ccache_file is [%s] and is %s active and TGT is %s valid.\n", + ccache_file ? ccache_file : "not set", + kr->active_ccache_present ? "" : "not", + kr->valid_tgt_present ? "" : "not")); kr->ccname = ccache_file; break; @@ -852,6 +845,8 @@ static void krb5_resolve_done(struct tevent_req *req) int dp_err = DP_ERR_FATAL; struct pam_data *pd = kr->pd; struct be_req *be_req = kr->req; + char *msg; + size_t offset = 0; ret = be_resolve_server_recv(req, &kr->srv); talloc_zfree(req); @@ -864,9 +859,59 @@ static void krb5_resolve_done(struct tevent_req *req) kr->is_offline = true; } + if (kr->ccname == NULL || + (be_is_offline(be_req->be_ctx) && !kr->active_ccache_present && + !kr->valid_tgt_present) || + (!be_is_offline(be_req->be_ctx) && !kr->active_ccache_present)) { + DEBUG(9, ("Recreating ccache file.\n")); + if (kr->ccname != NULL) { + if (strncmp(kr->ccname, "FILE:", 5) == 0) { + offset = 5; + } + if (kr->ccname[offset] != '/') { + DEBUG(1, ("Ccache file name [%s] is not an absolute path.\n", + kr->ccname + offset)); + goto done; + } + ret = unlink(kr->ccname + offset); + if (ret == -1 && errno != ENOENT) { + DEBUG(1, ("unlink [%s] failed [%d][%s].\n", kr->ccname, + errno, strerror(errno))); + goto done; + } + } + kr->ccname = expand_ccname_template(kr, kr, + dp_opt_get_cstring(kr->krb5_ctx->opts, + KRB5_CCNAME_TMPL) + ); + if (kr->ccname == NULL) { + DEBUG(1, ("expand_ccname_template failed.\n")); + goto done; + } + } + if (be_is_offline(be_req->be_ctx)) { DEBUG(9, ("Preparing for offline operation.\n")); kr->is_offline = true; + + if (kr->valid_tgt_present) { + DEBUG(9, ("Valid TGT available, nothing to do.\n")); + msg = talloc_asprintf(pd, "%s=%s", CCACHE_ENV_NAME, kr->ccname); + if (msg == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + goto done; + } + + ret = pam_add_response(pd, PAM_ENV_ITEM, strlen(msg) + 1, + (uint8_t *) msg); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + + pam_status = PAM_SUCCESS; + dp_err = DP_ERR_OFFLINE; + goto done; + } memset(pd->authtok, 0, pd->authtok_size); pd->authtok_size = 0; diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h index f222c7b..a011af8 100644 --- a/server/providers/krb5/krb5_auth.h +++ b/server/providers/krb5/krb5_auth.h @@ -51,6 +51,7 @@ struct krb5child_req { bool is_offline; struct fo_server *srv; bool active_ccache_present; + bool valid_tgt_present; }; struct fo_service; diff --git a/server/util/sss_krb5.c b/server/util/sss_krb5.c index e96e1ba..0bc25df 100644 --- a/server/util/sss_krb5.c +++ b/server/util/sss_krb5.c @@ -19,9 +19,11 @@ */ #include #include +#include #include "config.h" +#include "util/util.h" #include "util/sss_krb5.h" @@ -102,3 +104,93 @@ void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name) } #endif } + + +krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm, + const char *client_princ_str, bool *result) +{ + krb5_context context = NULL; + krb5_ccache ccache = NULL; + krb5_error_code krberr; + TALLOC_CTX *tmp_ctx = NULL; + krb5_creds mcred; + krb5_creds cred; + char *server_name = NULL; + krb5_principal client_principal = NULL; + krb5_principal server_principal = NULL; + + *result = false; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(1, ("talloc_new failed.\n")); + return ENOMEM; + } + + krberr = krb5_init_context(&context); + if (krberr) { + DEBUG(1, ("Failed to init kerberos context\n")); + goto done; + } + + krberr = krb5_cc_resolve(context, ccname, &ccache); + if (krberr != 0) { + DEBUG(1, ("krb5_cc_resolve failed.\n")); + goto done; + } + + server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm); + if (server_name == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + goto done; + } + + krberr = krb5_parse_name(context, server_name, &server_principal); + if (krberr != 0) { + DEBUG(1, ("krb5_parse_name failed.\n")); + goto done; + } + + krberr = krb5_parse_name(context, client_princ_str, &client_principal); + if (krberr != 0) { + DEBUG(1, ("krb5_parse_name failed.\n")); + goto done; + } + + memset(&mcred, 0, sizeof(mcred)); + memset(&cred, 0, sizeof(mcred)); + mcred.client = client_principal; + mcred.server = server_principal; + + krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred); + if (krberr != 0) { + DEBUG(1, ("krb5_cc_retrieve_cred failed.\n")); + krberr = 0; + goto done; + } + + DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime)); + + if (cred.times.endtime > time(NULL)) { + DEBUG(3, ("TGT is valid.\n")); + *result = true; + } + krb5_free_cred_contents(context, &cred); + + krberr = 0; + +done: + if (client_principal != NULL) { + krb5_free_principal(context, client_principal); + } + if (server_principal != NULL) { + krb5_free_principal(context, server_principal); + } + if (ccache != NULL) { + krb5_cc_close(context, ccache); + } + if (context != NULL) krb5_free_context(context); + talloc_free(tmp_ctx); + return krberr; +} + diff --git a/server/util/sss_krb5.h b/server/util/sss_krb5.h index 342196d..60994e1 100644 --- a/server/util/sss_krb5.h +++ b/server/util/sss_krb5.h @@ -44,4 +44,7 @@ void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context, krb5_get_init_creds_opt *opt); void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name); + +krb5_error_code check_for_valid_tgt(const char *ccname, const char *realm, + const char *client_princ_str, bool *result); #endif /* __SSS_KRB5_H__ */