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=50773b1cdd1c215d5ec32c9b58d9267e578c9741;hb=289ad31e013029c924c2777b4d3c0875b87db042;hpb=68f4b69f3b8c627d37f6d40c209702fb4f266a2e diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 50773b1..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 */ @@ -131,10 +134,12 @@ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; /*==========================*/ static int select_krb5_ccache(const struct dirent *d); -static int gssd_find_existing_krb5_ccache(uid_t uid, 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); /* * Called from the scandir function to weed out potential krb5 @@ -159,18 +164,17 @@ select_krb5_ccache(const struct dirent *d) } /* - * Look in the ccachedir for files that look like they + * Look in directory "dirname" for files that look like they * are Kerberos Credential Cache files for a given UID. Return * non-zero and the dirent pointer for the entry most likely to be * 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, struct dirent **d) +gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) { struct dirent **namelist; int n; @@ -178,20 +182,29 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d) int found = 0; struct dirent *best_match_dir = NULL; struct stat best_match_stat, tmp_stat; + char buf[1030]; + char *princname = NULL; + char *realm = NULL; + int score, best_match_score = 0, err = -EACCES; memset(&best_match_stat, 0, sizeof(best_match_stat)); *d = NULL; - n = scandir(ccachedir, &namelist, select_krb5_ccache, 0); + n = scandir(dirname, &namelist, select_krb5_ccache, 0); if (n < 0) { - perror("scandir looking for krb5 credentials caches"); + printerr(1, "Error doing scandir on directory '%s': %s\n", + dirname, strerror(errno)); } else if (n > 0) { char statname[1024]; for (i = 0; i < n; i++) { - printerr(3, "CC file '%s' being considered\n", - namelist[i]->d_name); snprintf(statname, sizeof(statname), - "%s/%s", ccachedir, namelist[i]->d_name); + "%s/%s", dirname, namelist[i]->d_name); + printerr(3, "CC file '%s' being considered, " + "with preferred realm '%s'\n", + statname, preferred_realm ? + preferred_realm : ""); + snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, + namelist[i]->d_name); if (lstat(statname, &tmp_stat)) { printerr(0, "Error doing stat on file '%s'\n", statname); @@ -200,20 +213,34 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d) } /* Only pick caches owned by the user (uid) */ if (tmp_stat.st_uid != uid) { - printerr(3, "'%s' owned by %u, not %u\n", + printerr(3, "CC file '%s' owned by %u, not %u\n", statname, tmp_stat.st_uid, uid); free(namelist[i]); continue; } if (!S_ISREG(tmp_stat.st_mode)) { - printerr(3, "'%s' is not a regular file\n", + printerr(3, "CC file '%s' is not a regular file\n", + statname); + free(namelist[i]); + continue; + } + if (!query_krb5_ccache(buf, &princname, &realm)) { + printerr(3, "CC file '%s' is expired or corrupt\n", statname); free(namelist[i]); + err = -EKEYEXPIRED; continue; } - printerr(3, "CC file '%s' matches owner check and has " - "mtime of %u\n", - namelist[i]->d_name, tmp_stat.st_mtime); + + score = 0; + if (preferred_realm && + strcmp(realm, preferred_realm) == 0) + score++; + + printerr(3, "CC file '%s'(%s@%s) passed all checks and" + " has mtime of %u\n", + statname, princname, realm, + tmp_stat.st_mtime); /* * if more than one match is found, return the most * recent (the one with the latest mtime), and @@ -222,38 +249,47 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d) if (!found) { best_match_dir = namelist[i]; best_match_stat = tmp_stat; + best_match_score = score; found++; } else { /* - * If the current match has an mtime later + * If current score is higher than best match + * score, we use the current match. Otherwise, + * if the current match has an mtime later * than the one we are looking at, then use * the current match. Otherwise, we still * have the best match. */ - if (tmp_stat.st_mtime > - best_match_stat.st_mtime) { + if (best_match_score < score || + (best_match_score == score && + tmp_stat.st_mtime > + best_match_stat.st_mtime)) { free(best_match_dir); best_match_dir = namelist[i]; best_match_stat = tmp_stat; + best_match_score = score; } else { free(namelist[i]); } - printerr(3, "CC file '%s' is our " + printerr(3, "CC file '%s/%s' is our " "current best match " "with mtime of %u\n", - best_match_dir->d_name, + dirname, best_match_dir->d_name, best_match_stat.st_mtime); } + free(princname); + free(realm); } free(namelist); } - if (found) - { + if (found) { *d = best_match_dir; + return 0; } - return found; + + return err; } @@ -277,7 +313,9 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) u_int maj_stat, min_stat; gss_cred_id_t credh; gss_OID_set_desc desired_mechs; - krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC }; + krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); /* We only care about getting a krb5 cred */ @@ -289,8 +327,9 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) &credh, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_acquire_cred", - maj_stat, min_stat, &krb5oid); + if (get_verbosity() > 0) + pgsserr("gss_acquire_cred", + maj_stat, min_stat, &krb5oid); return -1; } @@ -321,9 +360,15 @@ 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; +#else krb5_get_init_creds_opt options; +#endif + krb5_get_init_creds_opt *opts; krb5_creds my_creds; krb5_ccache ccache = NULL; char kt_name[BUFSIZ]; @@ -332,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; @@ -351,19 +397,40 @@ gssd_get_single_krb5_cred(krb5_context context, if ((krb5_unparse_name(context, ple->princ, &pname))) pname = NULL; +#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + code = krb5_get_init_creds_opt_alloc(context, &init_opts); + if (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)) + printerr(1, "WARNING: Unable to set option for addressless " + "tickets. May have problems behind a NAT.\n"); +#ifdef TEST_SHORT_LIFETIME + /* set a short lifetime (for debugging only!) */ + printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); + krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60); +#endif + opts = init_opts; + +#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */ + krb5_get_init_creds_opt_init(&options); krb5_get_init_creds_opt_set_address_list(&options, NULL); - #ifdef TEST_SHORT_LIFETIME /* set a short lifetime (for debugging only!) */ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); krb5_get_init_creds_opt_set_tkt_life(&options, 5*60); #endif + opts = &options; +#endif + if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ, - kt, 0, NULL, &options))) { - printerr(0, "WARNING: %s while getting initial ticket for " - "principal '%s' using keytab '%s'\n", - error_message(code), + 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", k5err, pname ? pname : "", kt_name); goto out; } @@ -378,7 +445,7 @@ gssd_get_single_krb5_cred(krb5_context context, cache_type = "FILE"; snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s", cache_type, - GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX, + ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); ple->endtime = my_creds.times.endtime; if (ple->ccname != NULL) @@ -391,18 +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", - error_message(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", error_message(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", - error_message(code), cc_name); + k5err, cc_name); goto out; } @@ -410,11 +480,16 @@ gssd_get_single_krb5_cred(krb5_context context, printerr(2, "Successfully obtained machine credentials for " "principal '%s' stored in ccache '%s'\n", pname, cc_name); out: +#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + if (init_opts) + krb5_get_init_creds_opt_free(context, init_opts); +#endif if (pname) k5_free_unparsed_name(context, pname); if (ccache) krb5_cc_close(context, ccache); krb5_free_cred_contents(context, &my_creds); + free(k5err); return (code); } @@ -566,7 +641,7 @@ get_full_hostname(const char *inhost, char *outhost, int outhostlen) /* Get full target hostname */ retval = getaddrinfo(inhost, NULL, &hints, &addrs); if (retval) { - printerr(0, "%s while getting full hostname for '%s'\n", + printerr(1, "%s while getting full hostname for '%s'\n", gai_strerror(retval), inhost); goto out; } @@ -638,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; @@ -651,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", - error_message(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", - error_message(code), kt_name); + "for keytab '%s'\n", k5err, kt_name); retval = code; goto out; } @@ -667,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", - error_message(code)); + k5err); k5_free_kt_entry(context, kte); continue; } @@ -703,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", - error_message(code), kt_name); + "keytab '%s'\n", k5err, kt_name); } retval = 0; out: + free(k5err); return retval; } @@ -720,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; @@ -742,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", - error_message(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)); @@ -753,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", - error_message(code)); + k5err = gssd_k5_err_msg(context, code); + printerr(1, "%s while getting default realm name\n", k5err); goto out; } @@ -766,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", - error_message(code), targethostname); + k5err, targethostname); retval = code; goto out; } @@ -798,17 +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", error_message(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", error_message(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", @@ -843,9 +924,98 @@ out: k5_free_default_realm(context, default_realm); if (realmnames) krb5_free_host_realm(context, realmnames); + free(k5err); return retval; } + +static inline int data_is_equal(krb5_data d1, krb5_data d2) +{ + return (d1.length == d2.length + && memcmp(d1.data, d2.data, d1.length) == 0); +} + +static int +check_for_tgt(krb5_context context, krb5_ccache ccache, + krb5_principal principal) +{ + krb5_error_code ret; + krb5_creds creds; + krb5_cc_cursor cur; + int found = 0; + + ret = krb5_cc_start_seq_get(context, ccache, &cur); + if (ret) + return 0; + + while (!found && + (ret = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) { + if (creds.server->length == 2 && + data_is_equal(creds.server->realm, + principal->realm) && + creds.server->data[0].length == 6 && + memcmp(creds.server->data[0].data, + "krbtgt", 6) == 0 && + data_is_equal(creds.server->data[1], + principal->realm) && + creds.times.endtime > time(NULL)) + found = 1; + krb5_free_cred_contents(context, &creds); + } + krb5_cc_end_seq_get(context, ccache, &cur); + + return found; +} + +static int +query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm) +{ + krb5_error_code ret; + krb5_context context; + krb5_ccache ccache; + krb5_principal principal; + int found = 0; + char *str = NULL; + char *princstring; + + ret = krb5_init_context(&context); + if (ret) + return 0; + + if(!cred_cache || krb5_cc_resolve(context, cred_cache, &ccache)) + goto err_cache; + + if (krb5_cc_set_flags(context, ccache, 0)) + goto err_princ; + + ret = krb5_cc_get_principal(context, ccache, &principal); + if (ret) + goto err_princ; + + found = check_for_tgt(context, ccache, principal); + if (found) { + ret = krb5_unparse_name(context, principal, &princstring); + if (ret == 0) { + if ((str = strchr(princstring, '@')) != NULL) { + *str = '\0'; + *ret_princname = strdup(princstring); + *ret_realm = strdup(str+1); + } + k5_free_unparsed_name(context, princstring); + } else { + found = 0; + } + } + krb5_free_principal(context, principal); +err_princ: + krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); + krb5_cc_close(context, ccache); +err_cache: + krb5_free_context(context); + return found; +} + /*==========================*/ /*=== External routines ===*/ /*==========================*/ @@ -855,29 +1025,29 @@ out: * given only a UID. We really need more information, but we * do the best we can. * - * Returns: - * void + * Returns 0 if a ccache was found, and a non-zero error code otherwise. */ -void -gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername) +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, &d)) { - snprintf(buf, sizeof(buf), "FILE:%s/%s", - ccachedir, d->d_name); - free(d); - } - else - snprintf(buf, sizeof(buf), "FILE:%s/%s%u", - ccachedir, GSSD_DEFAULT_CRED_PREFIX, uid); + 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 err; } /* @@ -926,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) { @@ -980,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", - error_message(code)); + k5err = gssd_k5_err_msg(NULL, code); + printerr(0, "ERROR: %s while initializing krb5\n", k5err); goto out; } @@ -992,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", - error_message(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", - error_message(code), ple->ccname); + "cache '%s'\n", k5err, ple->ccname); } } out: + free(k5err); krb5_free_context(context); } @@ -1013,34 +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__, error_message(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__, error_message(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", @@ -1065,11 +1252,55 @@ 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 + */ +char * +gssd_k5_err_msg(krb5_context context, krb5_error_code code) +{ + const char *origmsg; + char *msg = NULL; + +#if HAVE_KRB5_GET_ERROR_MESSAGE + 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 strdup(error_message(code)); +#else + if (context != NULL) + return strdup(krb5_get_err_text(context, code)); + else + return strdup(error_message(code)); +#endif +} + +/* + * Return default Kerberos realm + */ +void +gssd_k5_get_default_realm(char **def_realm) +{ + krb5_context context; + + if (krb5_init_context(&context)) + return; + + krb5_get_default_realm(context, def_realm); + + krb5_free_context(context); +}