Subject: bin/30772: Enable mountd(8) to bind to a fixed, user-given port
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <netbsd@wolfnode.de>
List: netbsd-bugs
Date: 07/17/2005 23:36:00
>Number:         30772
>Category:       bin
>Synopsis:       Enable mountd(8) to bind to a fixed, user-given port
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 17 23:36:00 +0000 2005
>Originator:     Florian Stoehr
>Release:        HEAD
>Organization:
>Environment:
-
>Description:
By default, mountd(8) binds to any port in the range 600-1023 which is horrible if one wants to enable NFS through a firewall.

This patch introduces a "-p <port>" to mountd, forcing it to bind to the specified port.

The patch is for IPv4 and IPv6. I've tested this on IPv4 only, so someone MUST LOOK AT THE CODE for IPv6. In particular, I don't known whether letting sin6->flow be zero is OK or not.

Patches for mountd.c as well as mountd.8 appended.
>How-To-Repeat:

>Fix:
--- src/usr.sbin/mountd/mountd.8.orig	2005-07-18 01:16:03.000000000 +0200
+++ src/usr.sbin/mountd/mountd.8	2005-07-18 01:16:21.000000000 +0200
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)mountd.8	8.4 (Berkeley) 4/28/95
 .\"
-.Dd January 14, 2005
+.Dd July 17, 2005
 .Dt MOUNTD 8
 .Os
 .Sh NAME
@@ -40,6 +40,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl dNn
+.Op Fl p Ar port
 .Op Fl P Ar policy
 .Op Ar exportsfile
 .Sh DESCRIPTION
@@ -78,11 +79,15 @@
 is only provided for backwards compatibility. Requests
 are checked for reserved ports on a per-export basis, see
 .Xr exports 5 .
-.It Ar exportsfile
-The
-.Ar exportsfile
-argument specifies an alternative location
-for the exports file.
+.It Fl p Ar port
+Force
+.Nm
+to bind to the given port. If this
+option is not given, 
+.Nm
+may bind to every anonymous port
+(in the range 600-1023) which causes trouble when trying to use
+NFS through a firewall.
 .It Fl P Ar policy
 IPsec
 .Ar policy
@@ -94,6 +99,11 @@
 the last string will take effect. If an invalid IPsec policy string is used
 .Nm
 logs an error message and terminates itself.
+.It Ar exportsfile
+The
+.Ar exportsfile
+argument specifies an alternative location
+for the exports file.
 .El
 .Pp
 When
--- src/usr.sbin/mountd/mountd.c.orig	2005-06-04 01:22:36.000000000 +0200
+++ src/usr.sbin/mountd/mountd.c	2005-07-17 04:40:57.000000000 +0200
@@ -265,6 +265,7 @@
 #define OP_MASKLEN	0x200
 
 static int      debug = 0;
+static int      forcedport = 0;
 #if 0
 static void SYSLOG __P((int, const char *,...));
 #endif
@@ -291,10 +292,13 @@
 {
 	SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
 	struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
+	struct sockaddr_in *udpforcedsa = NULL, *tcpforcedsa = NULL;
+	struct sockaddr_in6 *udpforcedsa6 = NULL, *tcpforcedsa6 = NULL;
 	int udpsock, tcpsock, udp6sock, tcp6sock;
 	int xcreated = 0, s;
 	int c, one = 1;
 	int maxrec = RPC_MAXDATASIZE;
+	extern char* optarg;
 #ifdef IPSEC
 	char *policy = NULL;
 #define ADDOPTS "P:"
@@ -302,7 +306,7 @@
 #define ADDOPTS
 #endif
 
-	while ((c = getopt(argc, argv, "dNnr" ADDOPTS)) != -1)
+	while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
 		switch (c) {
 #ifdef IPSEC
 		case 'P':
@@ -320,8 +324,12 @@
 		case 'n':
 		case 'r':
 			break;
+		case 'p':
+			/* A forced port "0" will dynamically allocate a port */
+			forcedport = atoi(optarg);
+			break;
 		default:
-			fprintf(stderr, "usage: %s [-dNn]"
+			fprintf(stderr, "usage: %s [-dNn] [-p port]"
 #ifdef IPSEC
 			    " [-P ipsec policy]"
 #endif
@@ -394,7 +402,25 @@
 	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
 
 	if (udpsock != -1 && udpconf != NULL) {
-		bindresvport(udpsock, NULL);
+		if (forcedport == 0) {
+			/* Dynamically allocate the port */
+			bindresvport(udpsock, NULL);
+		} else {
+			/* Force the port */
+			udpforcedsa = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
+			memset(udpforcedsa, 0, sizeof(struct sockaddr_in));
+			udpforcedsa->sin_len = sizeof(struct sockaddr_in);
+			udpforcedsa->sin_family = AF_INET;
+			udpforcedsa->sin_port = htons(forcedport);
+			
+			if (bindresvport(udpsock, udpforcedsa) == 0) {
+				if (debug)
+					(void)fprintf(stderr, "Forced udp port %d opened successfully\n", forcedport);
+			} else {
+				if (debug)
+					(void)fprintf(stderr, "Could not open forced udp port %d\n", forcedport);
+			}
+		}
 #ifdef IPSEC
 		if (policy)
 			ipsecsetup(AF_INET, udpsock, policy);
@@ -414,7 +440,25 @@
 	}
 
 	if (tcpsock != -1 && tcpconf != NULL) {
-		bindresvport(tcpsock, NULL);
+		if (forcedport == 0) {
+			/* Dynamically allocate the port */
+			bindresvport(tcpsock, NULL);
+		} else {
+			/* Force the port */
+			tcpforcedsa = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
+			memset(tcpforcedsa, 0, sizeof(struct sockaddr_in));
+			tcpforcedsa->sin_len = sizeof(struct sockaddr_in);
+			tcpforcedsa->sin_family = AF_INET;
+			tcpforcedsa->sin_port = htons(forcedport);
+			
+			if (bindresvport(tcpsock, tcpforcedsa) == 0) {
+				if (debug)
+					(void)fprintf(stderr, "Forced tcp port %d opened successfully\n", forcedport);
+			} else {
+				if (debug)
+					(void)fprintf(stderr, "Could not open forced tcp port %d\n", forcedport);
+			}
+		}
 #ifdef IPSEC
 		if (policy)
 			ipsecsetup(AF_INET, tcpsock, policy);
@@ -436,7 +480,25 @@
 	}
 
 	if (udp6sock != -1 && udp6conf != NULL) {
-		bindresvport(udp6sock, NULL);
+		if (forcedport == 0) {
+			/* Dynamically allocate the port */
+			bindresvport(udp6sock, NULL);
+		} else {
+			/* Force the port */
+			udpforcedsa6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
+			memset(udpforcedsa6, 0, sizeof(struct sockaddr_in6));
+			udpforcedsa6->sin6_len = sizeof(struct sockaddr_in6);
+			udpforcedsa6->sin6_family = AF_INET6;
+			udpforcedsa6->sin6_port = htons(forcedport);
+			
+			if (bindresvport_sa(udp6sock, (struct sockaddr *)udpforcedsa6) == 0) {
+				if (debug)
+					(void)fprintf(stderr, "Forced udp6 port %d opened successfully\n", forcedport);
+			} else {
+				if (debug)
+					(void)fprintf(stderr, "Could not open forced udp6 port %d\n", forcedport);
+			}
+		}
 #ifdef IPSEC
 		if (policy)
 			ipsecsetup(AF_INET6, tcpsock, policy);
@@ -456,7 +518,25 @@
 	}
 
 	if (tcp6sock != -1 && tcp6conf != NULL) {
-		bindresvport(tcp6sock, NULL);
+		if (forcedport == 0) {
+			/* Dynamically allocate the port */
+			bindresvport(tcp6sock, NULL);
+		} else {
+			/* Force the port */
+			tcpforcedsa6 = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
+			memset(tcpforcedsa6, 0, sizeof(struct sockaddr_in6));
+			tcpforcedsa6->sin6_len = sizeof(struct sockaddr_in6);
+			tcpforcedsa6->sin6_family = AF_INET6;
+			tcpforcedsa6->sin6_port = htons(forcedport);
+			
+			if (bindresvport_sa(tcp6sock, (struct sockaddr *)tcpforcedsa6) == 0) {
+				if (debug)
+					(void)fprintf(stderr, "Forced tcp6 port %d opened successfully\n", forcedport);
+			} else {
+				if (debug)
+					(void)fprintf(stderr, "Could not open forced tcp6 port %d\n", forcedport);
+			}
+		}
 #ifdef IPSEC
 		if (policy)
 			ipsecsetup(AF_INET6, tcpsock, policy);
@@ -486,6 +566,19 @@
 	kuidinit();
 #endif
 	svc_run();
+	
+	if (udpforcedsa != NULL)
+		free(udpforcedsa);
+		
+	if (tcpforcedsa != NULL)
+		free(tcpforcedsa);
+
+	if (udpforcedsa6 != NULL)
+		free(udpforcedsa6);
+
+	if (tcpforcedsa6 != NULL)
+		free(tcpforcedsa6);
+
 	syslog(LOG_ERR, "Mountd died");
 	exit(1);
 }