]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/krb5_util.c
gssd: picking wrong creds
[nfs-utils.git] / utils / gssd / krb5_util.c
index 3009cc57b24f00ec83393b55e32578eb05b49eb2..d23654ff1b9c581ebb713370efc8d93f88158f1a 100644 (file)
@@ -137,7 +137,7 @@ static int select_krb5_ccache(const 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);
 
@@ -170,9 +170,8 @@ select_krb5_ccache(const struct dirent *d)
  * 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, char *dirname, struct dirent **d)
@@ -186,7 +185,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
        char buf[1030];
        char *princname = NULL;
        char *realm = NULL;
-       int score, best_match_score = 0;
+       int score, best_match_score = 0, err = -EACCES;
 
        memset(&best_match_stat, 0, sizeof(best_match_stat));
        *d = NULL;
@@ -225,10 +224,18 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
                                free(namelist[i]);
                                continue;
                        }
+                       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;
                        }
 
@@ -284,67 +291,13 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
                }
                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,
-                                   ENCTYPE_DES_CBC_MD5,
-                                   ENCTYPE_DES_CBC_MD4 };
-       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) {
-               if (get_verbosity() > 0)
-                       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
@@ -359,7 +312,8 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
 static int
 gssd_get_single_krb5_cred(krb5_context context,
                          krb5_keytab kt,
-                         struct gssd_k5_kt_princ *ple)
+                         struct gssd_k5_kt_princ *ple,
+                         int nocache)
 {
 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
        krb5_get_init_creds_opt *init_opts = NULL;
@@ -379,7 +333,7 @@ gssd_get_single_krb5_cred(krb5_context context,
 
        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;
@@ -796,10 +750,9 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
  */
 static int
 find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
-                 krb5_keytab_entry *kte)
+                 krb5_keytab_entry *kte, const char **svcnames)
 {
        krb5_error_code code;
-       const char *svcnames[] = { "root", "nfs", "host", NULL };
        char **realmnames = NULL;
        char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
        int i, j, retval;
@@ -1024,29 +977,29 @@ err_cache:
  * given only a UID.  We really need more information, but we
  * do the best we can.
  *
- * Returns:
- *     0 => a ccache was found
- *     1 => no ccache was found
+ * Returns 0 if a ccache was found, and a non-zero error code otherwise.
  */
 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, dirname, &d)) {
-               snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
-               free(d);
-       }
-       else
-               return 1;
+       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 0;
+       return err;
 }
 
 /*
@@ -1095,7 +1048,8 @@ gssd_get_krb5_machine_cred_list(char ***list)
        for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
                if (ple->ccname) {
                        /* Make sure cred is up-to-date before returning it */
-                       retval = gssd_refresh_krb5_machine_credential(NULL, ple);
+                       retval = gssd_refresh_krb5_machine_credential(NULL, ple,
+                               NULL);
                        if (retval)
                                continue;
                        if (i + 1 > listsize) {
@@ -1185,14 +1139,24 @@ gssd_destroy_krb5_machine_creds(void)
  */
 int
 gssd_refresh_krb5_machine_credential(char *hostname,
-                                    struct gssd_k5_kt_princ *ple)
+                                    struct gssd_k5_kt_princ *ple, 
+                                        char *service)
 {
        krb5_error_code code = 0;
        krb5_context context;
        krb5_keytab kt = NULL;;
        int retval = 0;
        char *k5err = NULL;
+       const char *svcnames[4] = { "root", "nfs", "host", NULL };
 
+       /*
+        * If a specific service name was specified, use it.
+        * Otherwise, use the default list.
+        */
+       if (service != NULL && strcmp(service, "*") != 0) {
+               svcnames[0] = service;
+               svcnames[1] = NULL;
+       }
        if (hostname == NULL && ple == NULL)
                return EINVAL;
 
@@ -1215,7 +1179,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
        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",
@@ -1240,7 +1204,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
                        goto out;
                }
        }
-       retval = gssd_get_single_krb5_cred(context, kt, ple);
+       retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
 out:
        if (kt)
                krb5_kt_close(context, kt);
@@ -1292,3 +1256,68 @@ gssd_k5_get_default_realm(char **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, 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,
+                                   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 */