Page MenuHomeFreeBSD

D37741.id115280.diff
No OneTemporary

D37741.id115280.diff

diff --git a/sys/kern/vfs_mount.c.vnet b/sys/kern/vfs_mount.c
--- a/sys/kern/vfs_mount.c.vnet
+++ b/sys/kern/vfs_mount.c
@@ -797,7 +797,7 @@
struct vfsopt *opt, *tmp_opt;
char *fstype, *fspath, *errmsg;
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
- bool autoro;
+ bool autoro, done_nonexport, has_nonexport;
errmsg = fspath = NULL;
errmsg_len = fspathlen = 0;
@@ -834,6 +834,19 @@
}
/*
+ * Make sure that "export" is only used with the "update", "fstype",
+ * "fspath", "from" and "errmsg" options when in a vnet jail.
+ * These are the ones used to set/update exports by mountd(8).
+ * Clear MNT_EXPORTED here, so that it cannot be passed in
+ * via the flags argument to nmount(2), to force use of "export".
+ * Use has_nonexport and done_nonexport to track whether or
+ * not other options have been specified.
+ */
+ has_nonexport = false;
+ if (jailed(td->td_ucred) && prison_check_nfsd(td->td_ucred))
+ fsflags &= ~MNT_EXPORTED;
+
+ /*
* We need to see if we have the "update" option
* before we call vfs_domount(), since vfs_domount() has special
* logic based on MNT_UPDATE. This is very important
@@ -842,9 +855,11 @@
TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
int do_freeopt = 0;
+ done_nonexport = true;
if (strcmp(opt->name, "update") == 0) {
fsflags |= MNT_UPDATE;
do_freeopt = 1;
+ done_nonexport = false;
}
else if (strcmp(opt->name, "async") == 0)
fsflags |= MNT_ASYNC;
@@ -924,9 +939,11 @@
fsflags |= MNT_SYNCHRONOUS;
else if (strcmp(opt->name, "union") == 0)
fsflags |= MNT_UNION;
- else if (strcmp(opt->name, "export") == 0)
+ else if (strcmp(opt->name, "export") == 0) {
fsflags |= MNT_EXPORTED;
- else if (strcmp(opt->name, "automounted") == 0) {
+ done_nonexport = false;
+ autoro = false;
+ } else if (strcmp(opt->name, "automounted") == 0) {
fsflags |= MNT_AUTOMOUNTED;
do_freeopt = 1;
} else if (strcmp(opt->name, "nocover") == 0) {
@@ -941,9 +958,21 @@
} else if (strcmp(opt->name, "noemptydir") == 0) {
fsflags &= ~MNT_EMPTYDIR;
do_freeopt = 1;
+ } else if (strcmp(opt->name, "fstype") == 0 ||
+ strcmp(opt->name, "fspath") == 0 ||
+ strcmp(opt->name, "from") == 0 ||
+ strcmp(opt->name, "errmsg") == 0) {
+ /*
+ * These four options are used along
+ * with "export" and "update" by
+ * mountd(8) for file system exporting.
+ */
+ done_nonexport = false;
}
if (do_freeopt)
vfs_freeopt(optlist, opt);
+ if (done_nonexport)
+ has_nonexport = true;
}
/*
@@ -956,6 +985,18 @@
goto bail;
}
+ /*
+ * Do not allow "export" to be mixed with any other options
+ * that change behaviour and only allow "export" with "update",
+ * when in a vnet jail.
+ */
+ if ((fsflags & MNT_EXPORTED) != 0 && jailed(td->td_ucred) &&
+ prison_check_nfsd(td->td_ucred) && ((fsflags & MNT_UPDATE) == 0 ||
+ has_nonexport)) {
+ error = EINVAL;
+ goto bail;
+ }
+
error = vfs_domount(td, fstype, fspath, fsflags, &optlist);
if (error == ENOENT) {
error = EINVAL;
@@ -1035,6 +1076,13 @@
*/
flags &= ~MNT_ROOTFS;
+ /*
+ * Do not allow exports to be set with the old syscall, when
+ * inside a vnet jail.
+ */
+ if (jailed(td->td_ucred) && prison_check_nfsd(td->td_ucred))
+ flags &= ~MNT_EXPORTED;
+
fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL);
if (error) {
@@ -1303,7 +1351,18 @@
* Only privileged root, or (if MNT_USER is set) the user that
* did the original mount is permitted to update it.
*/
- error = vfs_suser(mp, td);
+ /*
+ * For the case of mountd(8) doing exports in a jail, don't
+ * call vfs_suser(). vfs_domount() has already checked that
+ * "root" is doing this and vfs_suser() will fail when
+ * the file system has been mounted outside the jail.
+ * vfs_donmount() has ensured that MNT_EXPORTED is not
+ * mixed with other options that change mount behaviour.
+ */
+ error = 0;
+ if ((fsflags & MNT_EXPORTED) == 0 || !jailed(td->td_ucred) ||
+ !prison_check_nfsd(td->td_ucred))
+ error = vfs_suser(mp, td);
if (error != 0) {
vput(vp);
return (error);
@@ -1333,9 +1392,19 @@
error = EBUSY;
goto end;
}
- mp->mnt_flag &= ~MNT_UPDATEMASK;
- mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE |
- MNT_SNAPSHOT | MNT_ROOTFS | MNT_UPDATEMASK | MNT_RDONLY);
+
+ /*
+ * For mountd(8) doing exports from within a jail, only allow the
+ * MNT_EXPORTED flag to be updated.
+ */
+ if ((fsflags & MNT_EXPORTED) != 0 && jailed(td->td_ucred) &&
+ prison_check_nfsd(td->td_ucred)) {
+ mp->mnt_flag |= MNT_EXPORTED;
+ } else {
+ mp->mnt_flag &= ~MNT_UPDATEMASK;
+ mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE |
+ MNT_SNAPSHOT | MNT_ROOTFS | MNT_UPDATEMASK | MNT_RDONLY);
+ }
if ((mp->mnt_flag & MNT_ASYNC) == 0)
mp->mnt_kern_flag &= ~MNTK_ASYNC;
rootvp = vfs_cache_root_clear(mp);
@@ -1348,7 +1417,18 @@
* XXX The final recipients of VFS_MOUNT just overwrite the ndp they
* get. No freeing of cn_pnbuf.
*/
- error = VFS_MOUNT(mp);
+ /*
+ * For the case of mountd(8) doing exports from within a vnet jail,
+ * "from" is typically not set correctly such that VFS_MOUNT() will
+ * return ENOENT. It is not obvious that VFS_MOUNT() ever needs to be
+ * called when mountd is doing exports, but this check only applies to
+ * the specific case where it is running inside a vnet jail, to
+ * avoid any POLA violation.
+ */
+ error = 0;
+ if ((fsflags & MNT_EXPORTED) == 0 || !jailed(td->td_ucred) ||
+ !prison_check_nfsd(td->td_ucred))
+ error = VFS_MOUNT(mp);
export_error = 0;
/* Process the export option. */
@@ -1503,7 +1583,12 @@
if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
return (ENAMETOOLONG);
- if (jailed(td->td_ucred) || usermount == 0) {
+ if ((fsflags & MNT_EXPORTED) != 0 && jailed(td->td_ucred) &&
+ prison_check_nfsd(td->td_ucred)) {
+ error = priv_check(td, PRIV_NFS_DAEMON);
+ if (error)
+ return (error);
+ } else if (jailed(td->td_ucred) || usermount == 0) {
if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0)
return (error);
}

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 19, 12:25 AM (20 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15498070
Default Alt Text
D37741.id115280.diff (6 KB)

Event Timeline