mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
imgact_binmisc: Optionally pre-open the interpreter vnode
This allows the use of chroot and/or jail environments which depend on interpreters registed with imgact_binmisc to use emulator binaries from the host to emulate programs inside the chroot. Reviewed by: imp MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D37432
This commit is contained in:
parent
cef3c4e0ba
commit
5eeb4f737f
6 changed files with 83 additions and 11 deletions
|
|
@ -30,15 +30,18 @@ __FBSDID("$FreeBSD$");
|
|||
#include <sys/param.h>
|
||||
#include <sys/ctype.h>
|
||||
#include <sys/exec.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/imgact.h>
|
||||
#include <sys/imgact_binmisc.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include <machine/atomic.h>
|
||||
|
||||
|
|
@ -63,6 +66,7 @@ typedef struct imgact_binmisc_entry {
|
|||
uint8_t *ibe_magic;
|
||||
uint8_t *ibe_mask;
|
||||
uint8_t *ibe_interpreter;
|
||||
struct vnode *ibe_interpreter_vnode;
|
||||
ssize_t ibe_interp_offset;
|
||||
uint32_t ibe_interp_argcnt;
|
||||
uint32_t ibe_interp_length;
|
||||
|
|
@ -114,7 +118,7 @@ static struct sx interp_list_sx;
|
|||
* Populate the entry with the information about the interpreter.
|
||||
*/
|
||||
static void
|
||||
imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe)
|
||||
imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe, int flags)
|
||||
{
|
||||
uint32_t len = 0, argc = 1;
|
||||
char t[IBE_INTERP_LEN_MAX];
|
||||
|
|
@ -150,6 +154,30 @@ imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe)
|
|||
memcpy(ibe->ibe_interpreter, t, len);
|
||||
ibe->ibe_interp_argcnt = argc;
|
||||
ibe->ibe_interp_length = len;
|
||||
|
||||
ibe->ibe_interpreter_vnode = NULL;
|
||||
if (flags & IBF_PRE_OPEN) {
|
||||
struct nameidata nd;
|
||||
int error;
|
||||
|
||||
tp = t;
|
||||
while (*tp != '\0' && *tp != ' ') {
|
||||
tp++;
|
||||
}
|
||||
*tp = '\0';
|
||||
NDINIT(&nd, LOOKUP, FOLLOW | ISOPEN, UIO_SYSSPACE, t);
|
||||
|
||||
/*
|
||||
* If there is an error, just stop now and fall back
|
||||
* to the non pre-open case where we lookup during
|
||||
* exec.
|
||||
*/
|
||||
error = namei(&nd);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
ibe->ibe_interpreter_vnode = nd.ni_vp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -167,7 +195,7 @@ imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe, ssize_t interp_offset,
|
|||
ibe->ibe_name = malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO);
|
||||
strlcpy(ibe->ibe_name, xbe->xbe_name, namesz);
|
||||
|
||||
imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe);
|
||||
imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe, xbe->xbe_flags);
|
||||
|
||||
ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO);
|
||||
memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize);
|
||||
|
|
@ -199,6 +227,8 @@ imgact_binmisc_destroy_entry(imgact_binmisc_entry_t *ibe)
|
|||
free(ibe->ibe_interpreter, M_BINMISC);
|
||||
if (ibe->ibe_name)
|
||||
free(ibe->ibe_name, M_BINMISC);
|
||||
if (ibe->ibe_interpreter_vnode)
|
||||
vrele(ibe->ibe_interpreter_vnode);
|
||||
if (ibe)
|
||||
free(ibe, M_BINMISC);
|
||||
}
|
||||
|
|
@ -271,15 +301,20 @@ imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Preallocate a new entry. We do this without holding the
|
||||
* lock to avoid lock-order problems if IBF_PRE_OPEN is
|
||||
* set.
|
||||
*/
|
||||
ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt);
|
||||
|
||||
INTERP_LIST_WLOCK();
|
||||
if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) {
|
||||
INTERP_LIST_WUNLOCK();
|
||||
imgact_binmisc_destroy_entry(ibe);
|
||||
return (EEXIST);
|
||||
}
|
||||
|
||||
/* Preallocate a new entry. */
|
||||
ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt);
|
||||
|
||||
SLIST_INSERT_HEAD(&interpreter_list, ibe, link);
|
||||
interp_list_entry_count++;
|
||||
INTERP_LIST_WUNLOCK();
|
||||
|
|
@ -698,6 +733,10 @@ imgact_binmisc_exec(struct image_params *imgp)
|
|||
/* Catch ibe->ibe_argv0_cnt counting more #a than we did. */
|
||||
MPASS(ibe->ibe_argv0_cnt == argv0_cnt);
|
||||
imgp->interpreter_name = imgp->args->begin_argv;
|
||||
if (ibe->ibe_interpreter_vnode) {
|
||||
imgp->interpreter_vp = ibe->ibe_interpreter_vnode;
|
||||
vref(imgp->interpreter_vp);
|
||||
}
|
||||
|
||||
done:
|
||||
INTERP_LIST_RUNLOCK();
|
||||
|
|
|
|||
|
|
@ -504,6 +504,18 @@ interpret:
|
|||
imgp->execpath = args->fname;
|
||||
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
|
||||
}
|
||||
} else if (imgp->interpreter_vp) {
|
||||
/*
|
||||
* An image activator has already provided an open vnode
|
||||
*/
|
||||
newtextvp = imgp->interpreter_vp;
|
||||
imgp->interpreter_vp = NULL;
|
||||
if (vn_fullpath(newtextvp, &imgp->execpath,
|
||||
&imgp->freepath) != 0)
|
||||
imgp->execpath = args->fname;
|
||||
vn_lock(newtextvp, LK_SHARED | LK_RETRY);
|
||||
AUDIT_ARG_VNODE1(newtextvp);
|
||||
imgp->vp = newtextvp;
|
||||
} else {
|
||||
AUDIT_ARG_FD(args->fd);
|
||||
|
||||
|
|
@ -702,7 +714,11 @@ interpret:
|
|||
free(imgp->freepath, M_TEMP);
|
||||
imgp->freepath = NULL;
|
||||
/* set new name to that of the interpreter */
|
||||
args->fname = imgp->interpreter_name;
|
||||
if (imgp->interpreter_vp) {
|
||||
args->fname = NULL;
|
||||
} else {
|
||||
args->fname = imgp->interpreter_name;
|
||||
}
|
||||
goto interpret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ struct image_params {
|
|||
u_int map_flags;
|
||||
#define IMGP_ASLR_SHARED_PAGE 0x1
|
||||
uint32_t imgp_flags;
|
||||
struct vnode *interpreter_vp; /* vnode of the interpreter */
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ _Static_assert(IBE_MAGIC_MAX <= IBE_MATCH_MAX,
|
|||
*/
|
||||
#define IBF_ENABLED 0x0001 /* Entry is active. */
|
||||
#define IBF_USE_MASK 0x0002 /* Use mask on header magic field. */
|
||||
#define IBF_PRE_OPEN 0x0004 /* Cache the vnode for interpreter */
|
||||
|
||||
#define IBF_VALID_UFLAGS 0x0003 /* Bits allowed from userland. */
|
||||
#define IBF_VALID_UFLAGS 0x0007 /* Bits allowed from userland. */
|
||||
|
||||
/*
|
||||
* Used with sysctlbyname() to pass imgact bin misc entries in and out of the
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
.Op Fl -mask Ar mask
|
||||
.Op Fl -offset Ar offset
|
||||
.Op Fl -set-enabled
|
||||
.Op Fl -pre-open
|
||||
.Nm
|
||||
.Cm disable
|
||||
.Ar name
|
||||
|
|
@ -87,6 +88,7 @@ Operation must be one of the following:
|
|||
.Op Fl -mask Ar mask
|
||||
.Op Fl -offset Ar offset
|
||||
.Op Fl -set-enabled
|
||||
.Op Fl -pre-open
|
||||
.Xc
|
||||
Add a new activator entry in the kernel.
|
||||
You must specify a
|
||||
|
|
@ -124,6 +126,12 @@ To enable the activator entry the
|
|||
option is used.
|
||||
The activator default state is disabled.
|
||||
.Pp
|
||||
To make the interpreter automatically available in jails and chroots,
|
||||
use the
|
||||
.Fl -pre-open
|
||||
option to allow the kernel to open the binary at configuration time
|
||||
rather then lazily when the the interpreted program is started.
|
||||
.Pp
|
||||
The interpreter
|
||||
.Ar path
|
||||
may also contain arguments for the interpreter including
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ static const struct {
|
|||
"<name> --interpreter <path_and_arguments> \\\n"
|
||||
"\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
|
||||
"\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
|
||||
"\t\t[--set-enabled]"
|
||||
"\t\t[--set-enabled] \\\n"
|
||||
"\t\t[--pre-open]"
|
||||
},
|
||||
{
|
||||
CMD_REMOVE,
|
||||
|
|
@ -122,6 +123,7 @@ add_opts[] = {
|
|||
{ "magic", required_argument, NULL, 'm' },
|
||||
{ "offset", required_argument, NULL, 'o' },
|
||||
{ "size", required_argument, NULL, 's' },
|
||||
{ "pre-open", no_argument, NULL, 'p' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
|
@ -192,8 +194,9 @@ printxbe(ximgact_binmisc_entry_t *xbe)
|
|||
|
||||
printf("name: %s\n", xbe->xbe_name);
|
||||
printf("interpreter: %s\n", xbe->xbe_interpreter);
|
||||
printf("flags: %s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
|
||||
(flags & IBF_USE_MASK) ? "USE_MASK " : "");
|
||||
printf("flags: %s%s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
|
||||
(flags & IBF_USE_MASK) ? "USE_MASK " : "",
|
||||
(flags & IBF_PRE_OPEN) ? "PRE_OPEN " : "");
|
||||
printf("magic size: %u\n", xbe->xbe_msize);
|
||||
printf("magic offset: %u\n", xbe->xbe_moffset);
|
||||
|
||||
|
|
@ -291,7 +294,7 @@ add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
|
|||
IBE_NAME_MAX);
|
||||
strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL))
|
||||
while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL))
|
||||
!= -1) {
|
||||
|
||||
switch(ch) {
|
||||
|
|
@ -328,6 +331,10 @@ add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
|
|||
xbe->xbe_msize);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
xbe->xbe_flags |= IBF_PRE_OPEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage("Unknown argument: '%c'", ch);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue