2007-03-30 22:32:05

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 0/8] gssd cleanup and modified machine cred handling

These patches do some gssd code clean-up and change the way keytabs are
selected to obtain machine credentials.

1) adds missing newlines to some seldom printed error messages
2) fixes a memory leak in the error path of limit_krb5_enctypes()
3) adds some macros to clean up differences between MIT and Heimdal interfaces
4) adds the new functions that change the way keytabs are selected.
the new behavior is to search for particular keytab entries in a
specified order:

root/<fqdn>@<REALM>
nfs/<fqdn>@<REALM>
host/<fqdn>@<REALM>
root/<any-name>@<REALM>
nfs/<any-name>@<REALM>
host/<any-name>@<REALM>

5) actually use the new functions added in the previous patch
6) does some cleanup in gssd_get_single_krb5_cred() and improves
the debugging messages
7) removes the now obsolete routines
8) adds a debug message to indicate processing is beginning

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs


2007-03-30 22:32:05

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 1/8] Add missing newlines

From: Kevin Coffman <[email protected]>

Add missing newlines to error messages.

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/gssd_proc.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 3b190f2..eff740c 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -555,7 +555,7 @@ #endif
ai_hints.ai_protocol = IPPROTO_UDP;
} else {
printerr(0, "WARNING: unrecognized protocol, '%s', requested "
- "for connection to server %s for user with uid %d",
+ "for connection to server %s for user with uid %d\n",
clp->protocol, clp->servername, uid);
goto out_fail;
}
@@ -563,12 +563,12 @@ #endif
/* extract the service name from clp->servicename */
if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
printerr(0, "WARNING: servicename (%s) not formatted as "
- "expected with service@host", clp->servicename);
+ "expected with service@host\n", clp->servicename);
goto out_fail;
}
if ((at_sign - clp->servicename) >= sizeof(service)) {
printerr(0, "WARNING: service portion of servicename (%s) "
- "is too long!", clp->servicename);
+ "is too long!\n", clp->servicename);
goto out_fail;
}
strncpy(service, clp->servicename, at_sign - clp->servicename);
@@ -577,13 +577,13 @@ #endif
errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
if (errcode) {
printerr(0, "WARNING: Error from getaddrinfo for server "
- "'%s': %s", clp->servername, gai_strerror(errcode));
+ "'%s': %s\n", clp->servername, gai_strerror(errcode));
goto out_fail;
}

if (a == NULL) {
printerr(0, "WARNING: No address information found for "
- "connection to server %s for user with uid %d",
+ "connection to server %s for user with uid %d\n",
clp->servername, uid);
goto out_fail;
}
@@ -617,7 +617,7 @@ #endif
} else {
/* Shouldn't happen! */
printerr(0, "ERROR: requested protocol '%s', but "
- "got addrinfo with protocol %d",
+ "got addrinfo with protocol %d\n",
clp->protocol, a->ai_protocol);
goto out_fail;
}

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:08

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 2/8] Fix memory leak on error path of limit_krb5_enctypes()

From: Kevin Coffman <[email protected]>

Return credential on error path of limit_krb5_enctypes()

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/krb5_util.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index f1682b8..a0ee110 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -298,6 +298,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *
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;

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:14

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 4/8] Add new keytab handling functions for dealing with machine credentials

From: Kevin Coffman <[email protected]>

Add new functions that will be used in the next patch. The new behavior
is to search for particular keytab entries in a specified order:

root/<fqdn>@<REALM>
nfs/<fqdn>@<REALM>
host/<fqdn>@<REALM>
root/<any-name>@<REALM>
nfs/<any-name>@<REALM>
host/<any-name>@<REALM>

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/krb5_util.c | 466 ++++++++++++++++++++++++++++++++++++++++++++++++
utils/gssd/krb5_util.h | 2
2 files changed, 468 insertions(+), 0 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 7ef690b..20396e0 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -103,8 +103,11 @@ #include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <string.h>
#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <gssapi/gssapi.h>
@@ -135,6 +138,7 @@ static int gssd_have_realm_ple(void *rea
static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
char *kt_name);

+
/*
* Called from the scandir function to weed out potential krb5
* credentials cache files
@@ -585,6 +589,403 @@ #else
#endif
}

+/*
+ * Given a principal, find a matching ple structure
+ */
+static struct gssd_k5_kt_princ *
+find_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
+ if (krb5_principal_compare(context, ple->princ, princ))
+ return ple;
+ }
+ /* no match found */
+ return NULL;
+}
+
+/*
+ * Create, initialize, and add a new ple structure to the global list
+ */
+static struct gssd_k5_kt_princ *
+new_ple(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple = NULL, *p;
+ krb5_error_code code;
+ char *default_realm;
+ int is_default_realm = 0;
+
+ ple = malloc(sizeof(struct gssd_k5_kt_princ));
+ if (ple == NULL)
+ goto outerr;
+ memset(ple, 0, sizeof(*ple));
+
+#ifdef HAVE_KRB5
+ ple->realm = strndup(princ->realm.data,
+ princ->realm.length);
+#else
+ ple->realm = strdup(princ->realm);
+#endif
+ if (ple->realm == NULL)
+ goto outerr;
+ code = krb5_copy_principal(context, princ, &ple->princ);
+ if (code)
+ goto outerr;
+
+ /*
+ * Add new entry onto the list (if this is the default
+ * realm, always add to the front of the list)
+ */
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code == 0) {
+ if (strcmp(ple->realm, default_realm) == 0)
+ is_default_realm = 1;
+ k5_free_default_realm(context, default_realm);
+ }
+
+ if (is_default_realm) {
+ ple->next = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple;
+ } else {
+ p = gssd_k5_kt_princ_list;
+ while (p != NULL && p->next != NULL)
+ p = p->next;
+ if (p == NULL)
+ gssd_k5_kt_princ_list = ple;
+ else
+ p->next = ple;
+ }
+
+ return ple;
+outerr:
+ if (ple) {
+ if (ple->realm)
+ free(ple->realm);
+ free(ple);
+ }
+ return NULL;
+}
+
+/*
+ * Given a principal, find an existing ple structure, or create one
+ */
+static struct gssd_k5_kt_princ *
+get_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ /* Need to serialize list if we ever become multi-threaded! */
+
+ ple = find_ple_by_princ(context, princ);
+ if (ple == NULL) {
+ ple = new_ple(context, princ);
+ }
+
+ return ple;
+}
+
+/*
+ * Given a (possibly unqualified) hostname,
+ * return the fully qualified (lower-case!) hostname
+ */
+static int
+get_full_hostname(const char *inhost, char *outhost, int outhostlen)
+{
+ struct addrinfo *addrs = NULL;
+ struct addrinfo hints;
+ int retval;
+ char *c;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ /* Get full target hostname */
+ retval = getaddrinfo(inhost, NULL, &hints, &addrs);
+ if (retval) {
+ printerr(0, "%s while getting full hostname for '%s'\n",
+ gai_strerror(retval), inhost);
+ goto out;
+ }
+ strncpy(outhost, addrs->ai_canonname, outhostlen);
+ freeaddrinfo(addrs);
+ for (c = outhost; *c != '\0'; c++)
+ *c = tolower(*c);
+
+ printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
+ retval = 0;
+out:
+ return retval;
+}
+
+/*
+ * If principal matches the given realm and service name,
+ * and has *any* instance (hostname), return 1.
+ * Otherwise return 0, indicating no match.
+ */
+static int
+realm_and_service_match(krb5_context context, 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;
+#else
+ 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)
+ return 0;
+ if ((strcmp(realm, p->realm) == 0)
+ && (strcmp(service, name) == 0))
+ return 1;
+#endif
+ return 0;
+}
+
+/*
+ * Search the given keytab file looking for an entry with the given
+ * service name and realm, ignoring hostname (instance).
+ *
+ * Returns:
+ * 0 => No error
+ * non-zero => An error occurred
+ *
+ * If a keytab entry is found, "found" is set to one, and the keytab
+ * entry is returned in "kte". Otherwise, "found" is zero, and the
+ * value of "kte" is unpredictable.
+ */
+static int
+gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
+ const char *realm, const char *service,
+ int *found, krb5_keytab_entry *kte)
+{
+ krb5_kt_cursor cursor;
+ krb5_error_code code;
+ struct gssd_k5_kt_princ *ple;
+ int retval = -1;
+ char kt_name[BUFSIZ];
+ char *pname;
+
+ if (found == NULL) {
+ retval = EINVAL;
+ goto out;
+ }
+ *found = 0;
+
+ /*
+ * Look through each entry in the keytab file and determine
+ * if we might want to use it as machine credentials. If so,
+ * 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));
+ retval = code;
+ goto out;
+ }
+ if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+ printerr(0, "ERROR: %s while beginning keytab scan "
+ "for keytab '%s'\n",
+ error_message(code), 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))) {
+ printerr(0, "WARNING: Skipping keytab entry because "
+ "we failed to unparse principal name: %s\n",
+ error_message(code));
+ 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))) {
+ printerr(4, "We WILL use this entry (%s)\n", pname);
+ ple = get_ple_by_princ(context, kte->principal);
+ /*
+ * Return, don't free, keytab entry if
+ * we were successful!
+ */
+ if (ple == NULL) {
+ retval = ENOMEM;
+ k5_free_kt_entry(context, kte);
+ } else {
+ retval = 0;
+ *found = 1;
+ }
+ k5_free_unparsed_name(context, pname);
+ break;
+ }
+ else {
+ printerr(4, "We will NOT use this entry (%s)\n",
+ pname);
+ }
+ k5_free_unparsed_name(context, pname);
+ k5_free_kt_entry(context, kte);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+ printerr(0, "WARNING: %s while ending keytab scan for "
+ "keytab '%s'\n",
+ error_message(code), kt_name);
+ }
+
+ retval = 0;
+ out:
+ return retval;
+}
+
+/*
+ * Find a keytab entry to use for a given target hostname.
+ * Tries to find the most appropriate keytab to use given the
+ * name of the host we are trying to connect with.
+ */
+static int
+find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
+ krb5_keytab_entry *kte)
+{
+ 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;
+ int tried_all = 0, tried_default = 0;
+ krb5_principal princ;
+
+
+ /* Get full target hostname */
+ retval = get_full_hostname(hostname, targethostname,
+ sizeof(targethostname));
+ if (retval)
+ goto out;
+
+ /* Get full local hostname */
+ retval = gethostname(myhostname, sizeof(myhostname));
+ if (retval) {
+ printerr(1, "%s while getting local hostname\n",
+ error_message(retval));
+ goto out;
+ }
+ retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
+ if (retval)
+ goto out;
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code) {
+ retval = code;
+ printerr(1, "%s while getting default realm name\n",
+ error_message(code));
+ goto out;
+ }
+
+ /*
+ * Get the realm name(s) for the target hostname.
+ * In reality, this function currently only returns a
+ * single realm, but we code with the assumption that
+ * someday it may actually return a list.
+ */
+ code = krb5_get_host_realm(context, targethostname, &realmnames);
+ if (code) {
+ printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
+ error_message(code), targethostname);
+ retval = code;
+ goto out;
+ }
+
+ /*
+ * Try the "appropriate" realm first, and if nothing found for that
+ * realm, try the default realm (if it hasn't already been tried).
+ */
+ i = 0;
+ realm = realmnames[i];
+ while (1) {
+ if (realm == NULL) {
+ tried_all = 1;
+ if (!tried_default)
+ realm = default_realm;
+ }
+ if (tried_all && tried_default)
+ break;
+ if (strcmp(realm, default_realm) == 0)
+ tried_default = 1;
+ for (j = 0; svcnames[j] != NULL; j++) {
+ code = krb5_build_principal_ext(context, &princ,
+ strlen(realm),
+ realm,
+ strlen(svcnames[j]),
+ svcnames[j],
+ strlen(myhostname),
+ myhostname,
+ NULL);
+ if (code) {
+ printerr(1, "%s while building principal for "
+ "'%s/%s@%s'\n", error_message(code),
+ svcnames[j], myhostname, realm);
+ continue;
+ }
+ code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
+ krb5_free_principal(context, princ);
+ if (code) {
+ printerr(3, "%s while getting keytab entry for "
+ "'%s/%s@%s'\n", error_message(code),
+ svcnames[j], myhostname, realm);
+ } else {
+ printerr(3, "Success getting keytab entry for "
+ "'%s/%s@%s'\n",
+ svcnames[j], myhostname, realm);
+ retval = 0;
+ goto out;
+ }
+ retval = code;
+ }
+ /*
+ * Nothing found with our hostname instance, now look for
+ * names with any instance (they must have an instance)
+ */
+ for (j = 0; svcnames[j] != NULL; j++) {
+ int found = 0;
+ code = gssd_search_krb5_keytab(context, kt, realm,
+ svcnames[j], &found, kte);
+ if (!code && found) {
+ printerr(3, "Success getting keytab entry for "
+ "%s/*@%s\n", svcnames[j], realm);
+ retval = 0;
+ goto out;
+ }
+ }
+ if (!tried_all) {
+ i++;
+ realm = realmnames[i];
+ }
+ }
+out:
+ if (default_realm)
+ k5_free_default_realm(context, default_realm);
+ if (realmnames)
+ krb5_free_host_realm(context, realmnames);
+ return retval;
+}
+
/*==========================*/
/*=== External routines ===*/
/*==========================*/
@@ -836,3 +1237,68 @@ gssd_destroy_krb5_machine_creds(void)
krb5_free_context(context);
}

+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ */
+int
+gssd_refresh_krb5_machine_credential(char *hostname,
+ struct gssd_k5_kt_princ *ple)
+{
+ krb5_error_code code = 0;
+ krb5_context context;
+ krb5_keytab kt = NULL;;
+ int retval = 0;
+
+ if (hostname == NULL && ple == NULL)
+ return EINVAL;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+ __FUNCTION__, error_message(code));
+ retval = code;
+ goto out;
+ }
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+ __FUNCTION__, error_message(code), keytabfile);
+ goto out;
+ }
+
+ if (ple == NULL) {
+ krb5_keytab_entry kte;
+
+ code = find_keytab_entry(context, kt, hostname, &kte);
+ if (code) {
+ printerr(0, "ERROR: %s: no usable keytab entry found "
+ "in keytab %s for connection with host %s\n",
+ __FUNCTION__, keytabfile, hostname);
+ retval = code;
+ goto out;
+ }
+
+ ple = get_ple_by_princ(context, kte.principal);
+ k5_free_kt_entry(context, &kte);
+ if (ple == NULL) {
+ char *pname;
+ if ((krb5_unparse_name(context, kte.principal, &pname))) {
+ pname = NULL;
+ }
+ printerr(0, "ERROR: %s: Could not locate or create "
+ "ple struct for principal %s for connection "
+ "with host %s\n",
+ __FUNCTION__, pname ? pname : "<unparsable>",
+ hostname);
+ if (pname) k5_free_unparsed_name(context, pname);
+ goto out;
+ }
+ }
+ retval = gssd_get_single_krb5_cred(context, kt, ple);
+out:
+ if (kt)
+ krb5_kt_close(context, kt);
+ krb5_free_context(context);
+ return retval;
+}
+
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index 36dfcc8..625d53c 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -22,6 +22,8 @@ int gssd_refresh_krb5_machine_creds(voi
void gssd_free_krb5_machine_cred_list(char **list);
void gssd_setup_krb5_machine_gss_ccache(char *servername);
void gssd_destroy_krb5_machine_creds(void);
+int gssd_refresh_krb5_machine_credential(char *hostname,
+ struct gssd_k5_kt_princ *ple);

#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:10

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 3/8] Hide differences between MIT and Heimdal in macros

From: Kevin Coffman <[email protected]>

Clean up a lot of #ifdef'd code using macros, masking
the differences between MIT and Heimdal implementations.
The currently unused macros will be used in later patches.

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/krb5_util.c | 30 +++++-------------------------
utils/gssd/krb5_util.h | 15 +++++++++++++++
2 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index a0ee110..7ef690b 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -364,11 +364,7 @@ #endif
"principal '%s' from keytab '%s'\n",
error_message(code),
pname ? pname : "<unparsable>", kt_name);
-#ifdef HAVE_KRB5
- if (pname) krb5_free_unparsed_name(context, pname);
-#else
- if (pname) free(pname);
-#endif
+ if (pname) k5_free_unparsed_name(context, pname);
goto out;
}

@@ -497,11 +493,7 @@ gssd_process_krb5_keytab(krb5_context co
if (ple == NULL) {
printerr(0, "ERROR: could not allocate storage "
"for principal list entry\n");
-#ifdef HAVE_KRB5
- krb5_free_unparsed_name(context, pname);
-#else
- free(pname);
-#endif
+ k5_free_unparsed_name(context, pname);
krb5_kt_free_entry(context, &kte);
retval = ENOMEM;
goto out;
@@ -521,11 +513,7 @@ #endif
printerr(0, "ERROR: %s while copying realm to "
"principal list entry\n",
"not enough memory");
-#ifdef HAVE_KRB5
- krb5_free_unparsed_name(context, pname);
-#else
- free(pname);
-#endif
+ k5_free_unparsed_name(context, pname);
krb5_kt_free_entry(context, &kte);
retval = ENOMEM;
goto out;
@@ -535,11 +523,7 @@ #endif
printerr(0, "ERROR: %s while copying principal "
"to principal list entry\n",
error_message(code));
-#ifdef HAVE_KRB5
- krb5_free_unparsed_name(context, pname);
-#else
- free(pname);
-#endif
+ k5_free_unparsed_name(context, pname);
krb5_kt_free_entry(context, &kte);
retval = code;
goto out;
@@ -555,11 +539,7 @@ #endif
printerr(2, "We will NOT use this entry (%s)\n",
pname);
}
-#ifdef HAVE_KRB5
- krb5_free_unparsed_name(context, pname);
-#else
- free(pname);
-#endif
+ k5_free_unparsed_name(context, pname);
krb5_kt_free_entry(context, &kte);
}

diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index da04530..36dfcc8 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -27,4 +27,19 @@ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
#endif

+/*
+ * Hide away some of the MIT vs. Heimdal differences
+ * here with macros...
+ */
+
+#ifdef HAVE_KRB5
+#define k5_free_unparsed_name(ctx, name) krb5_free_unparsed_name((ctx), (name))
+#define k5_free_default_realm(ctx, realm) krb5_free_default_realm((ctx), (realm))
+#define k5_free_kt_entry(ctx, kte) krb5_free_keytab_entry_contents((ctx),(kte))
+#else /* Heimdal */
+#define k5_free_unparsed_name(ctx, name) free(name)
+#define k5_free_default_realm(ctx, realm) free(realm)
+#define k5_free_kt_entry(ctx, kte) krb5_kt_free_entry((ctx),(kte))
+#endif
+
#endif /* KRB5_UTIL_H */

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:18

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 7/8] Remove the now unused functions

From: Kevin Coffman <[email protected]>

Remove functions that are no longer used when when obtaining
machine credentials.

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/krb5_util.c | 234 ------------------------------------------------
utils/gssd/krb5_util.h | 1
2 files changed, 0 insertions(+), 235 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 5d433b1..50773b1 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -134,9 +134,6 @@ static int select_krb5_ccache(const stru
static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
static int gssd_get_single_krb5_cred(krb5_context context,
krb5_keytab kt, struct gssd_k5_kt_princ *ple);
-static int gssd_have_realm_ple(void *realm);
-static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
- char *kt_name);


/*
@@ -422,147 +419,6 @@ #endif
}

/*
- * Determine if we already have a ple for the given realm
- *
- * Returns:
- * 0 => no ple found for given realm
- * 1 => found ple for given realm
- */
-static int
-gssd_have_realm_ple(void *r)
-{
- struct gssd_k5_kt_princ *ple;
-#ifdef HAVE_KRB5
- krb5_data *realm = (krb5_data *)r;
-#else
- char *realm = (char *)r;
-#endif
-
- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-#ifdef HAVE_KRB5
- if ((realm->length == strlen(ple->realm)) &&
- (strncmp(realm->data, ple->realm, realm->length) == 0)) {
-#else
- if (strcmp(realm, ple->realm) == 0) {
-#endif
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Process the given keytab file and create a list of principals we
- * might use as machine credentials.
- *
- * Returns:
- * 0 => Sucess
- * nonzero => Error
- */
-static int
-gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
-{
- krb5_kt_cursor cursor;
- krb5_keytab_entry kte;
- krb5_error_code code;
- struct gssd_k5_kt_princ *ple;
- int retval = -1;
-
- /*
- * Look through each entry in the keytab file and determine
- * if we might want to use it as machine credentials. If so,
- * save info in the global principal list (gssd_k5_kt_princ_list).
- * Note: (ple == principal list entry)
- */
- if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
- printerr(0, "ERROR: %s while beginning keytab scan "
- "for keytab '%s'\n",
- error_message(code), kt_name);
- retval = code;
- goto out;
- }
-
- while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
- char *pname;
- if ((code = krb5_unparse_name(context, kte.principal,
- &pname))) {
- printerr(0, "WARNING: Skipping keytab entry because "
- "we failed to unparse principal name: %s\n",
- error_message(code));
- krb5_kt_free_entry(context, &kte);
- continue;
- }
- printerr(2, "Processing keytab entry for principal '%s'\n",
- pname);
- /* Just use the first keytab entry found for each realm */
- if ((!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
- printerr(2, "We WILL use this entry (%s)\n", pname);
- ple = malloc(sizeof(struct gssd_k5_kt_princ));
- if (ple == NULL) {
- printerr(0, "ERROR: could not allocate storage "
- "for principal list entry\n");
- k5_free_unparsed_name(context, pname);
- krb5_kt_free_entry(context, &kte);
- retval = ENOMEM;
- goto out;
- }
- /* These will be filled in later */
- ple->next = NULL;
- ple->ccname = NULL;
- ple->endtime = 0;
- if ((ple->realm =
-#ifdef HAVE_KRB5
- strndup(kte.principal->realm.data,
- kte.principal->realm.length))
-#else
- strdup(kte.principal->realm))
-#endif
- == NULL) {
- printerr(0, "ERROR: %s while copying realm to "
- "principal list entry\n",
- "not enough memory");
- k5_free_unparsed_name(context, pname);
- krb5_kt_free_entry(context, &kte);
- retval = ENOMEM;
- goto out;
- }
- if ((code = krb5_copy_principal(context,
- kte.principal, &ple->princ))) {
- printerr(0, "ERROR: %s while copying principal "
- "to principal list entry\n",
- error_message(code));
- k5_free_unparsed_name(context, pname);
- krb5_kt_free_entry(context, &kte);
- retval = code;
- goto out;
- }
- if (gssd_k5_kt_princ_list == NULL)
- gssd_k5_kt_princ_list = ple;
- else {
- ple->next = gssd_k5_kt_princ_list;
- gssd_k5_kt_princ_list = ple;
- }
- }
- else {
- printerr(2, "We will NOT use this entry (%s)\n",
- pname);
- }
- k5_free_unparsed_name(context, pname);
- krb5_kt_free_entry(context, &kte);
- }
-
- if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
- printerr(0, "WARNING: %s while ending keytab scan for "
- "keytab '%s'\n",
- error_message(code), kt_name);
- }
-
- retval = 0;
- out:
- return retval;
-}
-
-/*
* Depending on the version of Kerberos, we either need to use
* a private function, or simply set the environment variable.
*/
@@ -1039,96 +895,6 @@ gssd_setup_krb5_machine_gss_ccache(char
}

/*
- * The first time through this routine, go through the keytab and
- * determine which keys we will try to use as machine credentials.
- * Every time through this routine, try to obtain credentials using
- * the keytab entries selected the first time through.
- *
- * Returns:
- * 0 => obtained one or more credentials
- * nonzero => error
- *
- */
-
-int
-gssd_refresh_krb5_machine_creds(void)
-{
- krb5_context context = NULL;
- krb5_keytab kt = NULL;;
- krb5_error_code code;
- int retval = -1;
- struct gssd_k5_kt_princ *ple;
- int gotone = 0;
- static int processed_keytab = 0;
-
-
- code = krb5_init_context(&context);
- if (code) {
- printerr(0, "ERROR: %s while initializing krb5 in "
- "gssd_refresh_krb5_machine_creds\n",
- error_message(code));
- retval = code;
- goto out;
- }
-
- printerr(1, "Using keytab file '%s'\n", keytabfile);
-
- if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
- printerr(0, "ERROR: %s while resolving keytab '%s'\n",
- error_message(code), keytabfile);
- goto out;
- }
-
- /* Only go through the keytab file once. Only print messages once. */
- if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
- processed_keytab = 1;
- gssd_process_krb5_keytab(context, kt, keytabfile);
- if (gssd_k5_kt_princ_list == NULL) {
- printerr(0, "ERROR: No usable keytab entries found in "
- "keytab '%s'\n", keytabfile);
- printerr(0, "Do you have a valid keytab entry for "
- "%s/<your.host>@<YOUR.REALM> in "
- "keytab file %s ?\n",
- GSSD_SERVICE_NAME, keytabfile);
- printerr(0, "Continuing without (machine) credentials "
- "- nfs4 mounts with Kerberos will fail\n");
- }
- }
-
- /*
- * If we don't have any keytab entries we liked, then we have a problem
- */
- if (gssd_k5_kt_princ_list == NULL) {
- retval = ENOENT;
- goto out;
- }
-
- /*
- * Now go through the list of saved entries and get initial
- * credentials for them (We can't do this while making the
- * list because it messes up the keytab iteration cursor
- * when we use the keytab to get credentials.)
- */
- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
- if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
- gotone++;
- }
- }
- if (!gotone) {
- printerr(0, "ERROR: No usable machine credentials obtained\n");
- goto out;
- }
-
- retval = 0;
- out:
- if (kt) krb5_kt_close(context, kt);
- krb5_free_context(context);
-
- return retval;
-}
-
-
-/*
* Return an array of pointers to names of credential cache files
* which can be used to try to create gss contexts with a server.
*
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index ce7cb57..6041048 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -19,7 +19,6 @@ struct gssd_k5_kt_princ {

void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
int gssd_get_krb5_machine_cred_list(char ***list);
-int gssd_refresh_krb5_machine_creds(void);
void gssd_free_krb5_machine_cred_list(char **list);
void gssd_setup_krb5_machine_gss_ccache(char *servername);
void gssd_destroy_krb5_machine_creds(void);

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:14

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 5/8] Use newly added keytab functions

From: Kevin Coffman <[email protected]>

Use the new functions added in the previous patch.

Obtain machine credentials in a pre-determined order

Look for appropriate machine credentials in the following order:
root/<fqdn>@REALM
nfs/<fqdn>@REALM
host/<fqdn>@REALM
root/<any-name>@REALM
nfs/<any-name>@REALM
host/<any-name>@REALM

The first matching credential will be used.

Also, the machine credentials to be used are now determined
"on-demand" rather than at gssd startup. This allows keytab
additions to be noticed and used without requiring a restart of gssd.

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/gssd.c | 4 ----
utils/gssd/gssd.man | 21 ++++++++++++++++-----
utils/gssd/gssd_proc.c | 8 +++++---
utils/gssd/krb5_util.c | 15 ++++++++-------
utils/gssd/krb5_util.h | 3 ++-
5 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 747637c..b6c4ee4 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -165,10 +165,6 @@ #endif
signal(SIGTERM, sig_die);
signal(SIGHUP, sig_hup);

- /* Process keytab file and get machine credentials */
- if (root_uses_machine_creds)
- gssd_refresh_krb5_machine_creds();
-
gssd_run();
printerr(0, "gssd_run returned!\n");
abort();
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index f2ecd69..8da10b2 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -45,14 +45,25 @@ to use the keys found in
.I keytab
to obtain "machine credentials".
The default value is "/etc/krb5.keytab".
+.IP
Previous versions of
.B rpc.gssd
used only "nfs/*" keys found within the keytab.
-Now, the first keytab entry for each distinct Kerberos realm
-within the keytab is used. This means that an NFS client
-no longer needs an "nfs/hostname" principal and keytab entry,
-but can instead use a "host/hostname" (or any other) keytab
-entry that is available.
+To be more consistent with other implementations, we now look for
+specific keytab entries. The search order for keytabs to be used
+for "machine credentials" is now:
+.br
+ root/<hostname>@<REALM>
+.br
+ nfs/<hostname>@<REALM>
+.br
+ host/<hostname>@<REALM>
+.br
+ root/<anyname>@<REALM>
+.br
+ nfs/<anyname>@<REALM>
+.br
+ host/<anyname>@<REALM>
.TP
.B -p path
Tells
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index eff740c..48880b6 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -700,14 +700,16 @@ handle_krb5_upcall(struct clnt_info *clp
if (uid == 0 && root_uses_machine_creds == 1) {
int success = 0;

+ gssd_refresh_krb5_machine_credential(clp->servername,
+ NULL);
/*
* Get a list of credential cache names and try each
* of them until one works or we've tried them all
*/
if (gssd_get_krb5_machine_cred_list(&credlist)) {
- printerr(0, "WARNING: Failed to obtain machine "
- "credentials for connection to "
- "server %s\n", clp->servername);
+ printerr(0, "ERROR: No credentials found "
+ "for connection to server %s\n",
+ clp->servername);
goto out_return_error;
}
for (ccname = credlist; ccname && *ccname; ccname++) {
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 20396e0..dee2639 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -358,8 +358,8 @@ #ifdef TEST_SHORT_LIFETIME
printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
#endif
- if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
- kt, 0, NULL, &options))) {
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, NULL, &options))) {
char *pname;
if ((krb5_unparse_name(context, ple->princ, &pname))) {
pname = NULL;
@@ -1146,18 +1146,19 @@ gssd_get_krb5_machine_cred_list(char ***
retval = -1;
*list = (char **) NULL;

- /* Refresh machine credentials */
- if ((retval = gssd_refresh_krb5_machine_creds())) {
- goto out;
- }
-
if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
retval = ENOMEM;
goto out;
}

+ /* Need to serialize list if we ever become multi-threaded! */
+
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);
+ if (retval)
+ continue;
if (i + 1 > listsize) {
listsize += listinc;
l = (char **)
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index 625d53c..ce7cb57 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -5,7 +5,8 @@ #include <krb5.h>

/*
* List of principals from our keytab that we
- * may try to get credentials for
+ * will try to use to obtain credentials
+ * (known as a principal list entry (ple))
*/
struct gssd_k5_kt_princ {
struct gssd_k5_kt_princ *next;

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:17

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 6/8] Clean up gssd_get_single_krb5_cred and its debugging messages

From: Kevin Coffman <[email protected]>

Clean up gssd_get_single_krb5_cred and its debugging messages

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/krb5_util.c | 20 ++++++++++++--------
1 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index dee2639..5d433b1 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -334,6 +334,7 @@ gssd_get_single_krb5_cred(krb5_context c
int code;
time_t now = time(0);
char *cache_type;
+ char *pname = NULL;

memset(&my_creds, 0, sizeof(my_creds));

@@ -350,6 +351,9 @@ gssd_get_single_krb5_cred(krb5_context c
goto out;
}

+ if ((krb5_unparse_name(context, ple->princ, &pname)))
+ pname = NULL;
+
krb5_get_init_creds_opt_init(&options);
krb5_get_init_creds_opt_set_address_list(&options, NULL);

@@ -360,15 +364,10 @@ #ifdef TEST_SHORT_LIFETIME
#endif
if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
kt, 0, NULL, &options))) {
- char *pname;
- if ((krb5_unparse_name(context, ple->princ, &pname))) {
- pname = NULL;
- }
printerr(0, "WARNING: %s while getting initial ticket for "
- "principal '%s' from keytab '%s'\n",
+ "principal '%s' using keytab '%s'\n",
error_message(code),
pname ? pname : "<unparsable>", kt_name);
- if (pname) k5_free_unparsed_name(context, pname);
goto out;
}

@@ -385,10 +384,12 @@ #endif
GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
ple->endtime = my_creds.times.endtime;
+ if (ple->ccname != NULL)
+ free(ple->ccname);
ple->ccname = strdup(cc_name);
if (ple->ccname == NULL) {
printerr(0, "ERROR: no storage to duplicate credentials "
- "cache name\n");
+ "cache name '%s'\n", cc_name);
code = ENOMEM;
goto out;
}
@@ -409,8 +410,11 @@ #endif
}

code = 0;
- printerr(1, "Using (machine) credentials cache: '%s'\n", cc_name);
+ printerr(2, "Successfully obtained machine credentials for "
+ "principal '%s' stored in ccache '%s'\n", pname, cc_name);
out:
+ if (pname)
+ k5_free_unparsed_name(context, pname);
if (ccache)
krb5_cc_close(context, ccache);
krb5_free_cred_contents(context, &my_creds);

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 22:32:20

by Kevin Coffman

[permalink] [raw]
Subject: [PATCH 8/8] Add a debug message indicating that gssd is ready to process requests

From: Kevin Coffman <[email protected]>

Add a debug message indicating that gssd is ready to process requests

Signed-off-by: Kevin Coffman <[email protected]>
---

utils/gssd/gssd_main_loop.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c
index 0559f7b..84f04e9 100644
--- a/utils/gssd/gssd_main_loop.c
+++ b/utils/gssd/gssd_main_loop.c
@@ -116,6 +116,7 @@ gssd_run()

init_client_list();

+ printerr(1, "beginning poll\n");
while (1) {
while (dir_changed) {
dir_changed = 0;

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs

2007-03-30 23:10:03

by NeilBrown

[permalink] [raw]
Subject: Re: [PATCH 0/8] gssd cleanup and modified machine cred handling

On Friday March 30, [email protected] wrote:
> These patches do some gssd code clean-up and change the way keytabs are
> selected to obtain machine credentials.
>

Thanks. All applied.

NeilBrown

> 1) adds missing newlines to some seldom printed error messages
> 2) fixes a memory leak in the error path of limit_krb5_enctypes()
> 3) adds some macros to clean up differences between MIT and Heimdal interfaces
> 4) adds the new functions that change the way keytabs are selected.
> the new behavior is to search for particular keytab entries in a
> specified order:
>
> root/<fqdn>@<REALM>
> nfs/<fqdn>@<REALM>
> host/<fqdn>@<REALM>
> root/<any-name>@<REALM>
> nfs/<any-name>@<REALM>
> host/<any-name>@<REALM>
>
> 5) actually use the new functions added in the previous patch
> 6) does some cleanup in gssd_get_single_krb5_cred() and improves
> the debugging messages
> 7) removes the now obsolete routines
> 8) adds a debug message to indicate processing is beginning

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
NFS maillist - [email protected]
https://lists.sourceforge.net/lists/listinfo/nfs