diff options
-rw-r--r-- | sys/fs/devfs/devfs_vfsops.c | 14 | ||||
-rw-r--r-- | sys/kern/kern_jail.c | 57 | ||||
-rw-r--r-- | sys/sys/jail.h | 3 | ||||
-rw-r--r-- | usr.sbin/jail/jail.8 | 13 |
4 files changed, 82 insertions, 5 deletions
diff --git a/sys/fs/devfs/devfs_vfsops.c b/sys/fs/devfs/devfs_vfsops.c index bba4bfc..9606974 100644 --- a/sys/fs/devfs/devfs_vfsops.c +++ b/sys/fs/devfs/devfs_vfsops.c @@ -44,6 +44,7 @@ #include <sys/sx.h> #include <sys/vnode.h> #include <sys/limits.h> +#include <sys/jail.h> #include <fs/devfs/devfs.h> @@ -69,6 +70,7 @@ devfs_mount(struct mount *mp) int error; struct devfs_mount *fmp; struct vnode *rvp; + struct thread *td = curthread; int rsnum; if (devfs_unr == NULL) @@ -91,6 +93,16 @@ devfs_mount(struct mount *mp) error = EINVAL; } + /* jails enforce their ruleset, prison0 has no restrictions */ + if (td->td_ucred->cr_prison->pr_devfs_rsnum != 0) { + rsnum = td->td_ucred->cr_prison->pr_devfs_rsnum; + if (rsnum == -1) + return (EPERM); + /* check rsnum for sanity, devfs_rsnum is uint16_t */ + if (rsnum < 0 || rsnum > 65535) + error = EINVAL; + } + if (error) { vfs_mount_error(mp, "%s", "invalid ruleset specification"); return (error); @@ -227,4 +239,4 @@ static struct vfsops devfs_vfsops = { .vfs_unmount = devfs_unmount, }; -VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC); +VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC | VFCF_JAIL); diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 9f5e8b8..b800eaa 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -103,6 +103,7 @@ struct prison prison0 = { .pr_uref = 1, .pr_path = "/", .pr_securelevel = -1, + .pr_devfs_rsnum = 0, .pr_childmax = JAIL_MAX, .pr_hostuuid = DEFAULT_HOSTUUID, .pr_children = LIST_HEAD_INITIALIZER(prison0.pr_children), @@ -216,8 +217,10 @@ const size_t pr_allow_nonames_size = sizeof(pr_allow_nonames); #define JAIL_DEFAULT_ALLOW PR_ALLOW_SET_HOSTNAME #define JAIL_DEFAULT_ENFORCE_STATFS 2 +#define JAIL_DEFAULT_DEVFS_RSNUM -1 static unsigned jail_default_allow = JAIL_DEFAULT_ALLOW; static int jail_default_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS; +static int jail_default_devfs_rsnum = JAIL_DEFAULT_DEVFS_RSNUM; #if defined(INET) || defined(INET6) static unsigned jail_max_af_ips = 255; #endif @@ -529,9 +532,9 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) unsigned long hid; size_t namelen, onamelen; int created, cuflags, descend, enforce, error, errmsg_len, errmsg_pos; - int gotchildmax, gotenforce, gothid, gotslevel; + int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel; int fi, jid, jsys, len, level; - int childmax, slevel, vfslocked; + int childmax, rsnum, slevel, vfslocked; int fullpath_disabled; #if defined(INET) || defined(INET6) int ii, ij; @@ -612,6 +615,14 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) } else gotenforce = 1; + error = vfs_copyopt(opts, "devfs_ruleset", &rsnum, sizeof(rsnum)); + if (error == ENOENT) + gotrsnum = 0; + else if (error != 0) + goto done_free; + else + gotrsnum = 1; + pr_flags = ch_flags = 0; for (fi = 0; fi < sizeof(pr_flag_names) / sizeof(pr_flag_names[0]); fi++) { @@ -1268,6 +1279,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) pr->pr_securelevel = ppr->pr_securelevel; pr->pr_allow = JAIL_DEFAULT_ALLOW & ppr->pr_allow; pr->pr_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS; + pr->pr_devfs_rsnum = JAIL_DEFAULT_DEVFS_RSNUM; LIST_INIT(&pr->pr_children); mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK); @@ -1346,6 +1358,27 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) goto done_deref_locked; } } + if (gotrsnum) { + /* + * devfs_rsnum is a uint16_t + * value of -1 disables devfs mounts + */ + if (rsnum < -1 || rsnum > 65535) { + error = EINVAL; + goto done_deref_locked; + } + /* + * Nested jails may inherit parent's devfs ruleset + * or disable devfs + */ + if (jailed(td->td_ucred)) { + if (rsnum > 0 && rsnum != ppr->pr_devfs_rsnum) { + error = EPERM; + goto done_deref_locked; + } else if (rsnum == 0) + rsnum = ppr->pr_devfs_rsnum; + } + } #ifdef INET if (ip4s > 0) { if (ppr->pr_flags & PR_IP4) { @@ -1586,6 +1619,13 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (tpr->pr_enforce_statfs < enforce) tpr->pr_enforce_statfs = enforce; } + if (gotrsnum) { + pr->pr_devfs_rsnum = rsnum; + /* Pass this restriction on to the children. */ + FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) + if (tpr->pr_devfs_rsnum != -1) + tpr->pr_devfs_rsnum = rsnum; + } if (name != NULL) { if (ppr == &prison0) strlcpy(pr->pr_name, name, sizeof(pr->pr_name)); @@ -2020,6 +2060,10 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) sizeof(pr->pr_enforce_statfs)); if (error != 0 && error != ENOENT) goto done_deref; + error = vfs_setopt(opts, "devfs_ruleset", &pr->pr_devfs_rsnum, + sizeof(pr->pr_devfs_rsnum)); + if (error != 0 && error != ENOENT) + goto done_deref; for (fi = 0; fi < sizeof(pr_flag_names) / sizeof(pr_flag_names[0]); fi++) { if (pr_flag_names[fi] == NULL) @@ -4173,6 +4217,12 @@ SYSCTL_PROC(_security_jail, OID_AUTO, enforce_statfs, sysctl_jail_default_level, "I", "Processes in jail cannot see all mounted file systems"); +SYSCTL_PROC(_security_jail, OID_AUTO, devfs_ruleset, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, + &jail_default_devfs_rsnum, offsetof(struct prison, pr_devfs_rsnum), + sysctl_jail_default_level, "I", + "Ruleset for the devfs filesystem in jail"); + /* * Nodes to describe jail parameters. Maximum length of string parameters * is returned in the string itself, and the other parameters exist merely @@ -4221,6 +4271,8 @@ SYSCTL_JAIL_PARAM(, securelevel, CTLTYPE_INT | CTLFLAG_RW, "I", "Jail secure level"); SYSCTL_JAIL_PARAM(, enforce_statfs, CTLTYPE_INT | CTLFLAG_RW, "I", "Jail cannot see all mounted file systems"); +SYSCTL_JAIL_PARAM(, devfs_ruleset, CTLTYPE_INT | CTLFLAG_RW, + "I", "Ruleset for in-jail devfs mounts"); SYSCTL_JAIL_PARAM(, persist, CTLTYPE_INT | CTLFLAG_RW, "B", "Jail persistence"); #ifdef VIMAGE @@ -4413,6 +4465,7 @@ db_show_prison(struct prison *pr) #endif db_printf(" root = %p\n", pr->pr_root); db_printf(" securelevel = %d\n", pr->pr_securelevel); + db_printf(" devfs_rsnum = %d\n", pr->pr_devfs_rsnum); db_printf(" children.max = %d\n", pr->pr_childmax); db_printf(" children.cur = %d\n", pr->pr_childcount); db_printf(" child = %p\n", LIST_FIRST(&pr->pr_children)); diff --git a/sys/sys/jail.h b/sys/sys/jail.h index bbaf381..565efa8 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -176,7 +176,8 @@ struct prison { unsigned pr_allow; /* (p) PR_ALLOW_* flags */ int pr_securelevel; /* (p) securelevel */ int pr_enforce_statfs; /* (p) statfs permission */ - int pr_spare[5]; + int pr_devfs_rsnum; /* (p) devfs ruleset */ + int pr_spare[4]; unsigned long pr_hostid; /* (p) jail hostid */ char pr_name[MAXHOSTNAMELEN]; /* (p) admin jail name */ char pr_path[MAXPATHLEN]; /* (c) chroot path */ diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index c5a2245..5cd77fc 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 23, 2011 +.Dd February 9, 2012 .Dt JAIL 8 .Os .Sh NAME @@ -301,6 +301,17 @@ A jail never has a lower securelevel than the default system, but by setting this parameter it may have a higher one. If the system securelevel is changed, any jail securelevels will be at least as secure. +.It Va devfs_ruleset +The number of the devfs ruleset that is enforced for mounting devfs in +this jail and its descendants. A value of zero means no ruleset is enforced +or if set inside a jail for a descendant jail, the parent jails's devfs +ruleset enforcement is inherited. A value of -1 (default) means mounting a +devfs filesystem is not allowed. Mounting devfs inside a jail is possible +only if the +.Va allow.mount +permission is effective and +.Va enforce_statfs +is set to a value lower than 2. .It Va children.max The number of child jails allowed to be created by this jail (or by other jails under this jail). |