X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fgssd%2Fkrb5_util.c;h=1295f5776eb78bc6e6154d8e80ca111fbbe8c3a2;hp=d4ee631b1a00be5549b99ca281d0ac47e91286a5;hb=289ad31e013029c924c2777b4d3c0875b87db042;hpb=09c7ad1cd9c5ca2fc46631a0057d47309abc8706 diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index d4ee631..1295f57 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -91,10 +91,14 @@ */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif -#include "config.h" + #include #include #include @@ -120,7 +124,6 @@ #include "gssd.h" #include "err_util.h" #include "gss_util.h" -#include "gss_oids.h" #include "krb5_util.h" /* Global list of principals/cache file names for machine credentials */ @@ -134,7 +137,7 @@ static int select_krb5_ccache(const struct dirent *d); static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d); static int gssd_get_single_krb5_cred(krb5_context context, - krb5_keytab kt, struct gssd_k5_kt_princ *ple); + krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); static int query_krb5_ccache(const char* cred_cache, char **ret_princname, char **ret_realm); @@ -167,9 +170,8 @@ select_krb5_ccache(const struct dirent *d) * what we want. Otherwise, return zero and no dirent pointer. * The caller is responsible for freeing the dirent if one is returned. * - * Returns: - * 0 => could not find an existing entry - * 1 => found an existing entry + * Returns 0 if a valid-looking entry was found and a non-zero error + * code otherwise. */ static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) @@ -183,7 +185,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) char buf[1030]; char *princname = NULL; char *realm = NULL; - int score, best_match_score = 0; + int score, best_match_score = 0, err = -EACCES; memset(&best_match_stat, 0, sizeof(best_match_stat)); *d = NULL; @@ -226,6 +228,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) printerr(3, "CC file '%s' is expired or corrupt\n", statname); free(namelist[i]); + err = -EKEYEXPIRED; continue; } @@ -281,11 +284,12 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) } free(namelist); } - if (found) - { + if (found) { *d = best_match_dir; + return 0; } - return found; + + return err; } @@ -356,7 +360,8 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, + int nocache) { #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS krb5_get_init_creds_opt *init_opts = NULL; @@ -372,10 +377,11 @@ gssd_get_single_krb5_cred(krb5_context context, time_t now = time(0); char *cache_type; char *pname = NULL; + char *k5err = NULL; memset(&my_creds, 0, sizeof(my_creds)); - if (ple->ccname && ple->endtime > now) { + if (ple->ccname && ple->endtime > now && !nocache) { printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", ple->ccname, ple->endtime); code = 0; @@ -394,8 +400,8 @@ gssd_get_single_krb5_cred(krb5_context context, #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS code = krb5_get_init_creds_opt_alloc(context, &init_opts); if (code) { - printerr(0, "ERROR: %s allocating gic options\n", - gssd_k5_err_msg(context, code)); + k5err = gssd_k5_err_msg(context, code); + printerr(0, "ERROR: %s allocating gic options\n", k5err); goto out; } if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1)) @@ -422,9 +428,9 @@ gssd_get_single_krb5_cred(krb5_context context, if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ, kt, 0, NULL, opts))) { + k5err = gssd_k5_err_msg(context, code); printerr(1, "WARNING: %s while getting initial ticket for " - "principal '%s' using keytab '%s'\n", - gssd_k5_err_msg(context, code), + "principal '%s' using keytab '%s'\n", k5err, pname ? pname : "", kt_name); goto out; } @@ -452,19 +458,21 @@ gssd_get_single_krb5_cred(krb5_context context, goto out; } if ((code = krb5_cc_resolve(context, cc_name, &ccache))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while opening credential cache '%s'\n", - gssd_k5_err_msg(context, code), cc_name); + k5err, cc_name); goto out; } if ((code = krb5_cc_initialize(context, ccache, ple->princ))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while initializing credential " - "cache '%s'\n", gssd_k5_err_msg(context, code), - cc_name); + "cache '%s'\n", k5err, cc_name); goto out; } if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while storing credentials in '%s'\n", - gssd_k5_err_msg(context, code), cc_name); + k5err, cc_name); goto out; } @@ -481,6 +489,7 @@ gssd_get_single_krb5_cred(krb5_context context, if (ccache) krb5_cc_close(context, ccache); krb5_free_cred_contents(context, &my_creds); + free(k5err); return (code); } @@ -704,6 +713,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, int retval = -1; char kt_name[BUFSIZ]; char *pname; + char *k5err = NULL; if (found == NULL) { retval = EINVAL; @@ -717,15 +727,15 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, * save info in the global principal list (gssd_k5_kt_princ_list). */ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) { - printerr(0, "ERROR: %s attempting to get keytab name\n", - gssd_k5_err_msg(context, code)); + k5err = gssd_k5_err_msg(context, code); + printerr(0, "ERROR: %s attempting to get keytab name\n", k5err); retval = code; goto out; } if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while beginning keytab scan " - "for keytab '%s'\n", - gssd_k5_err_msg(context, code), kt_name); + "for keytab '%s'\n", k5err, kt_name); retval = code; goto out; } @@ -733,9 +743,10 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) { if ((code = krb5_unparse_name(context, kte->principal, &pname))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: Skipping keytab entry because " "we failed to unparse principal name: %s\n", - gssd_k5_err_msg(context, code)); + k5err); k5_free_kt_entry(context, kte); continue; } @@ -769,13 +780,14 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, } if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while ending keytab scan for " - "keytab '%s'\n", - gssd_k5_err_msg(context, code), kt_name); + "keytab '%s'\n", k5err, kt_name); } retval = 0; out: + free(k5err); return retval; } @@ -786,15 +798,15 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, - krb5_keytab_entry *kte) + krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; - const char *svcnames[] = { "root", "nfs", "host", NULL }; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; int i, j, retval; char *default_realm = NULL; char *realm; + char *k5err = NULL; int tried_all = 0, tried_default = 0; krb5_principal princ; @@ -808,8 +820,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, /* Get full local hostname */ retval = gethostname(myhostname, sizeof(myhostname)); if (retval) { - printerr(1, "%s while getting local hostname\n", - gssd_k5_err_msg(context, retval)); + k5err = gssd_k5_err_msg(context, retval); + printerr(1, "%s while getting local hostname\n", k5err); goto out; } retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); @@ -819,8 +831,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, code = krb5_get_default_realm(context, &default_realm); if (code) { retval = code; - printerr(1, "%s while getting default realm name\n", - gssd_k5_err_msg(context, code)); + k5err = gssd_k5_err_msg(context, code); + printerr(1, "%s while getting default realm name\n", k5err); goto out; } @@ -832,8 +844,9 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, */ code = krb5_get_host_realm(context, targethostname, &realmnames); if (code) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n", - gssd_k5_err_msg(context, code), targethostname); + k5err, targethostname); retval = code; goto out; } @@ -864,19 +877,19 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, myhostname, NULL); if (code) { + k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while building principal for " - "'%s/%s@%s'\n", - gssd_k5_err_msg(context, code), - svcnames[j], myhostname, realm); + "'%s/%s@%s'\n", k5err, svcnames[j], + myhostname, realm); continue; } code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); krb5_free_principal(context, princ); if (code) { + k5err = gssd_k5_err_msg(context, code); printerr(3, "%s while getting keytab entry for " - "'%s/%s@%s'\n", - gssd_k5_err_msg(context, code), - svcnames[j], myhostname, realm); + "'%s/%s@%s'\n", k5err, svcnames[j], + myhostname, realm); } else { printerr(3, "Success getting keytab entry for " "'%s/%s@%s'\n", @@ -911,6 +924,7 @@ out: k5_free_default_realm(context, default_realm); if (realmnames) krb5_free_host_realm(context, realmnames); + free(k5err); return retval; } @@ -1011,29 +1025,29 @@ err_cache: * given only a UID. We really need more information, but we * do the best we can. * - * Returns: - * 0 => a ccache was found - * 1 => no ccache was found + * Returns 0 if a ccache was found, and a non-zero error code otherwise. */ int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname) { char buf[MAX_NETOBJ_SZ]; struct dirent *d; + int err; printerr(2, "getting credentials for client with uid %u for " "server %s\n", uid, servername); memset(buf, 0, sizeof(buf)); - if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) { - snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name); - free(d); - } - else - return 1; + err = gssd_find_existing_krb5_ccache(uid, dirname, &d); + if (err) + return err; + + snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name); + free(d); + printerr(2, "using %s as credentials cache for client with " "uid %u for server %s\n", buf, uid, servername); gssd_set_krb5_ccache_name(buf); - return 0; + return err; } /* @@ -1082,7 +1096,8 @@ gssd_get_krb5_machine_cred_list(char ***list) for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { if (ple->ccname) { /* Make sure cred is up-to-date before returning it */ - retval = gssd_refresh_krb5_machine_credential(NULL, ple); + retval = gssd_refresh_krb5_machine_credential(NULL, ple, + NULL); if (retval) continue; if (i + 1 > listsize) { @@ -1136,11 +1151,12 @@ gssd_destroy_krb5_machine_creds(void) krb5_error_code code = 0; krb5_ccache ccache; struct gssd_k5_kt_princ *ple; + char *k5err = NULL; code = krb5_init_context(&context); if (code) { - printerr(0, "ERROR: %s while initializing krb5\n", - gssd_k5_err_msg(NULL, code)); + k5err = gssd_k5_err_msg(NULL, code); + printerr(0, "ERROR: %s while initializing krb5\n", k5err); goto out; } @@ -1148,19 +1164,21 @@ gssd_destroy_krb5_machine_creds(void) if (!ple->ccname) continue; if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while resolving credential " - "cache '%s' for destruction\n", - gssd_k5_err_msg(context, code), ple->ccname); + "cache '%s' for destruction\n", k5err, + ple->ccname); continue; } if ((code = krb5_cc_destroy(context, ccache))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while destroying credential " - "cache '%s'\n", - gssd_k5_err_msg(context, code), ple->ccname); + "cache '%s'\n", k5err, ple->ccname); } } out: + free(k5err); krb5_free_context(context); } @@ -1169,35 +1187,47 @@ gssd_destroy_krb5_machine_creds(void) */ int gssd_refresh_krb5_machine_credential(char *hostname, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, + char *service) { krb5_error_code code = 0; krb5_context context; krb5_keytab kt = NULL;; int retval = 0; + char *k5err = NULL; + const char *svcnames[4] = { "root", "nfs", "host", NULL }; + /* + * If a specific service name was specified, use it. + * Otherwise, use the default list. + */ + if (service != NULL && strcmp(service, "*") != 0) { + svcnames[0] = service; + svcnames[1] = NULL; + } if (hostname == NULL && ple == NULL) return EINVAL; code = krb5_init_context(&context); if (code) { + k5err = gssd_k5_err_msg(NULL, code); printerr(0, "ERROR: %s: %s while initializing krb5 context\n", - __FUNCTION__, gssd_k5_err_msg(NULL, code)); + __func__, k5err); retval = code; goto out; } if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { + k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", - __FUNCTION__, gssd_k5_err_msg(context, code), - keytabfile); + __func__, k5err, keytabfile); goto out; } if (ple == NULL) { krb5_keytab_entry kte; - code = find_keytab_entry(context, kt, hostname, &kte); + code = find_keytab_entry(context, kt, hostname, &kte, svcnames); if (code) { printerr(0, "ERROR: %s: no usable keytab entry found " "in keytab %s for connection with host %s\n", @@ -1222,34 +1252,40 @@ gssd_refresh_krb5_machine_credential(char *hostname, goto out; } } - retval = gssd_get_single_krb5_cred(context, kt, ple); + retval = gssd_get_single_krb5_cred(context, kt, ple, 0); out: if (kt) krb5_kt_close(context, kt); krb5_free_context(context); + free(k5err); return retval; } /* * A common routine for getting the Kerberos error message */ -const char * +char * gssd_k5_err_msg(krb5_context context, krb5_error_code code) { - const char *msg = NULL; + const char *origmsg; + char *msg = NULL; + #if HAVE_KRB5_GET_ERROR_MESSAGE - if (context != NULL) - msg = krb5_get_error_message(context, code); + if (context != NULL) { + origmsg = krb5_get_error_message(context, code); + msg = strdup(origmsg); + krb5_free_error_message(context, origmsg); + } #endif if (msg != NULL) return msg; #if HAVE_KRB5 - return error_message(code); + return strdup(error_message(code)); #else if (context != NULL) - return krb5_get_err_text(context, code); + return strdup(krb5_get_err_text(context, code)); else - return error_message(code); + return strdup(error_message(code)); #endif }