Modify the nfsuserd daemon so that it uses an AF_LOCAL socket for upcalls.

This patch modifies the nfsuserd daemon so that it uses an AF_LOCAL socket
for upcalls by default. This should fix the problem with using a UDP
socket upcall to 127.0.0.1 when jails are used.
The AF_LOCAL socket case only supports a single server daemon, since hangs
were observed by the original problem reporter when multiple daemons
were used.
The patch adds a command line option called "-use-udpsock" which makes
the daemon revert to its prepatched behaviour.

Suggested by:	dfr
PR:		205193
Relnotes:	yes
This commit is contained in:
Rick Macklem 2017-07-06 22:04:37 +00:00
parent 1550c62236
commit 77193c0508
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=320757

View File

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/socketvar.h> #include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/ucred.h> #include <sys/ucred.h>
#include <sys/vnode.h> #include <sys/vnode.h>
@ -43,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <nfs/nfssvc.h> #include <nfs/nfssvc.h>
#include <rpc/rpc.h> #include <rpc/rpc.h>
#include <rpc/rpc_com.h>
#include <fs/nfs/rpcv2.h> #include <fs/nfs/rpcv2.h>
#include <fs/nfs/nfsproto.h> #include <fs/nfs/nfsproto.h>
@ -73,6 +75,9 @@ static bool_t xdr_getid(XDR *, caddr_t);
static bool_t xdr_getname(XDR *, caddr_t); static bool_t xdr_getname(XDR *, caddr_t);
static bool_t xdr_retval(XDR *, caddr_t); static bool_t xdr_retval(XDR *, caddr_t);
#ifndef _PATH_NFSUSERDSOCK
#define _PATH_NFSUSERDSOCK "/var/run/nfsuserd.sock"
#endif
#define MAXNAME 1024 #define MAXNAME 1024
#define MAXNFSUSERD 20 #define MAXNFSUSERD 20
#define DEFNFSUSERD 4 #define DEFNFSUSERD 4
@ -92,6 +97,7 @@ uid_t defaultuid = 65534;
u_char *defaultgroup = "nogroup"; u_char *defaultgroup = "nogroup";
gid_t defaultgid = 65533; gid_t defaultgid = 65533;
int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0; int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0;
int use_udpsock = 0;
int defusertimeout = DEFUSERTIMEOUT, manage_gids = 0; int defusertimeout = DEFUSERTIMEOUT, manage_gids = 0;
pid_t slaves[MAXNFSUSERD]; pid_t slaves[MAXNFSUSERD];
@ -103,15 +109,17 @@ main(int argc, char *argv[])
struct nfsd_idargs nid; struct nfsd_idargs nid;
struct passwd *pwd; struct passwd *pwd;
struct group *grp; struct group *grp;
int sock, one = 1; int oldmask, one = 1, sock;
SVCXPRT *udptransp; SVCXPRT *udptransp;
u_short portnum; u_short portnum;
SVCXPRT *xprt;
sigset_t signew; sigset_t signew;
char hostname[MAXHOSTNAMELEN + 1], *cp; char hostname[MAXHOSTNAMELEN + 1], *cp;
struct addrinfo *aip, hints; struct addrinfo *aip, hints;
static uid_t check_dups[MAXUSERMAX]; static uid_t check_dups[MAXUSERMAX];
gid_t grps[NGROUPS]; gid_t grps[NGROUPS];
int ngroup; int ngroup;
struct sockaddr_un sun;
if (modfind("nfscommon") < 0) { if (modfind("nfscommon") < 0) {
/* Not present in kernel, try loading it */ /* Not present in kernel, try loading it */
@ -164,6 +172,8 @@ main(int argc, char *argv[])
forcestart = 1; forcestart = 1;
} else if (!strcmp(*argv, "-manage-gids")) { } else if (!strcmp(*argv, "-manage-gids")) {
manage_gids = 1; manage_gids = 1;
} else if (!strcmp(*argv, "-use-udpsock")) {
use_udpsock = 1;
} else if (!strcmp(*argv, "-usermax")) { } else if (!strcmp(*argv, "-usermax")) {
if (argc == 1) if (argc == 1)
usage(); usage();
@ -207,6 +217,9 @@ main(int argc, char *argv[])
} }
if (nfsuserdcnt < 1) if (nfsuserdcnt < 1)
nfsuserdcnt = DEFNFSUSERD; nfsuserdcnt = DEFNFSUSERD;
if (use_udpsock == 0)
/* For AF_LOCAL socket, only allow one server daemon. */
nfsuserdcnt = 1;
/* /*
* Strip off leading and trailing '.'s in domain name and map * Strip off leading and trailing '.'s in domain name and map
@ -245,49 +258,93 @@ main(int argc, char *argv[])
for (i = 0; i < nfsuserdcnt; i++) for (i = 0; i < nfsuserdcnt; i++)
slaves[i] = (pid_t)-1; slaves[i] = (pid_t)-1;
/* if (use_udpsock != 0) {
* Set up the service port to accept requests via UDP from /*
* localhost (127.0.0.1). * Set up the service port to accept requests via UDP from
*/ * localhost (127.0.0.1).
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) */
err(1, "cannot create udp socket"); if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
err(1, "cannot create udp socket");
/*
* Not sure what this does, so I'll leave it here for now.
*/
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if ((udptransp = svcudp_create(sock)) == NULL) /*
err(1, "Can't set up socket"); * Not sure what this does, so I'll leave it here for now.
*/
/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
* By not specifying a protocol, it is linked into the
* dispatch queue, but not registered with portmapper, if ((udptransp = svcudp_create(sock)) == NULL)
* which is just what I want. err(1, "Can't set up socket");
*/
if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, /*
nfsuserdsrv, 0)) * By not specifying a protocol, it is linked into the
err(1, "Can't register nfsuserd"); * dispatch queue, but not registered with portmapper,
* which is just what I want.
/* */
* Tell the kernel what my port# is. if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
*/ nfsuserdsrv, 0))
portnum = htons(udptransp->xp_port); err(1, "Can't register nfsuserd");
/*
* Tell the kernel what my port# is.
*/
portnum = htons(udptransp->xp_port);
#ifdef DEBUG #ifdef DEBUG
printf("portnum=0x%x\n", portnum); printf("portnum=0x%x\n", portnum);
#else #else
if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) { if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) {
if (errno == EPERM) { if (errno == EPERM)
fprintf(stderr, fprintf(stderr, "Can't start nfsuserd when"
"Can't start nfsuserd when already running"); " already running\nIf not running,"
fprintf(stderr, " use the -force option.\n");
" If not running, use the -force option.\n"); else
} else { fprintf(stderr,
fprintf(stderr, "Can't do nfssvc() to add port\n"); "Can't do nfssvc() to add socket\n");
exit(1);
} }
exit(1);
}
#endif #endif
} else {
/* Use the AF_LOCAL socket. */
memset(&sun, 0, sizeof sun);
sun.sun_family = AF_LOCAL;
unlink(_PATH_NFSUSERDSOCK);
strcpy(sun.sun_path, _PATH_NFSUSERDSOCK);
sun.sun_len = SUN_LEN(&sun);
sock = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sock < 0)
err(1, "Can't create local nfsuserd socket");
oldmask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
if (bind(sock, (struct sockaddr *)&sun, sun.sun_len) < 0)
err(1, "Can't bind local nfsuserd socket");
umask(oldmask);
if (listen(sock, SOMAXCONN) < 0)
err(1, "Can't listen on local nfsuserd socket");
xprt = svc_vc_create(sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
if (xprt == NULL)
err(1,
"Can't create transport for local nfsuserd socket");
if (!svc_reg(xprt, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
nfsuserdsrv, NULL))
err(1,
"Can't register service for local nfsuserd socket");
/*
* Tell the kernel what the socket's path is.
*/
#ifdef DEBUG
printf("sockpath=%s\n", _PATH_NFSUSERDSOCK);
#else
if (nfssvc(NFSSVC_NFSUSERDPORT | NFSSVC_NEWSTRUCT,
_PATH_NFSUSERDSOCK) < 0) {
if (errno == EPERM)
fprintf(stderr, "Can't start nfsuserd when"
" already running\nIf not running,"
" use the -force option.\n");
else
fprintf(stderr,
"Can't do nfssvc() to add socket\n");
exit(1);
}
#endif
}
pwd = getpwnam(defaultuser); pwd = getpwnam(defaultuser);
if (pwd) if (pwd)
@ -462,21 +519,25 @@ nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
gid_t grps[NGROUPS]; gid_t grps[NGROUPS];
int ngroup; int ngroup;
/* if (use_udpsock != 0) {
* Only handle requests from 127.0.0.1 on a reserved port number. /*
* (Since a reserved port # at localhost implies a client with * Only handle requests from 127.0.0.1 on a reserved port
* local root, there won't be a security breach. This is about * number. (Since a reserved port # at localhost implies a
* the only case I can think of where a reserved port # means * client with local root, there won't be a security breach.
* something.) * This is about the only case I can think of where a reserved
*/ * port # means something.)
sport = ntohs(transp->xp_raddr.sin_port); */
saddr = ntohl(transp->xp_raddr.sin_addr.s_addr); sport = ntohs(transp->xp_raddr.sin_port);
if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) || saddr = ntohl(transp->xp_raddr.sin_addr.s_addr);
saddr != 0x7f000001) { if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) ||
syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport); saddr != 0x7f000001) {
svcerr_weakauth(transp); syslog(LOG_ERR, "req from ip=0x%x port=%d, consider"
return; " using an AF_LOCAL socket\n", saddr, sport);
svcerr_weakauth(transp);
return;
}
} }
switch (rqstp->rq_proc) { switch (rqstp->rq_proc) {
case NULLPROC: case NULLPROC:
if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
@ -720,6 +781,7 @@ static void
usage(void) usage(void)
{ {
errx(1, errx(1, "usage: nfsuserd [-usermax cache_size] [-usertimeout minutes]"
"usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-manage-gids] [-domain domain_name] [n]"); " [-verbose] [-manage-gids] [-use-udpsock] [-domain domain_name]"
" [n]");
} }