From owner-svn-src-stable-11@freebsd.org  Sun Apr 21 00:51:48 2019
Return-Path: <owner-svn-src-stable-11@freebsd.org>
Delivered-To: svn-src-stable-11@mailman.ysv.freebsd.org
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1])
 by mailman.ysv.freebsd.org (Postfix) with ESMTP id 49C79157B475;
 Sun, 21 Apr 2019 00:51:48 +0000 (UTC)
 (envelope-from rmacklem@FreeBSD.org)
Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org
 [IPv6:2610:1c1:1:606c::19:3])
 (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
 server-signature RSA-PSS (4096 bits)
 client-signature RSA-PSS (4096 bits) client-digest SHA256)
 (Client CN "mxrelay.nyi.freebsd.org",
 Issuer "Let's Encrypt Authority X3" (verified OK))
 by mx1.freebsd.org (Postfix) with ESMTPS id DE8358310F;
 Sun, 21 Apr 2019 00:51:47 +0000 (UTC)
 (envelope-from rmacklem@FreeBSD.org)
Received: from repo.freebsd.org (repo.freebsd.org
 [IPv6:2610:1c1:1:6068::e6a:0])
 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
 (Client did not present a certificate)
 by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 9AA1D1DF9B;
 Sun, 21 Apr 2019 00:51:44 +0000 (UTC)
 (envelope-from rmacklem@FreeBSD.org)
Received: from repo.freebsd.org ([127.0.1.37])
 by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x3L0pijx035927;
 Sun, 21 Apr 2019 00:51:44 GMT (envelope-from rmacklem@FreeBSD.org)
Received: (from rmacklem@localhost)
 by repo.freebsd.org (8.15.2/8.15.2/Submit) id x3L0pi0T035926;
 Sun, 21 Apr 2019 00:51:44 GMT (envelope-from rmacklem@FreeBSD.org)
Message-Id: <201904210051.x3L0pi0T035926@repo.freebsd.org>
X-Authentication-Warning: repo.freebsd.org: rmacklem set sender to
 rmacklem@FreeBSD.org using -f
From: Rick Macklem <rmacklem@FreeBSD.org>
Date: Sun, 21 Apr 2019 00:51:44 +0000 (UTC)
To: src-committers@freebsd.org, svn-src-all@freebsd.org,
 svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject: svn commit: r346464 - stable/11/usr.sbin/nfsuserd
X-SVN-Group: stable-11
X-SVN-Commit-Author: rmacklem
X-SVN-Commit-Paths: stable/11/usr.sbin/nfsuserd
X-SVN-Commit-Revision: 346464
X-SVN-Commit-Repository: base
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Rspamd-Queue-Id: DE8358310F
X-Spamd-Bar: --
Authentication-Results: mx1.freebsd.org
X-Spamd-Result: default: False [-2.96 / 15.00];
 local_wl_from(0.00)[FreeBSD.org];
 NEURAL_HAM_MEDIUM(-1.00)[-0.999,0];
 NEURAL_HAM_SHORT(-0.96)[-0.965,0];
 ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US];
 NEURAL_HAM_LONG(-1.00)[-1.000,0]
X-BeenThere: svn-src-stable-11@freebsd.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: SVN commit messages for only the 11-stable src tree
 <svn-src-stable-11.freebsd.org>
List-Unsubscribe: <https://lists.freebsd.org/mailman/options/svn-src-stable-11>, 
 <mailto:svn-src-stable-11-request@freebsd.org?subject=unsubscribe>
List-Archive: <http://lists.freebsd.org/pipermail/svn-src-stable-11/>
List-Post: <mailto:svn-src-stable-11@freebsd.org>
List-Help: <mailto:svn-src-stable-11-request@freebsd.org?subject=help>
List-Subscribe: <https://lists.freebsd.org/mailman/listinfo/svn-src-stable-11>, 
 <mailto:svn-src-stable-11-request@freebsd.org?subject=subscribe>
X-List-Received-Date: Sun, 21 Apr 2019 00:51:48 -0000

Author: rmacklem
Date: Sun Apr 21 00:51:43 2019
New Revision: 346464
URL: https://svnweb.freebsd.org/changeset/base/346464

Log:
  MFC: r345994
  Fix nfsuserd so that it handles the mapped localhost address when jails
  are enabled.
  
  The nfsuserd(8) daemon does not function correctly when jails are enabled,
  since localhost gets mapped to another IP address and, as such, the upcall
  RPC fails.
  This patch fixes the problem by doing a getsockname(2) of a socket mapped
  to localhost to find out what the correct address is for the comparison
  test with the upcall's from IP address.
  This patch also adds INET6 support and the required #ifdef's for INET and
  INET6. It now uses INET6 by default for the upcalls, if the kernel has
  INET6 support and the daemon is also built with INET6 support.

Modified:
  stable/11/usr.sbin/nfsuserd/Makefile
  stable/11/usr.sbin/nfsuserd/nfsuserd.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/usr.sbin/nfsuserd/Makefile
==============================================================================
--- stable/11/usr.sbin/nfsuserd/Makefile	Sun Apr 21 00:41:06 2019	(r346463)
+++ stable/11/usr.sbin/nfsuserd/Makefile	Sun Apr 21 00:51:43 2019	(r346464)
@@ -1,7 +1,16 @@
 # $FreeBSD$
 
+.include <src.opts.mk>
+
 PROG=	nfsuserd
 MAN=	nfsuserd.8
 WARNS?=	3
+
+.if ${MK_INET_SUPPORT} != "no"
+CFLAGS+= -DINET
+.endif
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
 
 .include <bsd.prog.mk>

Modified: stable/11/usr.sbin/nfsuserd/nfsuserd.c
==============================================================================
--- stable/11/usr.sbin/nfsuserd/nfsuserd.c	Sun Apr 21 00:41:06 2019	(r346463)
+++ stable/11/usr.sbin/nfsuserd/nfsuserd.c	Sun Apr 21 00:51:43 2019	(r346464)
@@ -40,6 +40,10 @@ __FBSDID("$FreeBSD$");
 #include <sys/vnode.h>
 #include <sys/wait.h>
 
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
 #include <nfs/nfssvc.h>
 
 #include <rpc/rpc.h>
@@ -72,6 +76,7 @@ static void	nfsuserdsrv(struct svc_req *, SVCXPRT *);
 static bool_t	xdr_getid(XDR *, caddr_t);
 static bool_t	xdr_getname(XDR *, caddr_t);
 static bool_t	xdr_retval(XDR *, caddr_t);
+static int	nfsbind_localhost(void);
 
 #define	MAXNAME		1024
 #define	MAXNFSUSERD	20
@@ -94,6 +99,10 @@ gid_t defaultgid = 65533;
 int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0;
 int defusertimeout = DEFUSERTIMEOUT, manage_gids = 0;
 pid_t slaves[MAXNFSUSERD];
+static struct sockaddr_storage fromip;
+#ifdef INET6
+static struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT;
+#endif
 
 int
 main(int argc, char *argv[])
@@ -105,13 +114,20 @@ main(int argc, char *argv[])
 	struct group *grp;
 	int sock, one = 1;
 	SVCXPRT *udptransp;
-	u_short portnum;
+	struct nfsuserd_args nargs;
 	sigset_t signew;
 	char hostname[MAXHOSTNAMELEN + 1], *cp;
 	struct addrinfo *aip, hints;
 	static uid_t check_dups[MAXUSERMAX];
 	gid_t grps[NGROUPS];
 	int ngroup;
+#ifdef INET
+	struct sockaddr_in *sin;
+#endif
+#ifdef INET6
+	struct sockaddr_in6 *sin6;
+#endif
+	int s;
 
 	if (modfind("nfscommon") < 0) {
 		/* Not present in kernel, try loading it */
@@ -144,6 +160,37 @@ main(int argc, char *argv[])
 			}
 		}
 	}
+
+	/*
+	 * See if this server handles IPv4 or IPv6 and set up the default
+	 * localhost address.
+	 */
+	s = -1;
+#ifdef INET6
+	s = socket(PF_INET6, SOCK_DGRAM, 0);
+	if (s >= 0) {
+		fromip.ss_family = AF_INET6;
+		fromip.ss_len = sizeof(struct sockaddr_in6);
+		sin6 = (struct sockaddr_in6 *)&fromip;
+		sin6->sin6_addr = in6loopback;
+		close(s);
+	}
+#endif	/* INET6 */
+#ifdef INET
+	if (s < 0) {
+		s = socket(PF_INET, SOCK_DGRAM, 0);
+		if (s >= 0) {
+			fromip.ss_family = AF_INET;
+			fromip.ss_len = sizeof(struct sockaddr_in);
+			sin = (struct sockaddr_in *)&fromip;
+			sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+			close(s);
+		}
+	}
+#endif	/* INET */
+	if (s < 0)
+		err(1, "Can't create a inet/inet6 socket");
+
 	nid.nid_usermax = DEFUSERMAX;
 	nid.nid_usertimeout = defusertimeout;
 
@@ -245,11 +292,12 @@ main(int argc, char *argv[])
 	for (i = 0; i < nfsuserdcnt; i++)
 		slaves[i] = (pid_t)-1;
 
+	nargs.nuserd_family = fromip.ss_family;
 	/*
 	 * Set up the service port to accept requests via UDP from
-	 * localhost (127.0.0.1).
+	 * localhost (INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT).
 	 */
-	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+	if ((sock = socket(nargs.nuserd_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 		err(1, "cannot create udp socket");
 
 	/*
@@ -272,11 +320,11 @@ main(int argc, char *argv[])
 	/*
 	 * Tell the kernel what my port# is.
 	 */
-	portnum = htons(udptransp->xp_port);
+	nargs.nuserd_port = htons(udptransp->xp_port);
 #ifdef DEBUG
-	printf("portnum=0x%x\n", portnum);
+	printf("portnum=0x%x\n", nargs.nuserd_port);
 #else
-	if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) {
+	if (nfssvc(NFSSVC_NFSUSERDPORT | NFSSVC_NEWSTRUCT, &nargs) < 0) {
 		if (errno == EPERM) {
 			fprintf(stderr,
 			    "Can't start nfsuserd when already running");
@@ -457,27 +505,92 @@ nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
 	struct passwd *pwd;
 	struct group *grp;
 	int error;
+#if defined(INET) || defined(INET6)
 	u_short sport;
+	int ret;
+#endif
 	struct info info;
 	struct nfsd_idargs nid;
-	u_int32_t saddr;
 	gid_t grps[NGROUPS];
 	int ngroup;
+#ifdef INET
+	struct sockaddr_in *fromsin, *sin;
+#endif
+#ifdef INET6
+	struct sockaddr_in6 *fromsin6, *sin6;
+	char buf[INET6_ADDRSTRLEN];
+#endif
 
 	/*
-	 * Only handle requests from 127.0.0.1 on a reserved port number.
+	 * Only handle requests from localhost on a reserved port number.
+	 * If the upcall is from a different address, call nfsbind_localhost()
+	 * to check for a remapping of localhost, due to jails.
 	 * (Since a reserved port # at localhost implies a client with
 	 *  local root, there won't be a security breach. 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);
-	if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) ||
-	    saddr != 0x7f000001) {
-		syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport);
-		svcerr_weakauth(transp);
-		return;
+	if (rqstp->rq_proc != NULLPROC) {
+		switch (fromip.ss_family) {
+#ifdef INET
+		case AF_INET:
+			if (transp->xp_rtaddr.len < sizeof(*sin)) {
+				syslog(LOG_ERR, "xp_rtaddr too small");
+				svcerr_weakauth(transp);
+				return;
+			}
+			sin = (struct sockaddr_in *)transp->xp_rtaddr.buf;
+			fromsin = (struct sockaddr_in *)&fromip;
+			sport = ntohs(sin->sin_port);
+			if (sport >= IPPORT_RESERVED) {
+				syslog(LOG_ERR, "not a reserved port#");
+				svcerr_weakauth(transp);
+				return;
+			}
+			ret = 1;
+			if (sin->sin_addr.s_addr != fromsin->sin_addr.s_addr)
+				ret = nfsbind_localhost();
+			if (ret == 0 || sin->sin_addr.s_addr !=
+			    fromsin->sin_addr.s_addr) {
+				syslog(LOG_ERR, "bad from ip %s",
+				    inet_ntoa(sin->sin_addr));
+				svcerr_weakauth(transp);
+				return;
+			}
+			break;
+#endif	/* INET */
+#ifdef INET6
+		case AF_INET6:
+			if (transp->xp_rtaddr.len < sizeof(*sin6)) {
+				syslog(LOG_ERR, "xp_rtaddr too small");
+				svcerr_weakauth(transp);
+				return;
+			}
+			sin6 = (struct sockaddr_in6 *)transp->xp_rtaddr.buf;
+			fromsin6 = (struct sockaddr_in6 *)&fromip;
+			sport = ntohs(sin6->sin6_port);
+			if (sport >= IPV6PORT_RESERVED) {
+				syslog(LOG_ERR, "not a reserved port#");
+				svcerr_weakauth(transp);
+				return;
+			}
+			ret = 1;
+			if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+			    &fromsin6->sin6_addr))
+				ret = nfsbind_localhost();
+			if (ret == 0 || !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+			    &fromsin6->sin6_addr)) {
+				if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf,
+				    INET6_ADDRSTRLEN) != NULL)
+					syslog(LOG_ERR, "bad from ip %s", buf);
+				else
+					syslog(LOG_ERR, "bad from ip6 addr");
+				svcerr_weakauth(transp);
+				return;
+			}
+			break;
+#endif	/* INET6 */
+		}
 	}
 	switch (rqstp->rq_proc) {
 	case NULLPROC:
@@ -716,6 +829,67 @@ cleanup_term(int signo __unused)
 		exit(1);
 	}
 	exit(0);
+}
+
+/*
+ * Get the IP address that the localhost address maps to.
+ * This is needed when jails map localhost to another IP address.
+ */
+static int
+nfsbind_localhost(void)
+{
+#ifdef INET
+	struct sockaddr_in sin;
+#endif
+#ifdef INET6
+	struct sockaddr_in6 sin6;
+#endif
+	socklen_t slen;
+	int ret, s;
+
+	switch (fromip.ss_family) {
+#ifdef INET6
+	case AF_INET6:
+		s = socket(PF_INET6, SOCK_DGRAM, 0);
+		if (s < 0)
+			return (0);
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_len = sizeof(sin6);
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_addr = in6loopback;
+		sin6.sin6_port = 0;
+		ret = bind(s, (struct sockaddr *)&sin6, sizeof(sin6));
+		if (ret < 0) {
+			close(s);
+			return (0);
+		}
+		break;
+#endif	/* INET6 */
+#ifdef INET
+	case AF_INET:
+		s = socket(PF_INET, SOCK_DGRAM, 0);
+		if (s < 0)
+			return (0);
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_len = sizeof(sin);
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+		sin.sin_port = 0;
+		ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
+		if (ret < 0) {
+			close(s);
+			return (0);
+		}
+		break;
+#endif	/* INET */
+	}
+	memset(&fromip, 0, sizeof(fromip));
+	slen = sizeof(fromip);
+	ret = getsockname(s, (struct sockaddr *)&fromip, &slen);
+	close(s);
+	if (ret < 0)
+		return (0);
+	return (1);
 }
 
 static void