aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/mountd/mountd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mountd/mountd.c')
-rw-r--r--usr.sbin/mountd/mountd.c500
1 files changed, 339 insertions, 161 deletions
diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
index 569ea0fbf707..7e2f9b6e73bd 100644
--- a/usr.sbin/mountd/mountd.c
+++ b/usr.sbin/mountd/mountd.c
@@ -32,21 +32,6 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char copyright[] =
-"@(#) Copyright (c) 1989, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /*not lint*/
-
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
-#endif /*not lint*/
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
@@ -72,12 +57,14 @@ __FBSDID("$FreeBSD$");
#include <arpa/inet.h>
+#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <libutil.h>
#include <limits.h>
+#include <mntopts.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
@@ -85,8 +72,8 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <vis.h>
#include "pathnames.h"
-#include "mntopts.h"
#ifdef DEBUG
#include <stdarg.h>
@@ -144,10 +131,11 @@ struct exportlist {
SLIST_ENTRY(exportlist) entries;
};
/* ex_flag bits */
-#define EX_LINKED 0x1
-#define EX_DONE 0x2
-#define EX_DEFSET 0x4
-#define EX_PUBLICFH 0x8
+#define EX_LINKED 0x01
+#define EX_DONE 0x02
+#define EX_DEFSET 0x04
+#define EX_PUBLICFH 0x08
+#define EX_ADMINWARN 0x10
SLIST_HEAD(exportlisthead, exportlist);
@@ -210,7 +198,9 @@ static void add_dlist(struct dirlist **, struct dirlist *,
struct grouplist *, int, struct exportlist *,
struct expcred *, uint64_t);
static void add_mlist(char *, char *);
-static int check_dirpath(char *);
+static int check_path_component(const char *, char **);
+static int check_dirpath(char *, char **);
+static int check_statfs(const char *, struct statfs *, char **);
static int check_options(struct dirlist *);
static int checkmask(struct sockaddr *sa);
static int chk_host(struct dirlist *, struct sockaddr *, int *, int *,
@@ -225,7 +215,8 @@ static int do_export_mount(struct exportlist *, struct statfs *);
static int do_mount(struct exportlist *, struct grouplist *, uint64_t,
struct expcred *, char *, int, struct statfs *, int, int *);
static int do_opt(char **, char **, struct exportlist *,
- struct grouplist *, int *, uint64_t *, struct expcred *);
+ struct grouplist *, int *, uint64_t *, struct expcred *,
+ char *);
static struct exportlist *ex_search(fsid_t *, struct exportlisthead *);
static struct exportlist *get_exp(void);
static void free_dir(struct dirlist *);
@@ -240,6 +231,7 @@ static void free_exports(struct exportlisthead *);
static void read_exportfile(int);
static int compare_nmount_exportlist(struct iovec *, int, char *);
static int compare_export(struct exportlist *, struct exportlist *);
+static int compare_addr(struct grouplist *, struct grouplist *);
static int compare_cred(struct expcred *, struct expcred *);
static int compare_secflavor(int *, int *, int);
static void delete_export(struct iovec *, int, struct statfs *, char *);
@@ -256,7 +248,7 @@ static void huphandler(int sig);
static int makemask(struct sockaddr_storage *ssp, int bitlen);
static void mntsrv(struct svc_req *, SVCXPRT *);
static void nextfield(char **, char **);
-static void out_of_mem(void);
+static void out_of_mem(void) __dead2;
static void parsecred(char *, struct expcred *);
static int parsesec(char *, struct exportlist *);
static int put_exlist(struct dirlist *, XDR *, struct dirlist *,
@@ -275,6 +267,8 @@ static int xdr_mlist(XDR *, caddr_t);
static void terminate(int);
static void cp_cred(struct expcred *, struct expcred *);
+static gid_t nogroup();
+
#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
static struct exportlisthead *exphead = NULL;
static struct exportlisthead *oldexphead = NULL;
@@ -284,11 +278,12 @@ static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
static char **exnames;
static char **hosts = NULL;
static int force_v2 = 0;
+static int warn_admin = 1;
static int resvport_only = 1;
static int nhosts = 0;
static int dir_only = 1;
static int dolog = 0;
-static int got_sighup = 0;
+static _Atomic(int) got_sighup = 0;
static int xcreated = 0;
static char *svcport_str = NULL;
@@ -297,6 +292,9 @@ static int *sock_fd;
static int sock_fdcnt;
static int sock_fdpos;
static int suspend_nfsd = 0;
+static int nofork = 0;
+static int skiplocalhost = 0;
+static int alldirs_fail = 0;
static int opt_flags;
static int have_v6 = 1;
@@ -308,6 +306,11 @@ static int has_publicfh = 0;
static int has_set_publicfh = 0;
static struct pidfh *pfh = NULL;
+
+/* Temporary storage for credentials' groups. */
+static long tngroups_max;
+static gid_t *tmp_groups = NULL;
+
/* Bits for opt_flags above */
#define OP_MAPROOT 0x01
#define OP_MAPALL 0x02
@@ -319,6 +322,8 @@ static struct pidfh *pfh = NULL;
#define OP_QUIET 0x100
#define OP_MASKLEN 0x200
#define OP_SEC 0x400
+#define OP_CLASSMASK 0x800 /* mask not specified, is Class A/B/C default */
+#define OP_NOTROOT 0x1000 /* Mark the the mount path is not a fs root */
#ifdef DEBUG
static int debug = 1;
@@ -439,17 +444,35 @@ main(int argc, char **argv)
warn("cannot open or create pidfile");
}
+ openlog("mountd", LOG_PID, LOG_DAEMON);
+
+ /* How many groups do we support? */
+ tngroups_max = sysconf(_SC_NGROUPS_MAX);
+ if (tngroups_max == -1)
+ tngroups_max = NGROUPS_MAX;
+ /* Add space for the effective GID. */
+ ++tngroups_max;
+ tmp_groups = malloc(tngroups_max);
+ if (tmp_groups == NULL)
+ out_of_mem();
+
s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0)
have_v6 = 0;
else
close(s);
- while ((c = getopt(argc, argv, "2deh:lnp:RrS")) != -1)
+ while ((c = getopt(argc, argv, "2Aadeh:lNnp:RrSs")) != -1)
switch (c) {
case '2':
force_v2 = 1;
break;
+ case 'A':
+ warn_admin = 0;
+ break;
+ case 'a':
+ alldirs_fail = 1;
+ break;
case 'e':
/* now a no-op, since this is the default */
break;
@@ -501,6 +524,12 @@ main(int argc, char **argv)
case 'S':
suspend_nfsd = 1;
break;
+ case 'N':
+ nofork = 1;
+ break;
+ case 's':
+ skiplocalhost = 1;
+ break;
default:
usage();
}
@@ -520,6 +549,9 @@ main(int argc, char **argv)
}
}
+ if (nhosts == 0 && skiplocalhost != 0)
+ warnx("-s without -h, ignored");
+
if (modfind("nfsd") < 0) {
/* Not present in kernel, try loading it */
if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
@@ -532,7 +564,6 @@ main(int argc, char **argv)
exnames = argv;
else
exnames = exnames_default;
- openlog("mountd", LOG_PID, LOG_DAEMON);
if (debug)
warnx("getting export list");
get_exportlist(0);
@@ -541,7 +572,7 @@ main(int argc, char **argv)
get_mountlist();
if (debug)
warnx("here we go");
- if (debug == 0) {
+ if (debug == 0 && nofork == 0) {
daemon(0, 0);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
@@ -577,7 +608,7 @@ main(int argc, char **argv)
out_of_mem();
hosts[0] = "*";
nhosts = 1;
- } else {
+ } else if (skiplocalhost == 0) {
hosts_bak = hosts;
if (have_v6) {
hosts_bak = realloc(hosts, (nhosts + 2) *
@@ -779,7 +810,7 @@ main(int argc, char **argv)
/*
* This routine creates and binds sockets on the appropriate
* addresses. It gets called one time for each transport.
- * It returns 0 upon success, 1 for ingore the call and -1 to indicate
+ * It returns 0 upon success, 1 for ignore the call and -1 to indicate
* bind failed with EADDRINUSE.
* Any file descriptors that have been created are stored in sock_fd and
* the total count of them is maintained in sock_fdcnt.
@@ -1117,8 +1148,8 @@ static void
usage(void)
{
fprintf(stderr,
- "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
- "[-S] [-h <bindip>] [export_file ...]\n");
+ "usage: mountd [-2] [-d] [-e] [-l] [-N] [-n] [-p <port>] [-r] [-S] "
+ "[-s] [-h <bindip>] [export_file ...]\n");
exit(1);
}
@@ -1557,12 +1588,17 @@ get_exportlist_one(int passno)
struct statfs fsb;
struct expcred anon;
char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ char *err_msg = NULL;
int len, has_host, got_nondir, dirplen, netgrp;
uint64_t exflags;
+ char unvis_dir[PATH_MAX + 1];
+ int unvis_len;
v4root_phase = 0;
- anon.cr_groups = NULL;
dirhead = (struct dirlist *)NULL;
+ unvis_dir[0] = '\0';
+ fsb.f_mntonname[0] = '\0';
+
while (get_line()) {
if (debug)
warnx("got line %s", line);
@@ -1575,10 +1611,10 @@ get_exportlist_one(int passno)
* Set defaults.
*/
has_host = FALSE;
- anon.cr_groups = anon.cr_smallgrps;
anon.cr_uid = UID_NOBODY;
anon.cr_ngroups = 1;
- anon.cr_groups[0] = GID_NOGROUP;
+ anon.cr_groups = tmp_groups;
+ anon.cr_groups[0] = nogroup();
exflags = MNT_EXPORTED;
got_nondir = 0;
opt_flags = 0;
@@ -1622,24 +1658,32 @@ get_exportlist_one(int passno)
warnx("doing opt %s", cp);
got_nondir = 1;
if (do_opt(&cp, &endcp, ep, grp, &has_host,
- &exflags, &anon)) {
+ &exflags, &anon, unvis_dir)) {
getexp_err(ep, tgrp, NULL);
goto nextline;
}
} else if (*cp == '/') {
savedc = *endcp;
*endcp = '\0';
+ unvis_len = strnunvis(unvis_dir, sizeof(unvis_dir),
+ cp);
+ if (unvis_len <= 0) {
+ getexp_err(ep, tgrp, "Cannot strunvis "
+ "decode dir");
+ goto nextline;
+ }
if (v4root_phase > 1) {
if (dirp != NULL) {
getexp_err(ep, tgrp, "Multiple V4 dirs");
goto nextline;
}
}
- if (check_dirpath(cp) &&
- statfs(cp, &fsb) >= 0) {
+ if (check_dirpath(unvis_dir, &err_msg) &&
+ check_statfs(unvis_dir, &fsb, &err_msg)) {
if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
syslog(LOG_ERR, "Warning: exporting of "
- "automounted fs %s not supported", cp);
+ "automounted fs %s not supported",
+ unvis_dir);
if (got_nondir) {
getexp_err(ep, tgrp, "dirs must be first");
goto nextline;
@@ -1650,16 +1694,17 @@ get_exportlist_one(int passno)
goto nextline;
}
if (strlen(v4root_dirpath) == 0) {
- strlcpy(v4root_dirpath, cp,
+ strlcpy(v4root_dirpath, unvis_dir,
sizeof (v4root_dirpath));
- } else if (strcmp(v4root_dirpath, cp)
+ } else if (strcmp(v4root_dirpath, unvis_dir)
!= 0) {
syslog(LOG_ERR,
- "different V4 dirpath %s", cp);
+ "different V4 dirpath %s",
+ unvis_dir);
getexp_err(ep, tgrp, NULL);
goto nextline;
}
- dirp = cp;
+ dirp = unvis_dir;
v4root_phase = 2;
got_nondir = 1;
ep = get_exp();
@@ -1694,15 +1739,27 @@ get_exportlist_one(int passno)
fsb.f_fsid.val[1]);
}
+ if (strcmp(unvis_dir, fsb.f_mntonname) !=
+ 0)
+ opt_flags |= OP_NOTROOT;
+
/*
* Add dirpath to export mount point.
*/
- dirp = add_expdir(&dirhead, cp, len);
- dirplen = len;
+ dirp = add_expdir(&dirhead, unvis_dir,
+ unvis_len);
+ dirplen = unvis_len;
}
} else {
- getexp_err(ep, tgrp,
- "symbolic link in export path or statfs failed");
+ if (err_msg != NULL) {
+ getexp_err(ep, tgrp, err_msg);
+ free(err_msg);
+ err_msg = NULL;
+ } else {
+ getexp_err(ep, tgrp,
+ "symbolic link in export path or "
+ "statfs failed");
+ }
goto nextline;
}
*endcp = savedc;
@@ -1749,6 +1806,22 @@ get_exportlist_one(int passno)
nextfield(&cp, &endcp);
len = endcp - cp;
}
+ if (opt_flags & OP_CLASSMASK)
+ syslog(LOG_ERR,
+ "WARNING: No mask specified for %s, "
+ "using out-of-date default",
+ (&grp->gr_ptr.gt_net)->nt_name);
+ if ((opt_flags & OP_NOTROOT) != 0 && warn_admin != 0 &&
+ (ep->ex_flag & EX_ADMINWARN) == 0 && unvis_dir[0] != '\0' &&
+ fsb.f_mntonname[0] != '\0') {
+ if (debug)
+ warnx("exporting %s exports entire %s file "
+ "system", unvis_dir, fsb.f_mntonname);
+ syslog(LOG_ERR, "Warning: exporting %s exports "
+ "entire %s file system", unvis_dir,
+ fsb.f_mntonname);
+ ep->ex_flag |= EX_ADMINWARN;
+ }
if (check_options(dirhead)) {
getexp_err(ep, tgrp, NULL);
goto nextline;
@@ -1871,10 +1944,6 @@ nextline:
free_dir(dirhead);
dirhead = (struct dirlist *)NULL;
}
- if (anon.cr_groups != anon.cr_smallgrps) {
- free(anon.cr_groups);
- anon.cr_groups = NULL;
- }
}
}
@@ -2031,19 +2100,7 @@ get_exportlist(int passno)
syslog(LOG_ERR, "NFSv4 requires at least one V4: line");
}
- if (iov != NULL) {
- /* Free strings allocated by strdup() in getmntopts.c */
- free(iov[0].iov_base); /* fstype */
- free(iov[2].iov_base); /* fspath */
- free(iov[4].iov_base); /* from */
- free(iov[6].iov_base); /* update */
- free(iov[8].iov_base); /* export */
- free(iov[10].iov_base); /* errmsg */
-
- /* free iov, allocated by realloc() */
- free(iov);
- iovlen = 0;
- }
+ free_iovec(&iov, &iovlen);
/*
* If there was no public fh, clear any previous one set.
@@ -2280,7 +2337,8 @@ compare_export(struct exportlist *ep, struct exportlist *oep)
grp->gr_exflags == ogrp->gr_exflags &&
compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
compare_secflavor(grp->gr_secflavors,
- ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
+ ogrp->gr_secflavors, grp->gr_numsecflavors) == 0 &&
+ compare_addr(grp, ogrp) == 0)
break;
if (ogrp != NULL)
ogrp->gr_flag |= GR_FND;
@@ -2294,13 +2352,53 @@ compare_export(struct exportlist *ep, struct exportlist *oep)
}
/*
+ * Compare the addresses in the group. It is safe to return they are not
+ * the same when the are, so only return they are the same when they are
+ * exactly the same.
+ */
+static int
+compare_addr(struct grouplist *grp, struct grouplist *ogrp)
+{
+ struct addrinfo *ai, *oai;
+
+ if (grp->gr_type != ogrp->gr_type)
+ return (1);
+ switch (grp->gr_type) {
+ case GT_HOST:
+ ai = grp->gr_ptr.gt_addrinfo;
+ oai = ogrp->gr_ptr.gt_addrinfo;
+ for (; ai != NULL && oai != NULL; ai = ai->ai_next,
+ oai = oai->ai_next) {
+ if (sacmp(ai->ai_addr, oai->ai_addr, NULL) != 0)
+ return (1);
+ }
+ if (ai != NULL || oai != NULL)
+ return (1);
+ break;
+ case GT_NET:
+ /* First compare the masks and then the nets. */
+ if (sacmp((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask,
+ (struct sockaddr *)&ogrp->gr_ptr.gt_net.nt_mask, NULL) != 0)
+ return (1);
+ if (sacmp((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net,
+ (struct sockaddr *)&ogrp->gr_ptr.gt_net.nt_net,
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask) != 0)
+ return (1);
+ break;
+ default:
+ return (1);
+ }
+ return (0);
+}
+
+/*
* This algorithm compares two arrays of "n" items. It returns 0 if they are
* the "same" and 1 otherwise. Although suboptimal, it is always safe to
* return 1, which makes compare_nmount_export() reload the exports entry.
* "same" refers to having the same set of values in the two arrays.
* The arrays are in no particular order and duplicates (multiple entries
* in an array with the same value) is allowed.
- * The algorithm is inefficient, but the common case of indentical arrays is
+ * The algorithm is inefficient, but the common case of identical arrays is
* handled first and "n" is normally fairly small.
* Since the two functions need the same algorithm but for arrays of
* different types (gid_t vs int), this is done as a macro.
@@ -2776,11 +2874,11 @@ parsesec(char *seclist, struct exportlist *ep)
*/
static int
do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
- int *has_hostp, uint64_t *exflagsp, struct expcred *cr)
+ int *has_hostp, uint64_t *exflagsp, struct expcred *cr, char *unvis_dir)
{
char *cpoptarg, *cpoptend;
char *cp, *endcp, *cpopt, savedc, savedc2;
- int allflag, usedarg;
+ int allflag, usedarg, fnd_equal;
savedc2 = '\0';
cpopt = *cpp;
@@ -2791,14 +2889,18 @@ do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
while (cpopt && *cpopt) {
allflag = 1;
usedarg = -2;
+ fnd_equal = 0;
if ((cpoptend = strchr(cpopt, ','))) {
*cpoptend++ = '\0';
- if ((cpoptarg = strchr(cpopt, '=')))
+ if ((cpoptarg = strchr(cpopt, '='))) {
*cpoptarg++ = '\0';
+ fnd_equal = 1;
+ }
} else {
- if ((cpoptarg = strchr(cpopt, '=')))
+ if ((cpoptarg = strchr(cpopt, '='))) {
*cpoptarg++ = '\0';
- else {
+ fnd_equal = 1;
+ } else {
*cp = savedc;
nextfield(&cp, &endcp);
**endcpp = '\0';
@@ -2811,6 +2913,10 @@ do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
}
}
if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= MNT_EXRDONLY;
} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
!(allflag = strcmp(cpopt, "mapall")) ||
@@ -2849,15 +2955,37 @@ do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
usedarg++;
opt_flags |= OP_NET;
} else if (!strcmp(cpopt, "alldirs")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
+ if ((opt_flags & OP_NOTROOT) != 0) {
+ syslog(LOG_ERR, "%s: path %s not mount point",
+ cpopt, unvis_dir);
+ if (alldirs_fail != 0)
+ return (1);
+ }
opt_flags |= OP_ALLDIRS;
} else if (!strcmp(cpopt, "public")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= MNT_EXPUBLIC;
} else if (!strcmp(cpopt, "webnfs")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
opt_flags |= OP_MAPALL;
} else if (cpoptarg && !strcmp(cpopt, "index")) {
ep->ex_indexfile = strdup(cpoptarg);
} else if (!strcmp(cpopt, "quiet")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
opt_flags |= OP_QUIET;
} else if (cpoptarg && !strcmp(cpopt, "sec")) {
if (parsesec(cpoptarg, ep))
@@ -2865,10 +2993,22 @@ do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
opt_flags |= OP_SEC;
usedarg++;
} else if (!strcmp(cpopt, "tls")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= MNT_EXTLS;
} else if (!strcmp(cpopt, "tlscert")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT);
} else if (!strcmp(cpopt, "tlscertuser")) {
+ if (fnd_equal == 1) {
+ syslog(LOG_ERR, "= after op: %s", cpopt);
+ return (1);
+ }
*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT |
MNT_EXTLSCERTUSER);
} else {
@@ -3104,11 +3244,11 @@ do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
eap->ex_flags = exflags;
eap->ex_uid = anoncrp->cr_uid;
eap->ex_ngroups = anoncrp->cr_ngroups;
- if (eap->ex_ngroups > 0) {
- eap->ex_groups = malloc(eap->ex_ngroups * sizeof(gid_t));
- memcpy(eap->ex_groups, anoncrp->cr_groups, eap->ex_ngroups *
- sizeof(gid_t));
- }
+ /*
+ * Use the memory pointed to by 'anoncrp', as it outlives 'eap' which is
+ * local to this function.
+ */
+ eap->ex_groups = anoncrp->cr_groups;
LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
eap->ex_indexfile = ep->ex_indexfile;
if (grp->gr_type == GT_HOST)
@@ -3221,7 +3361,8 @@ do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
ret = 1;
goto error_exit;
}
- if (opt_flags & OP_ALLDIRS) {
+ if ((opt_flags & OP_ALLDIRS) &&
+ alldirs_fail != 0) {
if (errno == EINVAL)
syslog(LOG_ERR,
"-alldirs requested but %s is not a filesystem mountpoint",
@@ -3298,19 +3439,7 @@ skip:
if (cp)
*cp = savedc;
error_exit:
- free(eap->ex_groups);
- /* free strings allocated by strdup() in getmntopts.c */
- if (iov != NULL) {
- free(iov[0].iov_base); /* fstype */
- free(iov[2].iov_base); /* fspath */
- free(iov[4].iov_base); /* from */
- free(iov[6].iov_base); /* update */
- free(iov[8].iov_base); /* export */
- free(iov[10].iov_base); /* errmsg */
-
- /* free iov, allocated by realloc() */
- free(iov);
- }
+ free_iovec(&iov, &iovlen);
return (ret);
}
@@ -3385,6 +3514,7 @@ get_net(char *cp, struct netmsk *net, int maskflg)
goto fail;
bcopy(sa, &net->nt_mask, sa->sa_len);
opt_flags |= OP_HAVEMASK;
+ opt_flags &= ~OP_CLASSMASK;
} else {
/* The specified sockaddr is a network address. */
bcopy(sa, &net->nt_net, sa->sa_len);
@@ -3418,9 +3548,6 @@ get_net(char *cp, struct netmsk *net, int maskflg)
(opt_flags & OP_MASK) == 0) {
in_addr_t addr;
- syslog(LOG_WARNING,
- "WARNING: No mask specified for %s, "
- "using out-of-date default", name);
addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
if (IN_CLASSA(addr))
preflen = 8;
@@ -3435,7 +3562,7 @@ get_net(char *cp, struct netmsk *net, int maskflg)
bcopy(sa, &net->nt_mask, sa->sa_len);
makemask(&net->nt_mask, (int)preflen);
- opt_flags |= OP_HAVEMASK;
+ opt_flags |= OP_HAVEMASK | OP_CLASSMASK;
}
}
@@ -3528,84 +3655,64 @@ get_line(void)
* Parse a description of a credential.
*/
static void
-parsecred(char *namelist, struct expcred *cr)
+parsecred(char *names, struct expcred *cr)
{
char *name;
- int inpos;
- char *names;
struct passwd *pw;
- struct group *gr;
- gid_t groups[NGROUPS_MAX + 1];
- int ngroups;
unsigned long name_ul;
char *end = NULL;
+ assert(cr->cr_groups == tmp_groups);
+
/*
- * Set up the unprivileged user.
- */
- cr->cr_groups = cr->cr_smallgrps;
- cr->cr_uid = UID_NOBODY;
- cr->cr_groups[0] = GID_NOGROUP;
- cr->cr_ngroups = 1;
- /*
- * Get the user's password table entry.
+ * Parse the user and if possible get its password table entry.
+ * 'cr_uid' is filled when exiting this block.
*/
- names = namelist;
name = strsep_quote(&names, ":");
- /* Bug? name could be NULL here */
name_ul = strtoul(name, &end, 10);
if (*end != '\0' || end == name)
pw = getpwnam(name);
else
pw = getpwuid((uid_t)name_ul);
+ if (pw != NULL) {
+ cr->cr_uid = pw->pw_uid;
+ } else if (*end != '\0' || end == name) {
+ syslog(LOG_ERR, "unknown user: %s", name);
+ cr->cr_uid = UID_NOBODY;
+ goto nogroup;
+ } else {
+ cr->cr_uid = name_ul;
+ }
+
/*
- * Credentials specified as those of a user.
+ * Credentials specified as those of a user (i.e., use its associated
+ * groups as specified in the password database).
*/
if (names == NULL) {
if (pw == NULL) {
- syslog(LOG_ERR, "unknown user: %s", name);
- return;
+ syslog(LOG_ERR, "no passwd entry for user: %s, "
+ "can't determine groups", name);
+ goto nogroup;
}
- cr->cr_uid = pw->pw_uid;
- ngroups = NGROUPS_MAX + 1;
- if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
+
+ cr->cr_ngroups = tngroups_max;
+ if (getgrouplist(pw->pw_name, pw->pw_gid,
+ cr->cr_groups, &cr->cr_ngroups) != 0) {
syslog(LOG_ERR, "too many groups");
- ngroups = NGROUPS_MAX + 1;
+ cr->cr_ngroups = tngroups_max;
}
-
- /*
- * Compress out duplicate.
- */
- if (ngroups > 1 && groups[0] == groups[1]) {
- ngroups--;
- inpos = 2;
- } else {
- inpos = 1;
- }
- if (ngroups > NGROUPS_MAX)
- ngroups = NGROUPS_MAX;
- if (ngroups > SMALLNGROUPS)
- cr->cr_groups = malloc(ngroups * sizeof(gid_t));
- cr->cr_ngroups = ngroups;
- cr->cr_groups[0] = groups[0];
- memcpy(&cr->cr_groups[1], &groups[inpos], (ngroups - 1) *
- sizeof(gid_t));
return;
}
+
/*
- * Explicit credential specified as a colon separated list:
+ * Explicit credentials specified as a colon separated list:
* uid:gid:gid:...
*/
- if (pw != NULL) {
- cr->cr_uid = pw->pw_uid;
- } else if (*end != '\0' || end == name) {
- syslog(LOG_ERR, "unknown user: %s", name);
- return;
- } else {
- cr->cr_uid = name_ul;
- }
cr->cr_ngroups = 0;
- while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) {
+ while (names != NULL && *names != '\0') {
+ const struct group *gr;
+ gid_t group;
+
name = strsep_quote(&names, ":");
name_ul = strtoul(name, &end, 10);
if (*end != '\0' || end == name) {
@@ -3613,16 +3720,23 @@ parsecred(char *namelist, struct expcred *cr)
syslog(LOG_ERR, "unknown group: %s", name);
continue;
}
- groups[cr->cr_ngroups++] = gr->gr_gid;
+ group = gr->gr_gid;
} else {
- groups[cr->cr_ngroups++] = name_ul;
+ group = name_ul;
}
+ if (cr->cr_ngroups == tngroups_max) {
+ syslog(LOG_ERR, "too many groups");
+ break;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = group;
}
- if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX)
- syslog(LOG_ERR, "too many groups");
- if (cr->cr_ngroups > SMALLNGROUPS)
- cr->cr_groups = malloc(cr->cr_ngroups * sizeof(gid_t));
- memcpy(cr->cr_groups, groups, cr->cr_ngroups * sizeof(gid_t));
+ if (cr->cr_ngroups == 0)
+ goto nogroup;
+ return;
+
+nogroup:
+ cr->cr_ngroups = 1;
+ cr->cr_groups[0] = nogroup();
}
#define STRSIZ (MNTNAMLEN+MNTPATHLEN+50)
@@ -3786,29 +3900,76 @@ check_options(struct dirlist *dp)
return (0);
}
+static int
+check_path_component(const char *path, char **err)
+{
+ struct stat sb;
+
+ if (lstat(path, &sb)) {
+ asprintf(err, "%s: lstat() failed: %s.\n",
+ path, strerror(errno));
+ return (0);
+ }
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ return (1);
+ case S_IFLNK:
+ asprintf(err, "%s: path is a symbolic link.\n", path);
+ break;
+ case S_IFREG:
+ asprintf(err, "%s: path is a file rather than a directory.\n",
+ path);
+ break;
+ default:
+ asprintf(err, "%s: path is not a directory.\n", path);
+ }
+
+ return (0);
+}
+
/*
- * Check an absolute directory path for any symbolic links. Return true
+ * Check each path component for the presence of symbolic links. Return true
*/
static int
-check_dirpath(char *dirp)
+check_dirpath(char *dirp, char **err)
{
char *cp;
- int ret = 1;
- struct stat sb;
cp = dirp + 1;
- while (*cp && ret) {
+ while (*cp) {
if (*cp == '/') {
*cp = '\0';
- if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
- ret = 0;
+
+ if (!check_path_component(dirp, err)) {
+ *cp = '/';
+ return (0);
+ }
+
*cp = '/';
}
cp++;
}
- if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
- ret = 0;
- return (ret);
+
+ if (!check_path_component(dirp, err))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Populate statfs information. Return true on success.
+ */
+static int
+check_statfs(const char *dirp, struct statfs *fsb, char **err)
+{
+ if (statfs(dirp, fsb)) {
+ asprintf(err, "%s: statfs() failed: %s\n", dirp,
+ strerror(errno));
+ return (0);
+ }
+
+ return (1);
}
/*
@@ -3939,6 +4100,7 @@ huphandler(int sig __unused)
static void
terminate(int sig __unused)
{
+ free(tmp_groups);
pidfile_remove(pfh);
rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
@@ -3958,3 +4120,19 @@ cp_cred(struct expcred *outcr, struct expcred *incr)
memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
sizeof(gid_t));
}
+
+static gid_t
+nogroup()
+{
+ static gid_t nogroup = 0; /* 0 means unset. */
+
+ if (nogroup == 0) {
+ const struct group *gr = getgrnam("nogroup");
+
+ if (gr != NULL && gr->gr_gid != 0)
+ nogroup = gr->gr_gid;
+ else
+ nogroup = GID_NOGROUP;
+ }
+ return (nogroup);
+}