Page MenuHomeFreeBSD

D34579.diff
No OneTemporary

D34579.diff

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

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)

Event Timeline