]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_lookup.c
xnu-4903.231.4.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_lookup.c
index 09bd470a99a30aa63493011100713210062a801a..ccee2e1c59a14ce37e65329d4793d7632e4f071e 100644 (file)
 #define VOLFS_MIN_PATH_LEN  9
 
 
-static void kdebug_lookup(struct vnode *dp, struct componentname *cnp);
-
 #if CONFIG_VOLFS
 static int vfs_getrealpath(const char * path, char * realpath, size_t bufsize, vfs_context_t ctx);
 #define MAX_VOLFS_RESTARTS 5
@@ -182,6 +180,7 @@ namei(struct nameidata *ndp)
 #if CONFIG_VOLFS
        int volfs_restarts = 0;
 #endif
+       size_t bytes_copied = 0;
 
        fdp = p->p_fd;
 
@@ -244,10 +243,10 @@ vnode_recycled:
 retry_copy:
        if (UIO_SEG_IS_USER_SPACE(ndp->ni_segflg)) {
                error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
-                           cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+                           cnp->cn_pnlen, &bytes_copied);
        } else {
                error = copystr(CAST_DOWN(void *, ndp->ni_dirp), cnp->cn_pnbuf,
-                           cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+                           cnp->cn_pnlen, &bytes_copied);
        }
        if (error == ENAMETOOLONG && !(cnp->cn_flags & HASBUF)) {
                MALLOC_ZONE(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
@@ -258,11 +257,14 @@ retry_copy:
 
                cnp->cn_flags |= HASBUF;
                cnp->cn_pnlen = MAXPATHLEN;
+               bytes_copied = 0;
                
                goto retry_copy;
        }
        if (error)
                goto error_out;
+       ndp->ni_pathlen = bytes_copied;
+       bytes_copied = 0;
 
        /*
         * Since the name cache may contain positive entries of
@@ -366,6 +368,21 @@ retry_copy:
        ndp->ni_vp  = NULLVP;
 
        for (;;) {
+#if CONFIG_MACF
+               /*
+                * Give MACF policies a chance to reject the lookup
+                * before performing any filesystem operations.
+                * This hook is called before resolving the path and
+                * again each time a symlink is encountered.
+                * NB: policies receive path information as supplied
+                *     by the caller and thus cannot be trusted.
+                */
+               error = mac_vnode_check_lookup_preflight(ctx, dp, cnp->cn_nameptr, cnp->cn_namelen);
+               if (error) {
+                       goto error_out;
+               }
+#endif
+
                ndp->ni_startdir = dp;
 
                if ( (error = lookup(ndp)) ) {
@@ -458,6 +475,7 @@ namei_compound_available(vnode_t dp, struct nameidata *ndp)
 
        return 0;
 }
+
 static int
 lookup_authorize_search(vnode_t dp, struct componentname *cnp, int dp_authorized_in_cache, vfs_context_t ctx)
 {
@@ -531,6 +549,7 @@ lookup_handle_rsrc_fork(vnode_t dp, struct nameidata *ndp, struct componentname
 {
        vnode_t svp = NULLVP;
        enum nsoperation nsop;
+       int nsflags;
        int error;
 
        if (dp->v_type != VREG) {
@@ -567,8 +586,13 @@ lookup_handle_rsrc_fork(vnode_t dp, struct nameidata *ndp, struct componentname
                        error = EPERM;
                        goto out;
        }
+
+       nsflags = 0;
+       if (cnp->cn_flags & CN_RAW_ENCRYPTED)
+               nsflags |= NS_GETRAWENCRYPTED;
+
        /* Ask the file system for the resource fork. */
-       error = vnode_getnamedstream(dp, &svp, XATTR_RESOURCEFORK_NAME, nsop, 0, ctx);
+       error = vnode_getnamedstream(dp, &svp, XATTR_RESOURCEFORK_NAME, nsop, nsflags, ctx);
 
        /* During a create, it OK for stream vnode to be missing. */
        if (error == ENOATTR || error == ENOENT) {
@@ -990,6 +1014,40 @@ dirloop:
         *    .. in the other file system.
         */
        if ( (cnp->cn_flags & ISDOTDOT) ) {
+               /*
+                * if this is a chroot'ed process, check if the current
+                * directory is still a subdirectory of the process's
+                * root directory.
+                */
+               if (ndp->ni_rootdir && (ndp->ni_rootdir != rootvnode) &&
+                   dp !=  ndp->ni_rootdir) {
+                       int sdir_error;
+                       int is_subdir = FALSE;
+
+                       sdir_error = vnode_issubdir(dp, ndp->ni_rootdir,
+                           &is_subdir, vfs_context_kernel());
+
+                       /*
+                        * If we couldn't determine if dp is a subdirectory of
+                        * ndp->ni_rootdir (sdir_error != 0), we let the request
+                        * proceed.
+                        */
+                       if (!sdir_error && !is_subdir) {
+                               vnode_put(dp);
+                               dp = ndp->ni_rootdir;
+                               /*
+                                * There's a ref on the process's root directory
+                                * but we can't use vnode_getwithref here as
+                                * there is nothing preventing that ref being
+                                * released by another thread.
+                                */
+                               if (vnode_get(dp)) {
+                                       error = ENOENT;
+                                       goto bad;
+                               }
+                       }
+               }
+
                for (;;) {
                        if (dp == ndp->ni_rootdir || dp == rootvnode) {
                                ndp->ni_dvp = dp;
@@ -1419,7 +1477,14 @@ lookup_handle_symlink(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx)
        int error;
        char *cp;               /* pointer into pathname argument */
        uio_t auio;
-       char uio_buf[ UIO_SIZEOF(1) ];
+       union {
+               union {
+                       struct user_iovec s_uiovec;
+                       struct kern_iovec s_kiovec;
+               } u_iovec;
+               struct uio s_uio;
+               char uio_buf[ UIO_SIZEOF(1) ];
+       } u_uio_buf; /* union only for aligning uio_buf correctly */
        int need_newpathbuf;
        u_int linklen;
        struct componentname *cnp = &ndp->ni_cnd;
@@ -1446,7 +1511,8 @@ lookup_handle_symlink(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx)
        } else {
                cp = cnp->cn_pnbuf;
        }
-       auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+       auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
+           &u_uio_buf.uio_buf[0], sizeof(u_uio_buf.uio_buf));
 
        uio_addiov(auio, CAST_USER_ADDR_T(cp), MAXPATHLEN);
 
@@ -1678,24 +1744,33 @@ nameidone(struct nameidata *ndp)
 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_IST)
 
 void
-kdebug_lookup_gen_events(long *dbg_parms, int dbg_namelen, void *dp, boolean_t lookup)
+kdebug_vfs_lookup(long *dbg_parms, int dbg_namelen, void *dp, uint32_t flags)
 {
        int code;
        unsigned int i;
+       bool lookup = flags & KDBG_VFS_LOOKUP_FLAG_LOOKUP;
+       bool noprocfilt = flags & KDBG_VFS_LOOKUP_FLAG_NOPROCFILT;
 
        /*
         * In the event that we collect multiple, consecutive pathname
         * entries, we must mark the start of the path's string and the end.
         */
-       if (lookup == TRUE)
+       if (lookup) {
                code = VFS_LOOKUP | DBG_FUNC_START;
-       else
+       } else {
                code = VFS_LOOKUP_DONE | DBG_FUNC_START;
+       }
 
        if (dbg_namelen <= (int)(3 * sizeof(long)))
                code |= DBG_FUNC_END;
 
-       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, code, VM_KERNEL_ADDRPERM(dp), dbg_parms[0], dbg_parms[1], dbg_parms[2], 0);
+       if (noprocfilt) {
+               KDBG_RELEASE_NOPROCFILT(code, kdebug_vnode(dp), dbg_parms[0],
+                               dbg_parms[1], dbg_parms[2]);
+       } else {
+               KDBG_RELEASE(code, kdebug_vnode(dp), dbg_parms[0], dbg_parms[1],
+                               dbg_parms[2]);
+       }
 
        code &= ~DBG_FUNC_START;
 
@@ -1703,11 +1778,25 @@ kdebug_lookup_gen_events(long *dbg_parms, int dbg_namelen, void *dp, boolean_t l
                if (dbg_namelen <= (int)(4 * sizeof(long)))
                        code |= DBG_FUNC_END;
 
-               KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, code, dbg_parms[i], dbg_parms[i+1], dbg_parms[i+2], dbg_parms[i+3], 0);
+               if (noprocfilt) {
+                       KDBG_RELEASE_NOPROCFILT(code, dbg_parms[i], dbg_parms[i + 1],
+                                       dbg_parms[i + 2], dbg_parms[i + 3]);
+               } else {
+                       KDBG_RELEASE(code, dbg_parms[i], dbg_parms[i + 1], dbg_parms[i + 2],
+                                       dbg_parms[i + 3]);
+               }
        }
 }
 
-static void
+void
+kdebug_lookup_gen_events(long *dbg_parms, int dbg_namelen, void *dp,
+               boolean_t lookup)
+{
+       kdebug_vfs_lookup(dbg_parms, dbg_namelen, dp,
+                       lookup ? KDBG_VFS_LOOKUP_FLAG_LOOKUP : 0);
+}
+
+void
 kdebug_lookup(vnode_t dp, struct componentname *cnp)
 {
        int dbg_namelen;
@@ -1731,13 +1820,15 @@ kdebug_lookup(vnode_t dp, struct componentname *cnp)
                       *(cnp->cn_nameptr + cnp->cn_namelen) ? '>' : 0,
                       sizeof(dbg_parms) - dbg_namelen);
        }
-       kdebug_lookup_gen_events(dbg_parms, dbg_namelen, (void *)dp, TRUE);
-}      
+       kdebug_vfs_lookup(dbg_parms, dbg_namelen, (void *)dp,
+                       KDBG_VFS_LOOKUP_FLAG_LOOKUP);
+}
 
 #else /* (KDEBUG_LEVEL >= KDEBUG_LEVEL_IST) */
 
 void
-kdebug_lookup_gen_events(long *dbg_parms __unused, int dbg_namelen __unused, void *dp __unused)
+kdebug_vfs_lookup(long *dbg_parms __unused, int dbg_namelen __unused,
+               void *dp __unused, __unused uint32_t flags)
 {
 }