Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142339646
D34579.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D34579.diff
View Options
diff --git a/head/sys/kern/uipc_usrreq.c b/head/sys/kern/uipc_usrreq.c
--- a/head/sys/kern/uipc_usrreq.c
+++ b/head/sys/kern/uipc_usrreq.c
@@ -160,6 +160,7 @@
static u_long unpdg_recvspace = 16*1024; /* support 8KB syslog msgs */
static u_long unpsp_sendspace = PIPSIZ; /* really max datagram size */
static u_long unpsp_recvspace = PIPSIZ;
+static int sendfd_check_root = 1;
static SYSCTL_NODE(_net, PF_LOCAL, local, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Local domain");
@@ -185,6 +186,9 @@
&unpsp_sendspace, 0, "Default seqpacket send space.");
SYSCTL_ULONG(_net_local_seqpacket, OID_AUTO, recvspace, CTLFLAG_RW,
&unpsp_recvspace, 0, "Default seqpacket receive space.");
+SYSCTL_INT(_net_local, OID_AUTO, sendfd_check_root, CTLFLAG_RW,
+ &sendfd_check_root, 0,
+ "recvmsg() will deny SCM_RIGHTS with outside-root directories.");
SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0,
"File descriptors in flight.");
SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD,
@@ -2006,6 +2010,36 @@
}
static int
+verify_dir_fds(struct filedescent **fdep, int n, int strict)
+{
+ struct pwd *pwd;
+ struct vnode *dp;
+ struct file *fp;
+ int i, error;
+
+ if (n == 0)
+ return (0);
+ pwd = pwd_hold(curthread);
+ error = 0;
+ for (i=0; i<n; i++, fdep++) {
+ fp = (*fdep)->fde_file;
+ if (fp == NULL || fp->f_ops == &badfileops) {
+ error = EBADF;
+ break;
+ }
+ if ((dp = fp->f_vnode) == NULL || (dp->v_type != VDIR))
+ continue;
+ vrefact(dp);
+ error = vn_dir_check_root(dp, pwd->pwd_rdir, pwd->pwd_jdir,
+ strict);
+ if (error != 0)
+ break;
+ }
+ pwd_drop(pwd);
+ return (error);
+}
+
+static int
unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
{
struct thread *td = curthread; /* XXX */
@@ -2043,6 +2077,16 @@
unp_freerights(fdep, newfds);
goto next;
}
+
+ /* Check chrooted access to directory fds */
+ if (sendfd_check_root &&
+ verify_dir_fds(fdep, newfds,
+ (sendfd_check_root > 1)) != 0) {
+ error = EPERM;
+ unp_freerights(fdep, newfds);
+ goto next;
+ }
+
FILEDESC_XLOCK(fdesc);
/*
diff --git a/head/sys/kern/vfs_lookup.c b/head/sys/kern/vfs_lookup.c
--- a/head/sys/kern/vfs_lookup.c
+++ b/head/sys/kern/vfs_lookup.c
@@ -1513,6 +1513,109 @@
}
/*
+ * Check if a vnode is under root-vnode, to prevent various chroot-breaks
+ * Input: referenced unlocked 'dp', will unref it
+ * return: 0 = ok, vnode is under current root
+ * EJUSTRETURN = vnode is outside current root
+ * other values = got a error during check
+ */
+int
+vn_dir_check_root(struct vnode *dp, struct vnode *rootdp, struct vnode *topdp,
+ int strict)
+{
+ struct vnode *pardp;
+ struct prison *pr;
+ int error;
+ struct componentname cn;
+
+ cn.cn_nameiop = LOOKUP;
+ cn.cn_cred = curthread->td_ucred;
+ vn_lock(dp,
+ compute_cn_lkflags(dp->v_mount, LK_SHARED | LK_RETRY, 0));
+ for (;;) {
+ if (VN_IS_DOOMED(dp)) {
+ vput(dp);
+ return (ENOENT);
+ }
+ MPASS(dp->v_type == VDIR);
+ /* XXX not sure if NULL valid here for VOP_LOOKUP */
+ cn.cn_pnbuf = NULL;
+ cn.cn_nameptr = "../../../";
+ cn.cn_namelen = 2;
+ cn.cn_flags = MAKEENTRY | ISDOTDOT;
+ if (strict == 0)
+ cn.cn_flags |= NOEXECCHECK;
+#ifdef INVARIANTS
+ cn.cn_origflags = cn.cn_flags;
+#endif
+
+ for (;;) {
+ /* check if we are at requested root */
+ if (dp == rootdp) {
+ vput(dp);
+ return (0);
+ }
+ /* check for local (chrooted/jailed) or global root */
+ for (pr = cn.cn_cred->cr_prison; pr != NULL;
+ pr = pr->pr_parent)
+ if (dp == pr->pr_root)
+ break;
+ if (dp == topdp || dp == rootvnode || pr != NULL) {
+ printf("caught out-of-chroot vnode, pid %d\n",
+ (int)curthread->td_proc->p_pid);
+ vput(dp);
+ return (EJUSTRETURN);
+ }
+ /*
+ * Check if we are at mounted filesystem root.
+ * If so, replace the vnode with underlying mountpoint
+ * and do root-node checks again before traversing ../
+ */
+ if ((dp->v_vflag & VV_ROOT) == 0)
+ break;
+ if (VN_IS_DOOMED(dp) ||
+ (pardp = dp->v_mount->mnt_vnodecovered) == NULL ||
+ pardp->v_mountedhere != dp->v_mount) {
+ /* forced unmount */
+ vput(dp);
+ return (ENOENT);
+ }
+ VREF(pardp);
+ vput(dp);
+ dp = pardp;
+ vn_lock(dp, compute_cn_lkflags(dp->v_mount,
+ LK_SHARED | LK_RETRY, ISDOTDOT));
+ }
+
+ ASSERT_VOP_LOCKED(dp, "lookup_find_root");
+ if (VN_IS_DOOMED(dp)) {
+ vput(dp);
+ return (ENOENT);
+ }
+ cn.cn_lkflags =
+ compute_cn_lkflags(dp->v_mount, LK_SHARED, cn.cn_flags);
+ pardp = NULL;
+ if ((error = VOP_LOOKUP(dp, &pardp, &cn)) != 0) {
+ KASSERT(pardp == NULL, ("leaf should be empty"));
+ if (error == ERELOOKUP)
+ continue;
+ MPASS(error != EJUSTRETURN);
+ vput(dp);
+ return (error);
+ }
+
+ MPASS(pardp != NULL);
+ if (dp == pardp) {
+ vrele(dp);
+ vput(pardp);
+ return (ENOENT);
+ }
+ vput(dp);
+ dp = pardp;
+ }
+}
+
+/*
* Free data allocated by namei(); see namei(9) for details.
*/
void
diff --git a/head/sys/sys/vnode.h b/head/sys/sys/vnode.h
--- a/head/sys/sys/vnode.h
+++ b/head/sys/sys/vnode.h
@@ -1112,6 +1112,8 @@
void vn_fsid(struct vnode *vp, struct vattr *va);
int vn_dir_check_exec(struct vnode *vp, struct componentname *cnp);
+int vn_dir_check_root(struct vnode *dp, struct vnode *rootdp,
+ struct vnode *topdp, int strict);
int vn_lktype_write(struct mount *mp, struct vnode *vp);
#define VOP_UNLOCK_FLAGS(vp, flags) ({ \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 19, 7:16 PM (1 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27751412
Default Alt Text
D34579.diff (5 KB)
Attached To
Mode
D34579: Verify directory fds against chroot when receiving them through SCM_RIGHTS
Attached
Detach File
Event Timeline
Log In to Comment