NFS/SUNRPC: don't lookup machine credential until rpcauth_bindcred().
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)
When NFS creates a machine credential, it is a "generic" credential,
not tied to any auth protocol, and is really just a container for
the princpal name.
This doesn't get linked to a genuine credential until rpcauth_bindcred()
is called.
The lookup always succeeds, so various places that test if the machine
credential is NULL, are pointless.

As a step towards getting rid of generic credentials, this patch gets
rid of generic machine credentials.  The nfs_client and rpc_client
just hold a pointer to a constant principal name.
When a machine credential is wanted, a special static 'struct rpc_cred'
pointer is used. rpcauth_bindcred() recognizes this, finds the
principal from the client, and binds the correct credential.

Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/client.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4state.c
fs/nfsd/nfs4callback.c
include/linux/nfs_fs_sb.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/clnt.h
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/clnt.c

index cce151776709b9fcca0ddc544c2fa53b0bac0c2d..fb1cf1a4bda2a105e60cb23d95dea4b3abc09f70 100644 (file)
@@ -151,7 +151,6 @@ EXPORT_SYMBOL_GPL(unregister_nfs_version);
 struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
 {
        struct nfs_client *clp;
-       struct rpc_cred *cred;
        int err = -ENOMEM;
 
        if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
@@ -182,9 +181,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
        clp->cl_proto = cl_init->proto;
        clp->cl_net = get_net(cl_init->net);
 
-       cred = rpc_lookup_machine_cred("*");
-       if (!IS_ERR(cred))
-               clp->cl_machine_cred = cred;
+       clp->cl_principal = "*";
        nfs_fscache_get_client_cookie(clp);
 
        return clp;
@@ -246,11 +243,6 @@ void nfs_free_client(struct nfs_client *clp)
        if (!IS_ERR(clp->cl_rpcclient))
                rpc_shutdown_client(clp->cl_rpcclient);
 
-       if (clp->cl_machine_cred != NULL)
-               put_rpccred(clp->cl_machine_cred);
-       if (clp->cl_root_cred != NULL)
-               put_rpccred(clp->cl_root_cred);
-
        put_net(clp->cl_net);
        put_nfs_version(clp->cl_nfs_mod);
        kfree(clp->cl_hostname);
@@ -529,6 +521,7 @@ int nfs_create_rpc_client(struct nfs_client *clp,
                return PTR_ERR(clnt);
        }
 
+       clnt->cl_principal = clp->cl_principal;
        clp->cl_rpcclient = clnt;
        return 0;
 }
index ad649a49822f4b5326cc1b1daf94e66bd9baad82..eab41490ce58f42b41df6657ce12fc28b99e7ba4 100644 (file)
@@ -338,7 +338,6 @@ static inline bool
 _nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode,
                    struct rpc_clnt **clntp, struct rpc_message *msg)
 {
-       struct rpc_cred *newcred = NULL;
        rpc_authflavor_t flavor;
 
        if (sp4_mode == NFS_SP4_MACH_CRED_CLEANUP ||
@@ -353,13 +352,7 @@ _nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode,
                        return false;
        }
        if (test_bit(sp4_mode, &clp->cl_sp4_flags)) {
-               spin_lock(&clp->cl_lock);
-               if (clp->cl_machine_cred != NULL)
-                       /* don't call get_rpccred on the machine cred -
-                        * a reference will be held for life of clp */
-                       newcred = clp->cl_machine_cred;
-               spin_unlock(&clp->cl_lock);
-               msg->rpc_cred = newcred;
+               msg->rpc_cred = rpc_machine_cred();
 
                flavor = clp->cl_rpcclient->cl_auth->au_flavor;
                WARN_ON_ONCE(flavor != RPC_AUTH_GSS_KRB5I &&
index f142fca6995bd2e9012e691827c75fc49e677427..6304c79dbcd120e6b01b2f4f5c97c833fa58bdcd 100644 (file)
@@ -166,28 +166,15 @@ out:
 
 struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp)
 {
-       struct rpc_cred *cred = clp->cl_root_cred;
-
-       if (!cred)
-               cred = clp->cl_machine_cred;
-       if (cred)
-               return get_rpccred(cred);
-       return cred;
+       return get_rpccred(rpc_machine_cred());
 }
 
 static void nfs4_root_machine_cred(struct nfs_client *clp)
 {
-       struct rpc_cred *new;
 
-       new = rpc_lookup_machine_cred(NULL);
-       spin_lock(&clp->cl_lock);
-       if (clp->cl_root_cred == NULL) {
-               clp->cl_root_cred = new;
-               new = NULL;
-       }
-       spin_unlock(&clp->cl_lock);
-       if (new != NULL)
-               put_rpccred(new);
+       /* Force root creds instead of machine */
+       clp->cl_principal = NULL;
+       clp->cl_rpcclient->cl_principal = NULL;
 }
 
 static struct rpc_cred *
index c032e4c24a8dfd483ec7ec3b0e280700140ff77b..1dcee1fd32d9c3eb327642bc60f7b4f125f357ff 100644 (file)
@@ -847,14 +847,10 @@ static int max_cb_time(struct net *net)
 static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
 {
        if (clp->cl_minorversion == 0) {
-               char *principal = clp->cl_cred.cr_targ_princ ?
-                                       clp->cl_cred.cr_targ_princ : "nfs";
-               struct rpc_cred *cred;
-
-               cred = rpc_lookup_machine_cred(principal);
-               if (!IS_ERR(cred))
-                       get_rpccred(cred);
-               return cred;
+               client->cl_principal = clp->cl_cred.cr_targ_princ ?
+                       clp->cl_cred.cr_targ_princ : "nfs";
+
+               return get_rpccred(rpc_machine_cred());
        } else {
                struct rpc_auth *auth = client->cl_auth;
                struct auth_cred acred = {};
index fea51b44fe509de6ecd4daaa8bdfbd99a3fb4209..6aa8cc83c3b66317ca0fd20e26d591b7f29654f8 100644 (file)
@@ -58,8 +58,7 @@ struct nfs_client {
        struct nfs_subversion * cl_nfs_mod;     /* pointer to nfs version module */
 
        u32                     cl_minorversion;/* NFSv4 minorversion */
-       struct rpc_cred         *cl_machine_cred;
-       struct rpc_cred         *cl_root_cred;  /* Use when machine_cred is ineffective */
+       const char *            cl_principal;  /* used for machine cred */
 
 #if IS_ENABLED(CONFIG_NFS_V4)
        struct list_head        cl_ds_clients; /* auth flavor data servers */
index 1c0468f3947963a40398b5df2f3d7214b26d7ac2..28b34c740c4394eec32242f68578d97f24ec55fa 100644 (file)
@@ -75,6 +75,8 @@ struct rpc_cred {
 #define RPCAUTH_CRED_HASHED    2
 #define RPCAUTH_CRED_NEGATIVE  3
 
+struct rpc_cred *rpc_machine_cred(void);
+
 /* rpc_auth au_flags */
 #define RPCAUTH_AUTH_NO_CRKEY_TIMEOUT  0x0001 /* underlying cred has no key timeout */
 
@@ -170,7 +172,6 @@ void                        rpc_destroy_authunix(void);
 struct rpc_cred *      rpc_lookup_cred(void);
 struct rpc_cred *      rpc_lookup_cred_nonblock(void);
 struct rpc_cred *      rpc_lookup_generic_cred(struct auth_cred *, int, gfp_t);
-struct rpc_cred *      rpc_lookup_machine_cred(const char *service_name);
 int                    rpcauth_register(const struct rpc_authops *);
 int                    rpcauth_unregister(const struct rpc_authops *);
 struct rpc_auth *      rpcauth_create(const struct rpc_auth_create_args *,
index 73d5c4a870faee9188f0d3f8e218d585b1e17148..fc6dfbf77a9d24ceceef01fdf1586b466e23c2d4 100644 (file)
@@ -66,6 +66,7 @@ struct rpc_clnt {
        struct rpc_rtt          cl_rtt_default;
        struct rpc_timeout      cl_timeout_default;
        const struct rpc_program *cl_program;
+       const char *            cl_principal;   /* use for machine cred */
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
        struct dentry           *cl_debugfs;    /* debugfs directory */
 #endif
index 63e2d35c10d5b7aee2aab67359bd24d5c59bd512..9e709dcc8c3990b8f69338edd447445549f8c6af 100644 (file)
@@ -39,6 +39,20 @@ static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
 static LIST_HEAD(cred_unused);
 static unsigned long number_cred_unused;
 
+static struct rpc_cred machine_cred = {
+       .cr_count = REFCOUNT_INIT(1),
+};
+
+/*
+ * Return the machine_cred pointer to be used whenever
+ * the a generic machine credential is needed.
+ */
+struct rpc_cred *rpc_machine_cred(void)
+{
+       return &machine_cred;
+}
+EXPORT_SYMBOL_GPL(rpc_machine_cred);
+
 #define MAX_HASHTABLE_BITS (14)
 static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
 {
@@ -702,6 +716,22 @@ rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
        return ret;
 }
 
+static struct rpc_cred *
+rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
+{
+       struct rpc_auth *auth = task->tk_client->cl_auth;
+       struct auth_cred acred = {
+               .principal = task->tk_client->cl_principal,
+               .cred = init_task.cred,
+       };
+
+       if (!acred.principal)
+               return NULL;
+       dprintk("RPC: %5u looking up %s machine cred\n",
+               task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
+       return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
+}
+
 static struct rpc_cred *
 rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
 {
@@ -716,14 +746,20 @@ static int
 rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags)
 {
        struct rpc_rqst *req = task->tk_rqstp;
-       struct rpc_cred *new;
+       struct rpc_cred *new = NULL;
        int lookupflags = 0;
 
        if (flags & RPC_TASK_ASYNC)
                lookupflags |= RPCAUTH_LOOKUP_NEW;
-       if (cred != NULL)
+       if (cred != NULL && cred != &machine_cred)
                new = cred->cr_ops->crbind(task, cred, lookupflags);
-       else if (flags & RPC_TASK_ROOTCREDS)
+       else if (cred == &machine_cred)
+               new = rpcauth_bind_machine_cred(task, lookupflags);
+
+       /* If machine cred couldn't be bound, try a root cred */
+       if (new)
+               ;
+       else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
                new = rpcauth_bind_root_cred(task, lookupflags);
        else
                new = rpcauth_bind_new_cred(task, lookupflags);
index 7d1a8f45726ccbb7c7f32bbc01ecf73fc70c5011..5f7aa6324b78fc81a28aae1a9e04bc1d855ebd64 100644 (file)
@@ -48,27 +48,6 @@ struct rpc_cred *rpc_lookup_cred_nonblock(void)
 }
 EXPORT_SYMBOL_GPL(rpc_lookup_cred_nonblock);
 
-/*
- * Public call interface for looking up machine creds.
- * Note that if service_name is NULL, we actually look up
- * "root" credential.
- */
-struct rpc_cred *rpc_lookup_machine_cred(const char *service_name)
-{
-       struct auth_cred acred = {
-               .principal = service_name,
-               .cred = get_task_cred(&init_task),
-       };
-       struct rpc_cred *ret;
-
-       dprintk("RPC:       looking up machine cred for service %s\n",
-                       service_name);
-       ret = generic_auth.au_ops->lookup_cred(&generic_auth, &acred, 0);
-       put_cred(acred.cred);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(rpc_lookup_machine_cred);
-
 static struct rpc_cred *generic_bind_cred(struct rpc_task *task,
                struct rpc_cred *cred, int lookupflags)
 {
index 24cbddc44c884ce6fa9a4ba51a7e85c30953edd2..c5bf56abf26666d571545014d77de00077601f4d 100644 (file)
@@ -627,6 +627,7 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
        new->cl_noretranstimeo = clnt->cl_noretranstimeo;
        new->cl_discrtry = clnt->cl_discrtry;
        new->cl_chatty = clnt->cl_chatty;
+       new->cl_principal = clnt->cl_principal;
        return new;
 
 out_err: