SUNRPC: Cache the process user cred in the RPC server listener
authorTrond Myklebust <trondmy@gmail.com>
Tue, 9 Apr 2019 16:13:37 +0000 (12:13 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Wed, 24 Apr 2019 13:46:35 +0000 (09:46 -0400)
In order to be able to interpret uids and gids correctly in knfsd, we
should cache the user namespace of the process that created the RPC
server's listener. To do so, we refcount the credential of that process.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/lockd/svc.c
fs/nfs/callback.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsd.h
fs/nfsd/nfssvc.c
include/linux/sunrpc/svc_xprt.h
include/linux/sunrpc/svcsock.h
net/sunrpc/svc_xprt.c
net/sunrpc/svcsock.c

index 96bb74c..82aa0f3 100644 (file)
@@ -190,12 +190,13 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
                                 struct net *net, const int family,
                                 const unsigned short port)
 {
+       const struct cred *cred = current_cred();
        struct svc_xprt *xprt;
 
        xprt = svc_find_xprt(serv, name, net, family, 0);
        if (xprt == NULL)
                return svc_create_xprt(serv, name, net, family, port,
-                                               SVC_SOCK_DEFAULTS);
+                                               SVC_SOCK_DEFAULTS, cred);
        svc_xprt_put(xprt);
        return 0;
 }
index 15c9575..7817ad9 100644 (file)
@@ -41,11 +41,13 @@ static struct svc_program nfs4_callback_program;
 
 static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
 {
+       const struct cred *cred = current_cred();
        int ret;
        struct nfs_net *nn = net_generic(net, nfs_net_id);
 
        ret = svc_create_xprt(serv, "tcp", net, PF_INET,
-                               nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
+                               nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
+                               cred);
        if (ret <= 0)
                goto out_err;
        nn->nfs_callback_tcpport = ret;
@@ -53,7 +55,8 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
                nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
 
        ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
-                               nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
+                               nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
+                               cred);
        if (ret > 0) {
                nn->nfs_callback_tcpport6 = ret;
                dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
index 630d629..90972e1 100644 (file)
@@ -439,7 +439,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
                        return rv;
                if (newthreads < 0)
                        return -EINVAL;
-               rv = nfsd_svc(newthreads, net);
+               rv = nfsd_svc(newthreads, net, file->f_cred);
                if (rv < 0)
                        return rv;
        } else
@@ -717,7 +717,7 @@ static ssize_t __write_ports_names(char *buf, struct net *net)
  * a socket of a supported family/protocol, and we use it as an
  * nfsd listener.
  */
-static ssize_t __write_ports_addfd(char *buf, struct net *net)
+static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred)
 {
        char *mesg = buf;
        int fd, err;
@@ -736,7 +736,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net)
        if (err != 0)
                return err;
 
-       err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
+       err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
        if (err < 0) {
                nfsd_destroy(net);
                return err;
@@ -751,7 +751,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net)
  * A transport listener is added by writing it's transport name and
  * a port number.
  */
-static ssize_t __write_ports_addxprt(char *buf, struct net *net)
+static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred)
 {
        char transport[16];
        struct svc_xprt *xprt;
@@ -769,12 +769,12 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net)
                return err;
 
        err = svc_create_xprt(nn->nfsd_serv, transport, net,
-                               PF_INET, port, SVC_SOCK_ANONYMOUS);
+                               PF_INET, port, SVC_SOCK_ANONYMOUS, cred);
        if (err < 0)
                goto out_err;
 
        err = svc_create_xprt(nn->nfsd_serv, transport, net,
-                               PF_INET6, port, SVC_SOCK_ANONYMOUS);
+                               PF_INET6, port, SVC_SOCK_ANONYMOUS, cred);
        if (err < 0 && err != -EAFNOSUPPORT)
                goto out_close;
 
@@ -799,10 +799,10 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size,
                return __write_ports_names(buf, net);
 
        if (isdigit(buf[0]))
-               return __write_ports_addfd(buf, net);
+               return __write_ports_addfd(buf, net, file->f_cred);
 
        if (isalpha(buf[0]))
-               return __write_ports_addxprt(buf, net);
+               return __write_ports_addxprt(buf, net, file->f_cred);
 
        return -EINVAL;
 }
index 6bae255..d200c86 100644 (file)
@@ -73,7 +73,7 @@ extern const struct seq_operations nfs_exports_op;
 /*
  * Function prototypes.
  */
-int            nfsd_svc(int nrservs, struct net *net);
+int            nfsd_svc(int nrservs, struct net *net, const struct cred *cred);
 int            nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp);
 
 int            nfsd_nrthreads(struct net *);
index 5207577..32e4d8a 100644 (file)
@@ -283,7 +283,7 @@ int nfsd_nrthreads(struct net *net)
        return rv;
 }
 
-static int nfsd_init_socks(struct net *net)
+static int nfsd_init_socks(struct net *net, const struct cred *cred)
 {
        int error;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@@ -292,12 +292,12 @@ static int nfsd_init_socks(struct net *net)
                return 0;
 
        error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
-                                       SVC_SOCK_DEFAULTS);
+                                       SVC_SOCK_DEFAULTS, cred);
        if (error < 0)
                return error;
 
        error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
-                                       SVC_SOCK_DEFAULTS);
+                                       SVC_SOCK_DEFAULTS, cred);
        if (error < 0)
                return error;
 
@@ -348,7 +348,7 @@ static bool nfsd_needs_lockd(struct nfsd_net *nn)
        return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
 }
 
-static int nfsd_startup_net(int nrservs, struct net *net)
+static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cred)
 {
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
        int ret;
@@ -359,7 +359,7 @@ static int nfsd_startup_net(int nrservs, struct net *net)
        ret = nfsd_startup_generic(nrservs);
        if (ret)
                return ret;
-       ret = nfsd_init_socks(net);
+       ret = nfsd_init_socks(net, cred);
        if (ret)
                goto out_socks;
 
@@ -697,7 +697,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
  * this is the first time nrservs is nonzero.
  */
 int
-nfsd_svc(int nrservs, struct net *net)
+nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
 {
        int     error;
        bool    nfsd_up_before;
@@ -719,7 +719,7 @@ nfsd_svc(int nrservs, struct net *net)
 
        nfsd_up_before = nn->nfsd_net_up;
 
-       error = nfsd_startup_net(nrservs, net);
+       error = nfsd_startup_net(nrservs, net, cred);
        if (error)
                goto out_destroy;
        error = nn->nfsd_serv->sv_ops->svo_setup(nn->nfsd_serv,
index b3f9577..ea6f46b 100644 (file)
@@ -86,6 +86,7 @@ struct svc_xprt {
        struct list_head        xpt_users;      /* callbacks on free */
 
        struct net              *xpt_net;
+       const struct cred       *xpt_cred;
        struct rpc_xprt         *xpt_bc_xprt;   /* NFSv4.1 backchannel */
        struct rpc_xprt_switch  *xpt_bc_xps;    /* NFSv4.1 backchannel */
 };
@@ -119,7 +120,8 @@ void        svc_unreg_xprt_class(struct svc_xprt_class *);
 void   svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *,
                      struct svc_serv *);
 int    svc_create_xprt(struct svc_serv *, const char *, struct net *,
-                       const int, const unsigned short, int);
+                       const int, const unsigned short, int,
+                       const struct cred *);
 void   svc_xprt_do_enqueue(struct svc_xprt *xprt);
 void   svc_xprt_enqueue(struct svc_xprt *xprt);
 void   svc_xprt_put(struct svc_xprt *xprt);
index 119718a..771baad 100644 (file)
@@ -59,7 +59,8 @@ void          svc_drop(struct svc_rqst *);
 void           svc_sock_update_bufs(struct svc_serv *serv);
 bool           svc_alien_sock(struct net *net, int fd);
 int            svc_addsock(struct svc_serv *serv, const int fd,
-                                       char *name_return, const size_t len);
+                                       char *name_return, const size_t len,
+                                       const struct cred *cred);
 void           svc_init_xprt_sock(void);
 void           svc_cleanup_xprt_sock(void);
 struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot);
index 61530b1..b76ee83 100644 (file)
@@ -136,6 +136,7 @@ static void svc_xprt_free(struct kref *kref)
        struct module *owner = xprt->xpt_class->xcl_owner;
        if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
                svcauth_unix_info_release(xprt);
+       put_cred(xprt->xpt_cred);
        put_net(xprt->xpt_net);
        /* See comment on corresponding get in xs_setup_bc_tcp(): */
        if (xprt->xpt_bc_xprt)
@@ -252,7 +253,8 @@ void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *new)
 
 static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
                            struct net *net, const int family,
-                           const unsigned short port, int flags)
+                           const unsigned short port, int flags,
+                           const struct cred *cred)
 {
        struct svc_xprt_class *xcl;
 
@@ -273,6 +275,7 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
                        module_put(xcl->xcl_owner);
                        return PTR_ERR(newxprt);
                }
+               newxprt->xpt_cred = get_cred(cred);
                svc_add_new_perm_xprt(serv, newxprt);
                newport = svc_xprt_local_port(newxprt);
                return newport;
@@ -286,15 +289,16 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
 
 int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
                    struct net *net, const int family,
-                   const unsigned short port, int flags)
+                   const unsigned short port, int flags,
+                   const struct cred *cred)
 {
        int err;
 
        dprintk("svc: creating transport %s[%d]\n", xprt_name, port);
-       err = _svc_create_xprt(serv, xprt_name, net, family, port, flags);
+       err = _svc_create_xprt(serv, xprt_name, net, family, port, flags, cred);
        if (err == -EPROTONOSUPPORT) {
                request_module("svc%s", xprt_name);
-               err = _svc_create_xprt(serv, xprt_name, net, family, port, flags);
+               err = _svc_create_xprt(serv, xprt_name, net, family, port, flags, cred);
        }
        if (err < 0)
                dprintk("svc: transport %s not found, err %d\n",
index 43590a9..540fde2 100644 (file)
@@ -1332,13 +1332,14 @@ EXPORT_SYMBOL_GPL(svc_alien_sock);
  * @fd: file descriptor of the new listener
  * @name_return: pointer to buffer to fill in with name of listener
  * @len: size of the buffer
+ * @cred: credential
  *
  * Fills in socket name and returns positive length of name if successful.
  * Name is terminated with '\n'.  On error, returns a negative errno
  * value.
  */
 int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
-               const size_t len)
+               const size_t len, const struct cred *cred)
 {
        int err = 0;
        struct socket *so = sockfd_lookup(fd, &err);
@@ -1371,6 +1372,7 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
        salen = kernel_getsockname(svsk->sk_sock, sin);
        if (salen >= 0)
                svc_xprt_set_local(&svsk->sk_xprt, sin, salen);
+       svsk->sk_xprt.xpt_cred = get_cred(cred);
        svc_add_new_perm_xprt(serv, &svsk->sk_xprt);
        return svc_one_sock_name(svsk, name_return, len);
 out: