From f63d2e920584a3d403a07e765a61eeac57210332 Mon Sep 17 00:00:00 2001 From: jonathan Date: Sat, 13 Aug 2011 09:21:16 +0000 Subject: Allow Capsicum capabilities to delegate constrained access to file system subtrees to sandboxed processes. - Use of absolute paths and '..' are limited in capability mode. - Use of absolute paths and '..' are limited when looking up relative to a capability. - When a name lookup is performed, identify what operation is to be performed (such as CAP_MKDIR) as well as check for CAP_LOOKUP. With these constraints, openat() and friends are now safe in capability mode, and can then be used by code such as the capability-mode runtime linker. Approved by: re (bz), mentor (rwatson) Sponsored by: Google Inc --- sys/kern/vfs_lookup.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'sys/kern/vfs_lookup.c') diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index ae8982f..a145017 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -180,6 +180,18 @@ namei(struct nameidata *ndp) if (!error && *cnp->cn_pnbuf == '\0') error = ENOENT; +#ifdef CAPABILITY_MODE + /* + * In capability mode, lookups must be "strictly relative" (i.e. + * not an absolute path, and not containing '..' components) to + * a real file descriptor, not the pseudo-descriptor AT_FDCWD. + */ + if (IN_CAPABILITY_MODE(td)) { + ndp->ni_strictrelative = 1; + if (ndp->ni_dirfd == AT_FDCWD) + error = ECAPMODE; + } +#endif if (error) { uma_zfree(namei_zone, cnp->cn_pnbuf); #ifdef DIAGNOSTIC @@ -214,12 +226,20 @@ namei(struct nameidata *ndp) AUDIT_ARG_ATFD1(ndp->ni_dirfd); if (cnp->cn_flags & AUDITVNODE2) AUDIT_ARG_ATFD2(ndp->ni_dirfd); -#ifdef CAPABILITY_MODE - KASSERT(!IN_CAPABILITY_MODE(td), - ("%s: reached %s:%d in capability mode", - __func__, __FILE__, __LINE__)); + error = fgetvp_rights(td, ndp->ni_dirfd, + ndp->ni_rightsneeded | CAP_LOOKUP, + &(ndp->ni_baserights), &dp); +#ifdef CAPABILITIES + /* + * Lookups relative to a capability must also be + * strictly relative. + * + * Note that a capability with rights CAP_MASK_VALID + * is treated exactly like a regular file descriptor. + */ + if (ndp->ni_baserights != CAP_MASK_VALID) + ndp->ni_strictrelative = 1; #endif - error = fgetvp(td, ndp->ni_dirfd, 0, &dp); } if (error != 0 || dp != NULL) { FILEDESC_SUNLOCK(fdp); @@ -261,6 +281,8 @@ namei(struct nameidata *ndp) if (*(cnp->cn_nameptr) == '/') { vrele(dp); VFS_UNLOCK_GIANT(vfslocked); + if (ndp->ni_strictrelative != 0) + return (ENOTCAPABLE); while (*(cnp->cn_nameptr) == '/') { cnp->cn_nameptr++; ndp->ni_pathlen--; @@ -604,7 +626,10 @@ dirloop: } /* - * Handle "..": four special cases. + * Handle "..": five special cases. + * 0. If doing a capability lookup, return ENOTCAPABLE (this is a + * fairly conservative design choice, but it's the only one that we + * are satisfied guarantees the property we're looking for). * 1. Return an error if this is the last component of * the name and the operation is DELETE or RENAME. * 2. If at root directory (e.g. after chroot) @@ -618,6 +643,10 @@ dirloop: * the jail or chroot, don't let them out. */ if (cnp->cn_flags & ISDOTDOT) { + if (ndp->ni_strictrelative != 0) { + error = ENOTCAPABLE; + goto bad; + } if ((cnp->cn_flags & ISLASTCN) != 0 && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { error = EINVAL; -- cgit v1.1