gssd: on krb5 upcall, have gssd send a more granular error code nfs-utils-1-2-2-rc5
authorJeff Layton <jlayton@redhat.com>
Tue, 12 Jan 2010 12:32:51 +0000 (07:32 -0500)
committerSteve Dickson <steved@redhat.com>
Tue, 12 Jan 2010 12:32:51 +0000 (07:32 -0500)
Currently if a krb5 context expires, GSSAPI authenticated RPC calls
start returning error (-EACCES in particular). This is bad when someone
has a long running job that's doing filesystem ops on a krb5 authenticated
NFS mount and just happens to forget to redo a 'kinit' in time.

The existing gssd always does a downcall with a '-1' error code if there
are problems, and the kernel always ignores this error code. Begin to
fix this by having gssd distinguish between someone that has no
credcache at all, and someone who has an expired one. In the case where
there is an existing credcache, have gssd downcall with an error code of
-EKEYEXPIRED. If there's not a credcache, then downcall with an error of
-EACCES.

We can then have the kernel use this error code to handle these
situations differently.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/gssd/gssd_proc.c
utils/gssd/krb5_util.c

index 795e06c..be4fb11 100644 (file)
@@ -904,6 +904,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
        char                    **ccname;
        char                    **dirname;
        int                     create_resp = -1;
+       int                     err, downcall_err = -EACCES;
 
        printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
 
@@ -944,7 +945,10 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
                                service == NULL)) {
                /* Tell krb5 gss which credentials cache to use */
                for (dirname = ccachesearch; *dirname != NULL; dirname++) {
-                       if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
+                       err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
+                       if (err == -EKEYEXPIRED)
+                               downcall_err = -EKEYEXPIRED;
+                       else if (!err)
                                create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
                                                             AUTHTYPE_KRB5);
                        if (create_resp == 0)
@@ -1034,7 +1038,7 @@ out:
        return;
 
 out_return_error:
-       do_error_downcall(fd, uid, -1);
+       do_error_downcall(fd, uid, downcall_err);
        goto out;
 }
 
index c3c131b..1295f57 100644 (file)
@@ -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;
@@ -229,6 +228,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
                                printerr(3, "CC file '%s' is expired or corrupt\n",
                                         statname);
                                free(namelist[i]);
+                               err = -EKEYEXPIRED;
                                continue;
                        }
 
@@ -284,11 +284,12 @@ 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;
+
+       return err;
 }
 
 
@@ -1024,29 +1025,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;
 }
 
 /*