The rpc.gssd scans for any suitable kerberos ticket. In cross-realm
authorLukas Hejtmanek <xhejtman@ics.muni.cz>
Tue, 15 Jul 2008 14:12:39 +0000 (10:12 -0400)
committerSteve Dickson <steved@redhat.com>
Tue, 15 Jul 2008 14:12:39 +0000 (10:12 -0400)
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@ics.muni.cz>
Signed-off-by: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/gssd/gssd.c
utils/gssd/gssd.h
utils/gssd/gssd.man
utils/gssd/krb5_util.c
utils/gssd/krb5_util.h

index 2e6f316..6d8f3b9 100644 (file)
@@ -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')
index aef14cf..082039a 100644 (file)
@@ -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;
 
index e4f68f9..0a23cd6 100644 (file)
@@ -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
index 4a4d10b..77814bc 100644 (file)
@@ -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);
+}
index addae1c..4b2da6b 100644 (file)
@@ -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);