[PATCH 3/3] Add new "preferred realms" option for ccache searching

Kevin Coffman kwc at citi.umich.edu
Thu Jul 3 08:35:49 EDT 2008


From: Lukas Hejtmanek <xhejtman at ics.muni.cz>

The rpc.gssd scans for any suitable kerberos ticket. In cross-realm
environment this may not be the desired behaviour. Therefore a new
option, -R preferred realm, is presented so that the rpc.gssd prefers tickets
from this realm. By default, the default realm is preferred.

Signed-off-by: Lukas Hejtmanek <xhejtman at ics.muni.cz>
Signed-off-by: Kevin Coffman <kwc at citi.umich.edu>
---

 utils/gssd/gssd.c      |   11 +++
 utils/gssd/gssd.h      |    1 
 utils/gssd/gssd.man    |    7 ++
 utils/gssd/krb5_util.c |  160 ++++++++++++++++++++++++++++++++++++++++++++----
 utils/gssd/krb5_util.h |    1 
 5 files changed, 165 insertions(+), 15 deletions(-)

diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 2e6f316..6d8f3b9 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -61,6 +61,7 @@ char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
 int  use_memcache = 0;
 int  root_uses_machine_creds = 1;
 unsigned int  context_timeout = 0;
+char *preferred_realm = NULL;
 
 void
 sig_die(int signal)
@@ -83,7 +84,7 @@ sig_hup(int signal)
 static void
 usage(char *progname)
 {
-	fprintf(stderr, "usage: %s [-f] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout]\n",
+	fprintf(stderr, "usage: %s [-f] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n",
 		progname);
 	exit(1);
 }
@@ -100,7 +101,7 @@ main(int argc, char *argv[])
 	char *progname;
 
 	memset(ccachesearch, 0, sizeof(ccachesearch));
-	while ((opt = getopt(argc, argv, "fvrmnMp:k:d:t:")) != -1) {
+	while ((opt = getopt(argc, argv, "fvrmnMp:k:d:t:R:")) != -1) {
 		switch (opt) {
 			case 'f':
 				fg = 1;
@@ -138,6 +139,9 @@ main(int argc, char *argv[])
 			case 't':
 				context_timeout = atoi(optarg);
 				break;
+			case 'R':
+				preferred_realm = strdup(optarg);
+				break;
 			default:
 				usage(argv[0]);
 				break;
@@ -150,6 +154,9 @@ main(int argc, char *argv[])
 		ccachesearch[i++] = strtok(NULL, ":");
 	} while (ccachesearch[i-1] != NULL && i < GSSD_MAX_CCACHE_SEARCH);
 
+	if (preferred_realm == NULL)
+		gssd_k5_get_default_realm(&preferred_realm);
+
 	snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
 		 pipefs_dir, GSSD_SERVICE_NAME);
 	if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
index aef14cf..082039a 100644
--- a/utils/gssd/gssd.h
+++ b/utils/gssd/gssd.h
@@ -66,6 +66,7 @@ extern char			*ccachesearch[];
 extern int			use_memcache;
 extern int			root_uses_machine_creds;
 extern unsigned int 		context_timeout;
+extern char			*preferred_realm;
 
 TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
 
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index e4f68f9..0a23cd6 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -87,6 +87,13 @@ Increases the verbosity of the output (can be specified multiple times).
 If the rpcsec_gss library supports setting debug level,
 increases the verbosity of the output (can be specified multiple times).
 .TP
+.B -R realm
+Kerberos tickets from this
+.I realm
+will be preferred when scanning available credentials cache files to be
+used to create a context.  By default, the default realm, as configured
+in the Kerberos configuration file, is preferred.
+.TP
 .B -t timeout
 Timeout, in seconds, for kernel gss contexts. This option allows you to force 
 new kernel contexts to be negotiated after
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index 4a4d10b..77814bc 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -135,7 +135,8 @@ 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);
-
+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
@@ -179,6 +180,10 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, 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;
 
 	memset(&best_match_stat, 0, sizeof(best_match_stat));
 	*d = NULL;
@@ -190,10 +195,14 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
 	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", 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);
@@ -202,20 +211,33 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, 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]);
 				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
@@ -224,30 +246,38 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, 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);
 	}
@@ -884,6 +914,94 @@ out:
 	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 ===*/
 /*==========================*/
@@ -1134,3 +1252,19 @@ gssd_k5_err_msg(krb5_context context, krb5_error_code code)
 		return 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);
+}
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index addae1c..4b2da6b 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -27,6 +27,7 @@ int  gssd_refresh_krb5_machine_credential(char *hostname,
 					  struct gssd_k5_kt_princ *ple);
 const char *
 gssd_k5_err_msg(krb5_context context, krb5_error_code code);
+void gssd_k5_get_default_realm(char **def_realm);
 
 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
 int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);



More information about the NFSv4 mailing list