In article <20190910145247.c52cc17f...@rebar.astron.com>,
Christos Zoulas <chris...@zoulas.com> wrote:

1. So the consensus is to leave the file descriptor alone.
2. I've added the reverse cache lookup, and now script execution works.
3. Nobody suggested anything to improve security.

Here's the latest patch; if I don't hear objections soon I will commit it...

Index: compat/netbsd32/netbsd32_execve.c
===================================================================
RCS file: /cvsroot/src/sys/compat/netbsd32/netbsd32_execve.c,v
retrieving revision 1.39
diff -u -p -u -r1.39 netbsd32_execve.c
--- compat/netbsd32/netbsd32_execve.c   3 Sep 2018 16:29:29 -0000       1.39
+++ compat/netbsd32/netbsd32_execve.c   15 Sep 2019 00:14:10 -0000
@@ -71,9 +71,8 @@ netbsd32_execve(struct lwp *l, const str
                syscallarg(netbsd32_charpp) argp;
                syscallarg(netbsd32_charpp) envp;
        } */
-       const char *path = SCARG_P32(uap, path);
 
-       return execve1(l, path, SCARG_P32(uap, argp),
+       return execve1(l, SCARG_P32(uap, path), -1, SCARG_P32(uap, argp),
            SCARG_P32(uap, envp), netbsd32_execve_fetch_element);
 }
 
@@ -86,13 +85,9 @@ netbsd32_fexecve(struct lwp *l, const st
                syscallarg(netbsd32_charpp) argp;
                syscallarg(netbsd32_charpp) envp;
        } */
-       struct sys_fexecve_args ua;
 
-       NETBSD32TO64_UAP(fd);
-       NETBSD32TOP_UAP(argp, char * const);
-       NETBSD32TOP_UAP(envp, char * const);
-
-       return sys_fexecve(l, &ua, retval);
+       return execve1(l, NULL, SCARG(uap, fd), SCARG_P32(uap, argp),
+           SCARG_P32(uap, envp), netbsd32_execve_fetch_element);
 }
 
 static int
Index: kern/exec_elf.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf.c,v
retrieving revision 1.98
diff -u -p -u -r1.98 exec_elf.c
--- kern/exec_elf.c     7 Jun 2019 23:35:52 -0000       1.98
+++ kern/exec_elf.c     15 Sep 2019 00:14:10 -0000
@@ -157,6 +157,7 @@ elf_populate_auxv(struct lwp *l, struct 
        size_t len, vlen;
        AuxInfo ai[ELF_AUX_ENTRIES], *a, *execname;
        struct elf_args *ap;
+       char *path = l->l_proc->p_path;
        int error;
 
        a = ai;
@@ -224,9 +225,11 @@ elf_populate_auxv(struct lwp *l, struct 
                a->a_v = l->l_proc->p_stackbase;
                a++;
 
-               execname = a;
-               a->a_type = AT_SUN_EXECNAME;
-               a++;
+               if (path[0] == '/' && path[1] != '\0') {
+                       execname = a;
+                       a->a_type = AT_SUN_EXECNAME;
+                       a++;
+               }
 
                exec_free_emul_arg(pack);
        } else {
@@ -242,7 +245,6 @@ elf_populate_auxv(struct lwp *l, struct 
        KASSERT(vlen <= sizeof(ai));
 
        if (execname) {
-               char *path = l->l_proc->p_path;
                execname->a_v = (uintptr_t)(*stackp + vlen);
                len = strlen(path) + 1;
                if ((error = copyout(path, (*stackp + vlen), len)) != 0)
Index: kern/exec_script.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_script.c,v
retrieving revision 1.79
diff -u -p -u -r1.79 exec_script.c
--- kern/exec_script.c  27 Jan 2019 02:08:43 -0000      1.79
+++ kern/exec_script.c  15 Sep 2019 00:14:10 -0000
@@ -290,7 +290,7 @@ check_shell:
        /* try loading the interpreter */
        if ((error = exec_makepathbuf(l, shellname, UIO_SYSSPACE,
            &shell_pathbuf, NULL)) == 0) {
-               error = check_exec(l, epp, shell_pathbuf);
+               error = check_exec(l, epp, shell_pathbuf, NULL);
                pathbuf_destroy(shell_pathbuf);
        }
 
Index: kern/kern_exec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_exec.c,v
retrieving revision 1.479
diff -u -p -u -r1.479 kern_exec.c
--- kern/kern_exec.c    7 Sep 2019 15:34:44 -0000       1.479
+++ kern/kern_exec.c    15 Sep 2019 00:14:10 -0000
@@ -257,7 +257,7 @@ struct execve_data {
        struct ps_strings       ed_arginfo;
        char                    *ed_argp;
        const char              *ed_pathstring;
-       char                    *ed_resolvedpathbuf;
+       char                    *ed_resolvedname;
        size_t                  ed_ps_strings_sz;
        int                     ed_szsigcode;
        size_t                  ed_argslen;
@@ -309,9 +309,32 @@ exec_path_free(struct execve_data *data)
 {              
        pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring);
        pathbuf_destroy(data->ed_pathbuf);
-       PNBUF_PUT(data->ed_resolvedpathbuf);
+       if (data->ed_resolvedname)
+               PNBUF_PUT(data->ed_resolvedname);
 }
 
+static void
+exec_resolvename(struct lwp *l, struct exec_package *epp, struct vnode *vp,
+    char **rpath)
+{
+       int error;
+       char *p;
+
+       KASSERT(rpath != NULL);
+
+       *rpath = PNBUF_GET();
+       error = vnode_to_path(*rpath, MAXPATHLEN, vp, l, l->l_proc);
+       if (error) {
+               PNBUF_PUT(*rpath);
+               *rpath = NULL;
+               return;
+       }
+       epp->ep_resolvedname = *rpath;
+       if ((p = strrchr(*rpath, '/')) != NULL)
+               epp->ep_kname = p + 1;
+}
+
+
 /*
  * check exec:
  * given an "executable" described in the exec package's namei info,
@@ -339,26 +362,40 @@ exec_path_free(struct execve_data *data)
  */
 int
 /*ARGSUSED*/
-check_exec(struct lwp *l, struct exec_package *epp, struct pathbuf *pb)
+check_exec(struct lwp *l, struct exec_package *epp, struct pathbuf *pb,
+    char **rpath)
 {
        int             error, i;
        struct vnode    *vp;
-       struct nameidata nd;
        size_t          resid;
 
-       // grab the absolute pathbuf here before namei() trashes it.
-       pathbuf_copystring(pb, epp->ep_resolvedname, PATH_MAX);
-       NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb);
+       if (epp->ep_resolvedname) {
+               struct nameidata nd;
 
-       /* first get the vnode */
-       if ((error = namei(&nd)) != 0)
-               return error;
-       epp->ep_vp = vp = nd.ni_vp;
+               // grab the absolute pathbuf here before namei() trashes it.
+               pathbuf_copystring(pb, epp->ep_resolvedname, PATH_MAX);
+               NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb);
 
+               /* first get the vnode */
+               if ((error = namei(&nd)) != 0)
+                       return error;
+
+               epp->ep_vp = vp = nd.ni_vp;
 #ifdef DIAGNOSTIC
-       /* paranoia (take this out once namei stuff stabilizes) */
-       memset(nd.ni_pnbuf, '~', PATH_MAX);
+               /* paranoia (take this out once namei stuff stabilizes) */
+               memset(nd.ni_pnbuf, '~', PATH_MAX);
 #endif
+       } else {
+               struct file *fp;
+
+               if ((error = fd_getvnode(epp->ep_xfd, &fp)) != 0)
+                       return error;
+               epp->ep_vp = vp = fp->f_vnode;
+               vref(vp);
+               fd_putfile(epp->ep_xfd);
+               exec_resolvename(l, epp, vp, rpath);
+               vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+       }
 
        /* check access and type */
        if (vp->v_type != VREG) {
@@ -387,19 +424,21 @@ check_exec(struct lwp *l, struct exec_pa
        /* unlock vp, since we need it unlocked from here on out. */
        VOP_UNLOCK(vp);
 
+       if (epp->ep_resolvedname) {
 #if NVERIEXEC > 0
-       error = veriexec_verify(l, vp, epp->ep_resolvedname,
-           epp->ep_flags & EXEC_INDIR ? VERIEXEC_INDIRECT : VERIEXEC_DIRECT,
-           NULL);
-       if (error)
-               goto bad2;
+               error = veriexec_verify(l, vp, epp->ep_resolvedname,
+                   epp->ep_flags & EXEC_INDIR ? VERIEXEC_INDIRECT
+                   : VERIEXEC_DIRECT, NULL);
+               if (error)
+                       goto bad2;
 #endif /* NVERIEXEC > 0 */
 
 #ifdef PAX_SEGVGUARD
-       error = pax_segvguard(l, vp, epp->ep_resolvedname, false);
-       if (error)
-               goto bad2;
+               error = pax_segvguard(l, vp, epp->ep_resolvedname, false);
+               if (error)
+                       goto bad2;
 #endif /* PAX_SEGVGUARD */
+       }
 
        /* now we have the file, get the exec header */
        error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0,
@@ -545,7 +584,7 @@ sys_execve(struct lwp *l, const struct s
                syscallarg(char * const *)      envp;
        } */
 
-       return execve1(l, SCARG(uap, path), SCARG(uap, argp),
+       return execve1(l, SCARG(uap, path), -1, SCARG(uap, argp),
            SCARG(uap, envp), execve_fetch_element);
 }
 
@@ -559,7 +598,8 @@ sys_fexecve(struct lwp *l, const struct 
                syscallarg(char * const *)      envp;
        } */
 
-       return ENOSYS;
+       return execve1(l, NULL, SCARG(uap, fd), SCARG(uap, argp),
+           SCARG(uap, envp), execve_fetch_element);
 }
 
 /*
@@ -680,7 +720,7 @@ exec_vm_minaddr(vaddr_t va_min)
 }
 
 static int
-execve_loadvm(struct lwp *l, const char *path, char * const *args,
+execve_loadvm(struct lwp *l, const char *path, int fd, char * const *args,
        char * const *envs, execve_fetch_element_t fetch_element,
        struct execve_data * restrict data)
 {
@@ -689,7 +729,6 @@ execve_loadvm(struct lwp *l, const char 
        struct proc             *p;
        char                    *dp;
        u_int                   modgen;
-       size_t                  offs;
 
        KASSERT(data != NULL);
 
@@ -732,24 +771,36 @@ execve_loadvm(struct lwp *l, const char 
         */
        rw_enter(&p->p_reflock, RW_WRITER);
 
-       /*
-        * Init the namei data to point the file user's program name.
-        * This is done here rather than in check_exec(), so that it's
-        * possible to override this settings if any of makecmd/probe
-        * functions call check_exec() recursively - for example,
-        * see exec_script_makecmds().
-        */
-       if ((error = exec_makepathbuf(l, path, UIO_USERSPACE,
-           &data->ed_pathbuf, &offs)) != 0)
-               goto clrflg;
-       data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf);
-       data->ed_resolvedpathbuf = PNBUF_GET();
+       if (path == NULL) {
+               data->ed_pathbuf = pathbuf_assimilate(strcpy(PNBUF_GET(), "/"));
+               data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf);
+               epp->ep_kname = "*fexecve*";
+               data->ed_resolvedname = NULL;
+               epp->ep_resolvedname = NULL;
+               epp->ep_xfd = fd;
+       } else {
+               size_t  offs;
+               /*
+                * Init the namei data to point the file user's program name.
+                * This is done here rather than in check_exec(), so that it's
+                * possible to override this settings if any of makecmd/probe
+                * functions call check_exec() recursively - for example,
+                * see exec_script_makecmds().
+                */
+               if ((error = exec_makepathbuf(l, path, UIO_USERSPACE,
+                   &data->ed_pathbuf, &offs)) != 0)
+                       goto clrflg;
+               data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf);
+               epp->ep_kname = data->ed_pathstring + offs;
+               data->ed_resolvedname = PNBUF_GET();
+               epp->ep_resolvedname = data->ed_resolvedname;
+               epp->ep_xfd = -1;
+       }
+
 
        /*
         * initialize the fields of the exec package.
         */
-       epp->ep_kname = data->ed_pathstring + offs;
-       epp->ep_resolvedname = data->ed_resolvedpathbuf;
        epp->ep_hdr = kmem_alloc(exec_maxhdrsz, KM_SLEEP);
        epp->ep_hdrlen = exec_maxhdrsz;
        epp->ep_hdrvalid = 0;
@@ -768,7 +819,8 @@ execve_loadvm(struct lwp *l, const char 
        rw_enter(&exec_lock, RW_READER);
 
        /* see if we can run it. */
-       if ((error = check_exec(l, epp, data->ed_pathbuf)) != 0) {
+       if ((error = check_exec(l, epp, data->ed_pathbuf,
+           &data->ed_resolvedname)) != 0) {
                if (error != ENOENT && error != EACCES && error != ENOEXEC) {
                        DPRINTF(("%s: check exec failed for %s, error %d\n",
                            __func__, epp->ep_kname, error));
@@ -942,10 +994,19 @@ execve_free_data(struct execve_data *dat
 static void
 pathexec(struct proc *p, const char *resolvedname)
 {
-       KASSERT(resolvedname[0] == '/');
-
        /* set command name & other accounting info */
-       strlcpy(p->p_comm, strrchr(resolvedname, '/') + 1, sizeof(p->p_comm));
+       const char *cmdname;
+
+       if (resolvedname == NULL) {
+               cmdname = "*fexecve*";
+               resolvedname = "/";
+       } else {
+               cmdname = strrchr(resolvedname, '/') + 1;
+       }
+       KASSERTMSG(resolvedname[0] == '/', "bad resolvedname `%s'",
+           resolvedname);
+
+       strlcpy(p->p_comm, cmdname, sizeof(p->p_comm));
 
        kmem_strfree(p->p_path);
        p->p_path = kmem_strdupsize(resolvedname, NULL, KM_SLEEP);
@@ -1346,13 +1407,13 @@ execve_runproc(struct lwp *l, struct exe
 }
 
 int
-execve1(struct lwp *l, const char *path, char * const *args,
+execve1(struct lwp *l, const char *path, int fd, char * const *args,
     char * const *envs, execve_fetch_element_t fetch_element)
 {
        struct execve_data data;
        int error;
 
-       error = execve_loadvm(l, path, args, envs, fetch_element, &data);
+       error = execve_loadvm(l, path, fd, args, envs, fetch_element, &data);
        if (error)
                return error;
        error = execve_runproc(l, &data, false, false);
@@ -2376,7 +2437,7 @@ do_posix_spawn(struct lwp *l1, pid_t *pi
         * Do the first part of the exec now, collect state
         * in spawn_data.
         */
-       error = execve_loadvm(l1, path, argv,
+       error = execve_loadvm(l1, path, -1, argv,
            envp, fetch, &spawn_data->sed_exec);
        if (error == EJUSTRETURN)
                error = 0;
Index: kern/vfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_vnops.c,v
retrieving revision 1.200
diff -u -p -u -r1.200 vfs_vnops.c
--- kern/vfs_vnops.c    7 Mar 2019 11:09:48 -0000       1.200
+++ kern/vfs_vnops.c    15 Sep 2019 00:14:11 -0000
@@ -309,6 +309,9 @@ vn_openchk(struct vnode *vp, kauth_cred_
        if ((fflags & FREAD) != 0) {
                permbits = VREAD;
        }
+       if ((fflags & FEXEC) != 0) {
+               permbits |= VEXEC;
+       }
        if ((fflags & (FWRITE | O_TRUNC)) != 0) {
                permbits |= VWRITE;
                if (vp->v_type == VDIR) {
Index: sys/exec.h
===================================================================
RCS file: /cvsroot/src/sys/sys/exec.h,v
retrieving revision 1.154
diff -u -p -u -r1.154 exec.h
--- sys/exec.h  27 Jan 2019 02:08:50 -0000      1.154
+++ sys/exec.h  15 Sep 2019 00:14:11 -0000
@@ -188,7 +188,8 @@ struct exec_fakearg {
 
 struct exec_package {
        const char *ep_kname;           /* kernel-side copy of file's name */
-       char *ep_resolvedname;          /* fully resolved path from namei */
+       char    *ep_resolvedname;       /* fully resolved path from namei */
+       int     ep_xfd;                 /* fexecve file descriptor */
        void    *ep_hdr;                /* file's exec header */
        u_int   ep_hdrlen;              /* length of ep_hdr */
        u_int   ep_hdrvalid;            /* bytes of ep_hdr that are valid */
@@ -268,7 +269,7 @@ void        setregs                 (struct lwp *, struct 
exe
 int    check_veriexec          (struct lwp *, struct vnode *,
                                     struct exec_package *, int);
 int    check_exec              (struct lwp *, struct exec_package *,
-                                    struct pathbuf *);
+                                    struct pathbuf *, char **);
 int    exec_init               (int);
 int    exec_read_from          (struct lwp *, struct vnode *, u_long off,
                                    void *, size_t);
@@ -301,7 +302,7 @@ void        new_vmcmd(struct exec_vmcmd_set *,
        new_vmcmd(evsp,lwp,len,addr,vp,offset,prot,flags)
 
 typedef        int (*execve_fetch_element_t)(char * const *, size_t, char **);
-int    execve1(struct lwp *, const char *, char * const *, char * const *,
+int    execve1(struct lwp *, const char *, int, char * const *, char * const *,
     execve_fetch_element_t);
 
 struct posix_spawn_file_actions;
Index: sys/fcntl.h
===================================================================
RCS file: /cvsroot/src/sys/sys/fcntl.h,v
retrieving revision 1.50
diff -u -p -u -r1.50 fcntl.h
--- sys/fcntl.h 20 Feb 2018 18:20:05 -0000      1.50
+++ sys/fcntl.h 15 Sep 2019 00:14:11 -0000
@@ -121,6 +121,7 @@
 #if defined(_NETBSD_SOURCE)
 #define        O_NOSIGPIPE     0x01000000      /* don't deliver sigpipe */
 #define        O_REGULAR       0x02000000      /* fail if not a regular file */
+#define        O_EXEC          0x04000000      /* open for executing only */
 #endif
 
 #ifdef _KERNEL
@@ -132,8 +133,9 @@
 #define        O_MASK          
(O_ACCMODE|O_NONBLOCK|O_APPEND|O_SHLOCK|O_EXLOCK|\
                         O_ASYNC|O_SYNC|O_CREAT|O_TRUNC|O_EXCL|O_DSYNC|\
                         O_RSYNC|O_NOCTTY|O_ALT_IO|O_NOFOLLOW|O_DIRECT|\
-                        O_DIRECTORY|O_CLOEXEC|O_NOSIGPIPE|O_REGULAR)
+                        O_DIRECTORY|O_CLOEXEC|O_NOSIGPIPE|O_REGULAR|O_EXEC)
 
+#define        FEXEC           O_EXEC
 #define        FMARK           0x00001000      /* mark during gc() */
 #define        FDEFER          0x00002000      /* defer for next gc pass */
 #define        FHASLOCK        0x00004000      /* descriptor holds advisory 
lock */
@@ -144,7 +146,7 @@
 #define        FCNTLFLAGS      
(FAPPEND|FASYNC|FFSYNC|FNONBLOCK|FDSYNC|FRSYNC|FALTIO|\
                         FDIRECT|FNOSIGPIPE)
 /* bits to save after open(2) */
-#define        FMASK           (FREAD|FWRITE|FCNTLFLAGS)
+#define        FMASK           (FREAD|FWRITE|FCNTLFLAGS|FEXEC)
 #endif /* _KERNEL */
 
 /*

Reply via email to