NFS: move credential expiry tracking out of SUNRPC into NFS.
authorNeilBrown <neilb@suse.com>
Mon, 3 Dec 2018 00:30:30 +0000 (11:30 +1100)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 19 Dec 2018 18:52:45 +0000 (13:52 -0500)
NFS needs to know when a credential is about to expire so that
it can modify write-back behaviour to finish the write inside the
expiry time.
It currently uses functions in SUNRPC code which make use of a
fairly complex callback scheme and flags in the generic credientials.

As I am working to discard the generic credentials, this has to change.

This patch moves the logic into NFS, in part by finding and caching
the low-level credential in the open_context.  We then make direct
cred-api calls on that.

This makes the code much simpler and removes a dependency on generic
rpc credentials.

Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/inode.c
fs/nfs/write.c
include/linux/nfs_fs.h
include/linux/sunrpc/auth.h
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/auth_gss.c

index 5b1eee4952b7309b2c80b0777d8c0390ab8e599c..aea0157431724c038818921d74e955956f9d05ea 100644 (file)
@@ -962,6 +962,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
        nfs_sb_active(dentry->d_sb);
        ctx->dentry = dget(dentry);
        ctx->cred = cred;
+       ctx->ll_cred = NULL;
        ctx->state = NULL;
        ctx->mode = f_mode;
        ctx->flags = 0;
@@ -1001,6 +1002,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
                put_rpccred(ctx->cred);
        dput(ctx->dentry);
        nfs_sb_deactive(sb);
+       put_rpccred(ctx->ll_cred);
        kfree(ctx->mdsthreshold);
        kfree_rcu(ctx, rcu_head);
 }
index 586726a590d88149fa0fe769de37fa7aa14033a2..c1452f838131d93985ed8918a63a5244a3c78b8e 100644 (file)
@@ -1233,9 +1233,12 @@ int
 nfs_key_timeout_notify(struct file *filp, struct inode *inode)
 {
        struct nfs_open_context *ctx = nfs_file_open_context(filp);
-       struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
 
-       return rpcauth_key_timeout_notify(auth, ctx->cred);
+       if (nfs_ctx_key_to_expire(ctx, inode) &&
+           !ctx->ll_cred)
+               /* Already expired! */
+               return -EACCES;
+       return 0;
 }
 
 /*
@@ -1244,8 +1247,23 @@ nfs_key_timeout_notify(struct file *filp, struct inode *inode)
 bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
 {
        struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
+       struct rpc_cred *cred = ctx->ll_cred;
+       struct auth_cred acred = {
+               .cred = ctx->cred->cr_cred,
+       };
 
-       return rpcauth_cred_key_to_expire(auth, ctx->cred);
+       if (cred && !cred->cr_ops->crmatch(&acred, cred, 0)) {
+               put_rpccred(cred);
+               ctx->ll_cred = NULL;
+               cred = NULL;
+       }
+       if (!cred)
+               cred = auth->au_ops->lookup_cred(auth, &acred, 0);
+       if (!cred || IS_ERR(cred))
+               return true;
+       ctx->ll_cred = cred;
+       return !!(cred->cr_ops->crkey_timeout &&
+                 cred->cr_ops->crkey_timeout(cred));
 }
 
 /*
index 6e0417c022799154bc3809ec5ffd65344dac75a3..ecf22c0034d560d21409bbf916544013cda7234b 100644 (file)
@@ -71,6 +71,7 @@ struct nfs_open_context {
        fl_owner_t flock_owner;
        struct dentry *dentry;
        struct rpc_cred *cred;
+       struct rpc_cred *ll_cred;       /* low-level cred - use to check for expiry */
        struct nfs4_state *state;
        fmode_t mode;
 
index 28b34c740c4394eec32242f68578d97f24ec55fa..0bdc2f4957ff08912a22a0ab6aec0e8f2c0c798c 100644 (file)
 
 struct rpcsec_gss_info;
 
-/* auth_cred ac_flags bits */
-enum {
-       RPC_CRED_KEY_EXPIRE_SOON = 1, /* underlying cred key will expire soon */
-       RPC_CRED_NOTIFY_TIMEOUT = 2,   /* nofity generic cred when underlying
-                                       key will expire soon */
-};
-
 struct auth_cred {
        const struct cred *cred;
        const char *principal;  /* If present, this is a machine credential */
-       unsigned long ac_flags;
 };
 
 /*
@@ -154,7 +146,6 @@ struct rpc_credops {
        int                     (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t,
                                                void *, __be32 *, void *);
        int                     (*crkey_timeout)(struct rpc_cred *);
-       bool                    (*crkey_to_expire)(struct rpc_cred *);
        char *                  (*crstringify_acceptor)(struct rpc_cred *);
        bool                    (*crneed_reencode)(struct rpc_task *);
 };
@@ -198,9 +189,6 @@ int                 rpcauth_uptodatecred(struct rpc_task *);
 int                    rpcauth_init_credcache(struct rpc_auth *);
 void                   rpcauth_destroy_credcache(struct rpc_auth *);
 void                   rpcauth_clear_credcache(struct rpc_cred_cache *);
-int                    rpcauth_key_timeout_notify(struct rpc_auth *,
-                                               struct rpc_cred *);
-bool                   rpcauth_cred_key_to_expire(struct rpc_auth *, struct rpc_cred *);
 char *                 rpcauth_stringify_acceptor(struct rpc_cred *);
 
 static inline
index 27d90578e7a0c117f3c7637ff218834799b44534..cf23eed01b1c287511a878a695323ad45378079a 100644 (file)
@@ -360,29 +360,6 @@ out_nocache:
 }
 EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
 
-/*
- * Setup a credential key lifetime timeout notification
- */
-int
-rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
-{
-       if (!cred->cr_auth->au_ops->key_timeout)
-               return 0;
-       return cred->cr_auth->au_ops->key_timeout(auth, cred);
-}
-EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
-
-bool
-rpcauth_cred_key_to_expire(struct rpc_auth *auth, struct rpc_cred *cred)
-{
-       if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
-               return false;
-       if (!cred->cr_ops->crkey_to_expire)
-               return false;
-       return cred->cr_ops->crkey_to_expire(cred);
-}
-EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
-
 char *
 rpcauth_stringify_acceptor(struct rpc_cred *cred)
 {
index 5f7aa6324b78fc81a28aae1a9e04bc1d855ebd64..c57e83184d3c0f1b4ebed1af70d70e58549b292e 100644 (file)
@@ -87,7 +87,6 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, g
        gcred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
 
        gcred->acred.cred = gcred->gc_base.cr_cred;
-       gcred->acred.ac_flags = 0;
        gcred->acred.principal = acred->principal;
 
        dprintk("RPC:       allocated %s cred %p for uid %d gid %d\n",
@@ -179,72 +178,12 @@ void rpc_destroy_generic_auth(void)
        rpcauth_destroy_credcache(&generic_auth);
 }
 
-/*
- * Test the the current time (now) against the underlying credential key expiry
- * minus a timeout and setup notification.
- *
- * The normal case:
- * If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
- * the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
- * rpc_credops crmatch routine to notify this generic cred when it's key
- * expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
- *
- * The error case:
- * If the underlying cred lookup fails, return -EACCES.
- *
- * The 'almost' error case:
- * If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
- * key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
- * on the acred ac_flags and return 0.
- */
-static int
-generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
-{
-       struct auth_cred *acred = &container_of(cred, struct generic_cred,
-                                               gc_base)->acred;
-       struct rpc_cred *tcred;
-       int ret = 0;
-
-
-       /* Fast track for non crkey_timeout (no key) underlying credentials */
-       if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
-               return 0;
-
-       /* Fast track for the normal case */
-       if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
-               return 0;
-
-       /* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
-       tcred = auth->au_ops->lookup_cred(auth, acred, 0);
-       if (IS_ERR(tcred))
-               return -EACCES;
-
-       /* Test for the almost error case */
-       ret = tcred->cr_ops->crkey_timeout(tcred);
-       if (ret != 0) {
-               set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
-               ret = 0;
-       } else {
-               /* In case underlying cred key has been reset */
-               if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
-                                       &acred->ac_flags))
-                       dprintk("RPC:        UID %d Credential key reset\n",
-                               from_kuid(&init_user_ns, tcred->cr_uid));
-               /* set up fasttrack for the normal case */
-               set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
-       }
-
-       put_rpccred(tcred);
-       return ret;
-}
-
 static const struct rpc_authops generic_auth_ops = {
        .owner = THIS_MODULE,
        .au_name = "Generic",
        .hash_cred = generic_hash_cred,
        .lookup_cred = generic_lookup_cred,
        .crcreate = generic_create_cred,
-       .key_timeout = generic_key_timeout,
 };
 
 static struct rpc_auth generic_auth = {
@@ -252,17 +191,9 @@ static struct rpc_auth generic_auth = {
        .au_count = REFCOUNT_INIT(1),
 };
 
-static bool generic_key_to_expire(struct rpc_cred *cred)
-{
-       struct auth_cred *acred = &container_of(cred, struct generic_cred,
-                                               gc_base)->acred;
-       return test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
-}
-
 static const struct rpc_credops generic_credops = {
        .cr_name = "Generic cred",
        .crdestroy = generic_destroy_cred,
        .crbind = generic_bind_cred,
        .crmatch = generic_match,
-       .crkey_to_expire = generic_key_to_expire,
 };
index b218e15b61cb8ddb87050db664f5fb48f325a094..03a1cd5bfb430a1431be5a7a14aa8bb4fb5e4e0d 100644 (file)
@@ -1517,23 +1517,10 @@ out:
                if (gss_cred->gc_principal == NULL)
                        return 0;
                ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
-               goto check_expire;
-       }
-       if (gss_cred->gc_principal != NULL)
-               return 0;
-       ret = uid_eq(rc->cr_uid, acred->cred->fsuid);
-
-check_expire:
-       if (ret == 0)
-               return ret;
-
-       /* Notify acred users of GSS context expiration timeout */
-       if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) &&
-           (gss_key_timeout(rc) != 0)) {
-               /* test will now be done from generic cred */
-               test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
-               /* tell NFS layer that key will expire soon */
-               set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
+       } else {
+               if (gss_cred->gc_principal != NULL)
+                       return 0;
+               ret = uid_eq(rc->cr_uid, acred->cred->fsuid);
        }
        return ret;
 }