Add "-R" option to tell mountd to not register with rpcbind.

rpcbind is now considered a security risk for some sites.
Since an NFSv4 only NFS server does not need rpcbind,
it makes sense to have an option that implements this.

This patch adds a "-R" option that disables the Mount
protocol (not used by NFSv4) and does not register
with rpcbind.

Changes are required to /etc/rc.d/mountd and /etc/rc.d/nfsd.
Those will be in a separate commit.

Reviewed by:	freqlabs, asomers
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D26746
This commit is contained in:
Rick Macklem 2020-10-24 22:48:28 +00:00
parent ae37905b93
commit bde6f938bf

View file

@ -427,8 +427,10 @@ main(int argc, char **argv)
uint64_t curtime, nexttime;
struct timeval tv;
struct timespec tp;
sigset_t sighup_mask;
sigset_t sig_mask, sighup_mask;
int enable_rpcbind;
enable_rpcbind = 1;
/* Check that another mountd isn't already running. */
pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
if (pfh == NULL) {
@ -443,7 +445,7 @@ main(int argc, char **argv)
else
close(s);
while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
while ((c = getopt(argc, argv, "2deh:lnp:RrS")) != -1)
switch (c) {
case '2':
force_v2 = 1;
@ -454,6 +456,10 @@ main(int argc, char **argv)
case 'n':
resvport_only = 0;
break;
case 'R':
/* Do not support Mount protocol */
enable_rpcbind = 0;
break;
case 'r':
dir_only = 0;
break;
@ -498,6 +504,21 @@ main(int argc, char **argv)
default:
usage();
}
if (enable_rpcbind == 0) {
if (svcport_str != NULL) {
warnx("-p option not compatible with -R, ignored");
free(svcport_str);
svcport_str = NULL;
}
if (nhosts > 0) {
warnx("-h option not compatible with -R, ignored");
for (k = 0; k < nhosts; k++)
free(hosts[k]);
free(hosts);
hosts = NULL;
nhosts = 0;
}
}
if (modfind("nfsd") < 0) {
/* Not present in kernel, try loading it */
@ -531,58 +552,61 @@ main(int argc, char **argv)
pidfile_write(pfh);
rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
if (enable_rpcbind != 0) {
rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
if (!resvport_only) {
if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
&resvport_only, sizeof(resvport_only)) != 0 &&
errno != ENOENT) {
syslog(LOG_ERR, "sysctl: %m");
exit(1);
}
}
/*
* If no hosts were specified, add a wildcard entry to bind to
* INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
* list.
*/
if (nhosts == 0) {
hosts = malloc(sizeof(char *));
if (hosts == NULL)
out_of_mem();
hosts[0] = "*";
nhosts = 1;
} else {
hosts_bak = hosts;
if (have_v6) {
hosts_bak = realloc(hosts, (nhosts + 2) *
sizeof(char *));
if (hosts_bak == NULL) {
for (k = 0; k < nhosts; k++)
free(hosts[k]);
free(hosts);
out_of_mem();
} else
hosts = hosts_bak;
nhosts += 2;
hosts[nhosts - 2] = "::1";
} else {
hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
if (hosts_bak == NULL) {
for (k = 0; k < nhosts; k++)
free(hosts[k]);
free(hosts);
out_of_mem();
} else {
nhosts += 1;
hosts = hosts_bak;
if (!resvport_only) {
if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
&resvport_only, sizeof(resvport_only)) != 0 &&
errno != ENOENT) {
syslog(LOG_ERR, "sysctl: %m");
exit(1);
}
}
hosts[nhosts - 1] = "127.0.0.1";
/*
* If no hosts were specified, add a wildcard entry to bind to
* INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added
* to the list.
*/
if (nhosts == 0) {
hosts = malloc(sizeof(char *));
if (hosts == NULL)
out_of_mem();
hosts[0] = "*";
nhosts = 1;
} else {
hosts_bak = hosts;
if (have_v6) {
hosts_bak = realloc(hosts, (nhosts + 2) *
sizeof(char *));
if (hosts_bak == NULL) {
for (k = 0; k < nhosts; k++)
free(hosts[k]);
free(hosts);
out_of_mem();
} else
hosts = hosts_bak;
nhosts += 2;
hosts[nhosts - 2] = "::1";
} else {
hosts_bak = realloc(hosts, (nhosts + 1) *
sizeof(char *));
if (hosts_bak == NULL) {
for (k = 0; k < nhosts; k++)
free(hosts[k]);
free(hosts);
out_of_mem();
} else {
nhosts += 1;
hosts = hosts_bak;
}
}
hosts[nhosts - 1] = "127.0.0.1";
}
}
attempt_cnt = 1;
@ -590,96 +614,109 @@ main(int argc, char **argv)
sock_fd = NULL;
port_list = NULL;
port_len = 0;
nc_handle = setnetconfig();
while ((nconf = getnetconfig(nc_handle))) {
if (nconf->nc_flag & NC_VISIBLE) {
if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
"inet6") == 0) {
/* DO NOTHING */
} else {
ret = create_service(nconf);
if (ret == 1)
/* Ignore this call */
continue;
if (ret < 0) {
/*
* Failed to bind port, so close off
* all sockets created and try again
* if the port# was dynamically
* assigned via bind(2).
*/
clearout_service();
if (mallocd_svcport != 0 &&
attempt_cnt < GETPORT_MAXTRY) {
free(svcport_str);
if (enable_rpcbind != 0) {
nc_handle = setnetconfig();
while ((nconf = getnetconfig(nc_handle))) {
if (nconf->nc_flag & NC_VISIBLE) {
if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
"inet6") == 0) {
/* DO NOTHING */
} else {
ret = create_service(nconf);
if (ret == 1)
/* Ignore this call */
continue;
if (ret < 0) {
/*
* Failed to bind port, so close
* off all sockets created and
* try again if the port# was
* dynamically assigned via
* bind(2).
*/
clearout_service();
if (mallocd_svcport != 0 &&
attempt_cnt <
GETPORT_MAXTRY) {
free(svcport_str);
svcport_str = NULL;
mallocd_svcport = 0;
} else {
errno = EADDRINUSE;
syslog(LOG_ERR,
"bindresvport_sa:"
" %m");
exit(1);
}
/*
* Start over at the first
* service.
*/
free(sock_fd);
sock_fdcnt = 0;
sock_fd = NULL;
nc_handle = setnetconfig();
attempt_cnt++;
} else if (mallocd_svcport != 0 &&
attempt_cnt == GETPORT_MAXTRY) {
/*
* For the last attempt, allow
* different port #s for each
* nconf by saving the
* svcport_str setting it back
* to NULL.
*/
port_list = realloc(port_list,
(port_len + 1) *
sizeof(char *));
if (port_list == NULL)
out_of_mem();
port_list[port_len++] =
svcport_str;
svcport_str = NULL;
mallocd_svcport = 0;
} else {
errno = EADDRINUSE;
syslog(LOG_ERR,
"bindresvport_sa: %m");
exit(1);
}
/* Start over at the first service. */
free(sock_fd);
sock_fdcnt = 0;
sock_fd = NULL;
nc_handle = setnetconfig();
attempt_cnt++;
} else if (mallocd_svcport != 0 &&
attempt_cnt == GETPORT_MAXTRY) {
/*
* For the last attempt, allow
* different port #s for each nconf
* by saving the svcport_str and
* setting it back to NULL.
*/
port_list = realloc(port_list,
(port_len + 1) * sizeof(char *));
if (port_list == NULL)
out_of_mem();
port_list[port_len++] = svcport_str;
svcport_str = NULL;
mallocd_svcport = 0;
}
}
}
}
/*
* Successfully bound the ports, so call complete_service() to
* do the rest of the setup on the service(s).
*/
sock_fdpos = 0;
port_pos = 0;
nc_handle = setnetconfig();
while ((nconf = getnetconfig(nc_handle))) {
if (nconf->nc_flag & NC_VISIBLE) {
if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
"inet6") == 0) {
/* DO NOTHING */
} else if (port_list != NULL) {
if (port_pos >= port_len) {
syslog(LOG_ERR, "too many port#s");
exit(1);
}
complete_service(nconf, port_list[port_pos++]);
} else
complete_service(nconf, svcport_str);
/*
* Successfully bound the ports, so call complete_service() to
* do the rest of the setup on the service(s).
*/
sock_fdpos = 0;
port_pos = 0;
nc_handle = setnetconfig();
while ((nconf = getnetconfig(nc_handle))) {
if (nconf->nc_flag & NC_VISIBLE) {
if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
"inet6") == 0) {
/* DO NOTHING */
} else if (port_list != NULL) {
if (port_pos >= port_len) {
syslog(LOG_ERR, "too many"
" port#s");
exit(1);
}
complete_service(nconf,
port_list[port_pos++]);
} else
complete_service(nconf, svcport_str);
}
}
endnetconfig(nc_handle);
free(sock_fd);
if (port_list != NULL) {
for (port_pos = 0; port_pos < port_len; port_pos++)
free(port_list[port_pos]);
free(port_list);
}
}
endnetconfig(nc_handle);
free(sock_fd);
if (port_list != NULL) {
for (port_pos = 0; port_pos < port_len; port_pos++)
free(port_list[port_pos]);
free(port_list);
}
if (xcreated == 0) {
syslog(LOG_ERR, "could not create any services");
exit(1);
if (xcreated == 0) {
syslog(LOG_ERR, "could not create any services");
exit(1);
}
}
/* Expand svc_run() here so that we can call get_exportlist(). */
@ -690,7 +727,7 @@ main(int argc, char **argv)
clock_gettime(CLOCK_MONOTONIC, &tp);
curtime = tp.tv_sec;
curtime = curtime * 1000000 + tp.tv_nsec / 1000;
sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
sigprocmask(SIG_BLOCK, &sighup_mask, &sig_mask);
if (got_sighup && curtime >= nexttime) {
got_sighup = 0;
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
@ -713,22 +750,28 @@ main(int argc, char **argv)
tv.tv_usec = 0;
else
tv.tv_usec = RELOADDELAY;
readfds = svc_fdset;
switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) {
case -1:
if (errno == EINTR) {
if (enable_rpcbind != 0) {
readfds = svc_fdset;
switch (select(svc_maxfd + 1, &readfds, NULL, NULL,
&tv)) {
case -1:
if (errno == EINTR) {
/* Allow a reload now. */
nexttime = 0;
continue;
}
syslog(LOG_ERR, "mountd died: select: %m");
exit(1);
case 0:
/* Allow a reload now. */
nexttime = 0;
continue;
default:
svc_getreqset(&readfds);
}
syslog(LOG_ERR, "mountd died: select: %m");
exit(1);
case 0:
/* Allow a reload now. */
nexttime = 0;
continue;
default:
svc_getreqset(&readfds);
} else {
/* Simply wait for a signal. */
sigsuspend(&sig_mask);
}
}
}