Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145310394
D37741.id117542.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D37741.id117542.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D37741: Modify vfs_mount.c so that mountd can run in a vnet prison
Attached
Detach File
Event Timeline
Log In to Comment