Page MenuHomeFreeBSD

D37741.id117542.diff
No OneTemporary

D37741.id117542.diff

diff --git a/sys/kern/vfs_mount.c.export b/sys/kern/vfs_mount.c
--- a/sys/kern/vfs_mount.c.export
+++ b/sys/kern/vfs_mount.c
@@ -806,7 +806,7 @@
struct vfsopt *opt, *tmp_opt;
char *fstype, *fspath, *errmsg;
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
- bool autoro;
+ bool autoro, has_nonexport;
errmsg = fspath = NULL;
errmsg_len = fspathlen = 0;
@@ -843,113 +843,143 @@
}
/*
+ * 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 to note that options other than those
+ * listed above have been specified.
+ */
+ if (jailed(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
* when we want to update the root filesystem.
*/
+ has_nonexport = false;
TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
int do_freeopt = 0;
if (strcmp(opt->name, "update") == 0) {
fsflags |= MNT_UPDATE;
do_freeopt = 1;
- }
- else if (strcmp(opt->name, "async") == 0)
+ } else if (strcmp(opt->name, "async") == 0) {
fsflags |= MNT_ASYNC;
- else if (strcmp(opt->name, "force") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "force") == 0) {
fsflags |= MNT_FORCE;
do_freeopt = 1;
- }
- else if (strcmp(opt->name, "reload") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "reload") == 0) {
fsflags |= MNT_RELOAD;
do_freeopt = 1;
- }
- else if (strcmp(opt->name, "multilabel") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "multilabel") == 0) {
fsflags |= MNT_MULTILABEL;
- else if (strcmp(opt->name, "noasync") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noasync") == 0) {
fsflags &= ~MNT_ASYNC;
- else if (strcmp(opt->name, "noatime") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noatime") == 0) {
fsflags |= MNT_NOATIME;
- else if (strcmp(opt->name, "atime") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "atime") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonoatime", M_MOUNT);
- }
- else if (strcmp(opt->name, "noclusterr") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noclusterr") == 0) {
fsflags |= MNT_NOCLUSTERR;
- else if (strcmp(opt->name, "clusterr") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "clusterr") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonoclusterr", M_MOUNT);
- }
- else if (strcmp(opt->name, "noclusterw") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noclusterw") == 0) {
fsflags |= MNT_NOCLUSTERW;
- else if (strcmp(opt->name, "clusterw") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "clusterw") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonoclusterw", M_MOUNT);
- }
- else if (strcmp(opt->name, "noexec") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noexec") == 0) {
fsflags |= MNT_NOEXEC;
- else if (strcmp(opt->name, "exec") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "exec") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonoexec", M_MOUNT);
- }
- else if (strcmp(opt->name, "nosuid") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "nosuid") == 0) {
fsflags |= MNT_NOSUID;
- else if (strcmp(opt->name, "suid") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "suid") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonosuid", M_MOUNT);
- }
- else if (strcmp(opt->name, "nosymfollow") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "nosymfollow") == 0) {
fsflags |= MNT_NOSYMFOLLOW;
- else if (strcmp(opt->name, "symfollow") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "symfollow") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("nonosymfollow", M_MOUNT);
- }
- else if (strcmp(opt->name, "noro") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "noro") == 0) {
fsflags &= ~MNT_RDONLY;
autoro = false;
- }
- else if (strcmp(opt->name, "rw") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "rw") == 0) {
fsflags &= ~MNT_RDONLY;
autoro = false;
- }
- else if (strcmp(opt->name, "ro") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "ro") == 0) {
fsflags |= MNT_RDONLY;
autoro = false;
- }
- else if (strcmp(opt->name, "rdonly") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "rdonly") == 0) {
free(opt->name, M_MOUNT);
opt->name = strdup("ro", M_MOUNT);
fsflags |= MNT_RDONLY;
autoro = false;
- }
- else if (strcmp(opt->name, "autoro") == 0) {
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "autoro") == 0) {
do_freeopt = 1;
autoro = true;
- }
- else if (strcmp(opt->name, "suiddir") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "suiddir") == 0) {
fsflags |= MNT_SUIDDIR;
- else if (strcmp(opt->name, "sync") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "sync") == 0) {
fsflags |= MNT_SYNCHRONOUS;
- else if (strcmp(opt->name, "union") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "union") == 0) {
fsflags |= MNT_UNION;
- else if (strcmp(opt->name, "export") == 0)
+ has_nonexport = true;
+ } else if (strcmp(opt->name, "export") == 0) {
fsflags |= MNT_EXPORTED;
- else if (strcmp(opt->name, "automounted") == 0) {
+ autoro = false;
+ } else if (strcmp(opt->name, "automounted") == 0) {
fsflags |= MNT_AUTOMOUNTED;
do_freeopt = 1;
+ has_nonexport = true;
} else if (strcmp(opt->name, "nocover") == 0) {
fsflags |= MNT_NOCOVER;
do_freeopt = 1;
+ has_nonexport = true;
} else if (strcmp(opt->name, "cover") == 0) {
fsflags &= ~MNT_NOCOVER;
do_freeopt = 1;
+ has_nonexport = true;
} else if (strcmp(opt->name, "emptydir") == 0) {
fsflags |= MNT_EMPTYDIR;
do_freeopt = 1;
+ has_nonexport = true;
} else if (strcmp(opt->name, "noemptydir") == 0) {
fsflags &= ~MNT_EMPTYDIR;
do_freeopt = 1;
+ has_nonexport = true;
}
if (do_freeopt)
vfs_freeopt(optlist, opt);
@@ -965,6 +995,16 @@
goto bail;
}
+ /*
+ * In a prison, only allow "export" to be combined with the
+ * options listed in the comment, above.
+ */
+ if ((fsflags & MNT_EXPORTED) != 0 && jailed(td->td_ucred) &&
+ (has_nonexport || (fsflags & MNT_UPDATE) == 0)) {
+ error = EINVAL;
+ goto bail;
+ }
+
error = vfs_domount(td, fstype, fspath, fsflags, &optlist);
if (error == ENOENT) {
error = EINVAL;
@@ -1044,6 +1084,13 @@
*/
flags &= ~MNT_ROOTFS;
+ /*
+ * Do not allow exports to be set with the old syscall, when
+ * inside a jail.
+ */
+ if (jailed(td->td_ucred))
+ flags &= ~MNT_EXPORTED;
+
fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL);
if (error) {
@@ -1312,7 +1359,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);
@@ -1342,9 +1400,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);
@@ -1357,7 +1425,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. */
@@ -1400,7 +1479,7 @@
} else
export_error = EINVAL;
if (export_error == 0)
- export_error = vfs_export(mp, &export);
+ export_error = vfs_export(mp, &export, true);
free(export.ex_groups, M_TEMP);
break;
case (sizeof(export)):
@@ -1422,7 +1501,7 @@
else
export_error = EINVAL;
if (export_error == 0)
- export_error = vfs_export(mp, &export);
+ export_error = vfs_export(mp, &export, true);
free(grps, M_TEMP);
break;
default:
@@ -1512,7 +1591,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, Feb 19, 6:37 AM (10 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28871160
Default Alt Text
D37741.id117542.diff (10 KB)

Event Timeline