*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
-#include "config.h"
+
#include <sys/param.h>
#include <rpc/rpc.h>
#include <sys/stat.h>
#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 */
/*==========================*/
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
}
/*
- * 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;
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 : "<none selected>");
+ 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);
}
/* 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;
}
- printerr(3, "CC file '%s' matches owner check and has "
- "mtime of %u\n",
- namelist[i]->d_name, tmp_stat.st_mtime);
+ if (uid == 0 && !root_uses_machine_creds &&
+ strstr(namelist[i]->d_name, "_machine_")) {
+ printerr(3, "CC file '%s' not available to root\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;
+ }
+
+ 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
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;
-}
-
-
-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
-/*
- * this routine obtains a credentials handle via gss_acquire_cred()
- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
- * types negotiated.
- *
- * XXX Should call some function to determine the enctypes supported
- * by the kernel. (Only need to do that once!)
- *
- * Returns:
- * 0 => all went well
- * -1 => there was an error
- */
-
-int
-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 };
- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
-
- /* We only care about getting a krb5 cred */
- desired_mechs.count = 1;
- desired_mechs.elements = &krb5oid;
-
- maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
- &desired_mechs, GSS_C_INITIATE,
- &credh, NULL, NULL);
-
- if (maj_stat != GSS_S_COMPLETE) {
- pgsserr("gss_acquire_cred",
- maj_stat, min_stat, &krb5oid);
- return -1;
- }
-
- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
- num_enctypes, &enctypes);
- if (maj_stat != GSS_S_COMPLETE) {
- pgsserr("gss_set_allowable_enctypes",
- maj_stat, min_stat, &krb5oid);
- gss_release_cred(&min_stat, &credh);
- return -1;
- }
- sec->cred = credh;
- return 0;
+ return err;
}
-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
/*
* Obtain credentials via a key in the keytab given
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];
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;
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",
- gssd_k5_err_msg(context, 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 : "<unparsable>", kt_name);
goto out;
}
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)
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;
}
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);
}
/* 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;
}
* and has *any* instance (hostname), return 1.
* Otherwise return 0, indicating no match.
*/
+#ifdef HAVE_KRB5
static int
-realm_and_service_match(krb5_context context, krb5_principal p,
- const char *realm, const char *service)
+realm_and_service_match(krb5_principal p, const char *realm, const char *service)
{
-#ifdef HAVE_KRB5
/* Must have two components */
if (p->length != 2)
return 0;
+
if ((strlen(realm) == p->realm.length)
&& (strncmp(realm, p->realm.data, p->realm.length) == 0)
&& (strlen(service) == p->data[0].length)
&& (strncmp(service, p->data[0].data, p->data[0].length) == 0))
return 1;
+
+ return 0;
+}
#else
+static int
+realm_and_service_match(krb5_context context, krb5_principal p,
+ const char *realm, const char *service)
+{
const char *name, *inst;
if (p->name.name_string.len != 2)
return 0;
+
name = krb5_principal_get_comp_string(context, p, 0);
inst = krb5_principal_get_comp_string(context, p, 1);
if (name == NULL || inst == NULL)
if ((strcmp(realm, p->realm) == 0)
&& (strcmp(service, name) == 0))
return 1;
-#endif
+
return 0;
}
+#endif
/*
* Search the given keytab file looking for an entry with the given
krb5_kt_cursor cursor;
krb5_error_code code;
struct gssd_k5_kt_princ *ple;
- int retval = -1;
+ int retval = -1, status;
char kt_name[BUFSIZ];
char *pname;
+ char *k5err = NULL;
if (found == NULL) {
retval = EINVAL;
* 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;
}
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;
}
printerr(4, "Processing keytab entry for principal '%s'\n",
pname);
/* Use the first matching keytab entry found */
- if ((realm_and_service_match(context, kte->principal, realm,
- service))) {
+#ifdef HAVE_KRB5
+ status = realm_and_service_match(kte->principal, realm, service);
+#else
+ status = realm_and_service_match(context, kte->principal, realm, service);
+#endif
+ if (status) {
printerr(4, "We WILL use this entry (%s)\n", pname);
ple = get_ple_by_princ(context, kte->principal);
/*
}
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;
}
*/
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;
/* 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));
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;
}
*/
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;
}
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",
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 ===*/
/*==========================*/
* 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;
}
/*
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) {
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;
}
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);
}
*/
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",
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
}
+
+/*
+ * 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);
+}
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * XXX Should call some function to determine the enctypes supported
+ * by the kernel. (Only need to do that once!)
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec)
+{
+ u_int maj_stat, min_stat;
+ gss_cred_id_t credh;
+ gss_OID_set_desc desired_mechs;
+ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+ extern int num_krb5_enctypes;
+ extern krb5_enctype *krb5_enctypes;
+
+ /* We only care about getting a krb5 cred */
+ desired_mechs.count = 1;
+ desired_mechs.elements = &krb5oid;
+
+ maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
+ &desired_mechs, GSS_C_INITIATE,
+ &credh, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_acquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ /*
+ * If we failed for any reason to produce global
+ * list of supported enctypes, use local default here.
+ */
+ if (krb5_enctypes == NULL)
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_enctypes, enctypes);
+ else
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_krb5_enctypes, krb5_enctypes);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ gss_release_cred(&min_stat, &credh);
+ return -1;
+ }
+ sec->cred = credh;
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */