Skip to content

Commit

Permalink
SUNRPC: Cache the process user cred in the RPC server listener
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
  • Loading branch information
trondmy authored and J. Bruce Fields committed Apr 24, 2019
1 parent e333f3b commit 4df493a
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 26 deletions.
3 changes: 2 additions & 1 deletion fs/lockd/svc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
7 changes: 5 additions & 2 deletions fs/nfs/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,22 @@ 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;
dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
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",
Expand Down
16 changes: 8 additions & 8 deletions fs/nfsd/nfsctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion fs/nfsd/nfsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
14 changes: 7 additions & 7 deletions fs/nfsd/nfssvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion include/linux/sunrpc/svc_xprt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion include/linux/sunrpc/svcsock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 8 additions & 4 deletions net/sunrpc/svc_xprt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion net/sunrpc/svcsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 4df493a

Please sign in to comment.