Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile (revision 250181) +++ usr.sbin/Makefile (working copy) @@ -5,6 +5,7 @@ SUBDIR= adduser \ arp \ + binmiscctl \ bootparamd \ bsdinstall \ cdcontrol \ Index: usr.sbin/binmiscctl/binmiscctl.c =================================================================== --- usr.sbin/binmiscctl/binmiscctl.c (revision 0) +++ usr.sbin/binmiscctl/binmiscctl.c (working copy) @@ -0,0 +1,509 @@ +/*- + * Copyright (c) 2013 Stacey D. Son + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +enum cmd { + CMD_ADD = 0, + CMD_REMOVE, + CMD_DISABLE, + CMD_ENABLE, + CMD_LOOKUP, + CMD_LIST, +}; + +extern char *__progname; + +typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); + +int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); +int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); +int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); + +static const struct { + const int token; + const char *name; + cmd_func_t func; + const char *desc; + const char *args; +} cmds[] = { + { + CMD_ADD, + "add", + add_cmd, + "Add a new binary image activator (requires 'root' privilege)", + " --interpreter \\\n" + "\t\t--magic [--mask ] \\\n" + "\t\t--size [--offset ] \\\n" + "\t\t[--set-enabled]" + }, + { + CMD_REMOVE, + "remove", + name_cmd, + "Remove a binary image activator (requires 'root' privilege)", + "" + }, + { + CMD_DISABLE, + "disable", + name_cmd, + "Disable a binary image activator (requires 'root' privilege)", + "" + }, + { + CMD_ENABLE, + "enable", + name_cmd, + "Enable a binary image activator (requires 'root' privilege)", + "" + }, + { + CMD_LOOKUP, + "lookup", + name_cmd, + "Lookup a binary image activator", + "" + }, + { + CMD_LIST, + "list", + noname_cmd, + "List all the binary image activators", + "" + }, +}; + +static const struct option +add_opts[] = { + { "set-enabled", no_argument, NULL, 'e' }, + { "interpreter", required_argument, NULL, 'i' }, + { "mask", required_argument, NULL, 'M' }, + { "magic", required_argument, NULL, 'm' }, + { "offset", required_argument, NULL, 'o' }, + { "size", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } +}; + +static char const *cmd_sysctl_name[] = { + IBE_SYSCTL_NAME_ADD, + IBE_SYSCTL_NAME_REMOVE, + IBE_SYSCTL_NAME_DISABLE, + IBE_SYSCTL_NAME_ENABLE, + IBE_SYSCTL_NAME_LOOKUP, + IBE_SYSCTL_NAME_LIST +}; + +static void +usage(const char *format, ...) +{ + va_list args; + size_t i; + int error = 0; + + va_start(args, format); + if (format) { + vfprintf(stderr, format, args); + error = -1; + } + va_end(args); + fprintf(stderr, "\n"); + fprintf(stderr, "usage: %s command [args...]\n\n", __progname); + + for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) { + fprintf(stderr, "%s:\n", cmds[i].desc); + fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name, + cmds[i].args); + } + + exit (error); +} + +static void +fatal(const char *format, ...) +{ + va_list args; + + va_start(args, format); + if (format) + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + + exit(-1); +} + +static void +getoptstr(char *str, size_t size, const char *argname) +{ + if (strlen(optarg) > size) + usage("'%s' too large", argname); + strlcpy(str, optarg, size); +} + +static void +printxbe(ximgact_binmisc_entry_t *xbe) +{ + uint32_t i, flags = xbe->xbe_flags; + + if (xbe->xbe_version != IBE_VERSION) { + fprintf(stderr, "Error: XBE version mismatch\n"); + return; + } + + 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("magic size: %u\n", xbe->xbe_msize); + printf("magic offset: %u\n", xbe->xbe_moffset); + + printf("magic: "); + for(i = 0; i < xbe->xbe_msize; i++) { + if (i && !(i % 12)) + printf("\n "); + else + if (i && !(i % 4)) + printf(" "); + printf("0x%02x ", xbe->xbe_magic[i]); + } + printf("\n"); + + if (flags & IBF_USE_MASK) { + printf("mask: "); + for(i = 0; i < xbe->xbe_msize; i++) { + if (i && !(i % 12)) + printf("\n "); + else + if (i && !(i % 4)) + printf(" "); + printf("0x%02x ", xbe->xbe_mask[i]); + } + printf("\n"); + } + + printf("\n"); +} + +static int +demux_cmd(__unused int argc, char *const argv[]) +{ + size_t i; + + optind = 1; + optreset = 1; + + for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) { + if (!strcasecmp(cmds[i].name, argv[0])) { + return (i); + } + } + + /* Unknown command */ + return (-1); +} + +static int +strlit2bin_cpy(uint8_t *d, char *s, size_t size) +{ + int c; + size_t cnt = 0; + + while((c = *s++) != '\0') { + if (c == '\\') { + /* Do '\' escapes. */ + switch (*s) { + case '\\': + *d++ = '\\'; + break; + + case 'x': + s++; + c = toupper(*s++); + *d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4; + c = toupper(*s++); + *d++ |= c - (isdigit(c) ? '0' : ('A' - 10)); + break; + + default: + return (-1); + } + } else + *d++ = c; + + if (++cnt > size) + return (-1); + } + + return (cnt); +} + +int +add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe) +{ + int ch; + char *magic = NULL, *mask = NULL; + int sz; + + if (strlen(argv[0]) > IBE_NAME_MAX) + usage("'%s' string length longer than IBE_NAME_MAX (%d)", + 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)) + != -1) { + + switch(ch) { + case 'i': + getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX, + "interpreter"); + break; + + case 'm': + magic = strdup(optarg); + break; + + case 'M': + mask = strdup(optarg); + xbe->xbe_flags |= IBF_USE_MASK; + break; + + case 'e': + xbe->xbe_flags |= IBF_ENABLED; + break; + + case 'o': + xbe->xbe_moffset = atol(optarg); + break; + + case 's': + xbe->xbe_msize = atol(optarg); + if (xbe->xbe_msize == 0 || + xbe->xbe_msize > IBE_MAGIC_MAX) + usage("Error: Not valid '--size' value. " + "(Must be > 0 and < %u.)\n", + xbe->xbe_msize); + break; + + default: + usage("Unknown argument: '%c'", ch); + } + } + + if (xbe->xbe_msize == 0) { + if (NULL != magic) + free(magic); + if (NULL != mask) + free(mask); + usage("Error: Missing '--size' argument"); + } + + if (NULL != magic) { + if (xbe->xbe_msize == 0) { + if (magic) + free(magic); + if (mask) + free(mask); + usage("Error: Missing magic size argument"); + } + sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX); + free(magic); + if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) { + if (mask) + free(mask); + usage("Error: invalid magic argument"); + } + if (mask) { + sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX); + free(mask); + if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) + usage("Error: invalid mask argument"); + } + } else { + if (mask) + free(mask); + usage("Error: Missing magic argument"); + } + + if (!xbe->xbe_interpreter) { + usage("Error: Missing 'interpreter' argument"); + } + + return (0); +} + +int +name_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe) +{ + if (strlen(argv[0]) > IBE_NAME_MAX) + usage("'%s' string length longer than IBE_NAME_MAX (%d)", + IBE_NAME_MAX); + strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); + + return (0); +} + +int +noname_cmd(__unused int argc, __unused char *argv[], + __unused ximgact_binmisc_entry_t *xbe) +{ + + return (0); +} + +int +main(int argc, char **argv) +{ + int error = 0, cmd = -1; + ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL; + ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL; + size_t xbe_in_sz = 0; + size_t xbe_out_sz = 0, *xbe_out_szp = NULL; + uint32_t i; + + if (kldfind(KMOD_NAME) == -1) { + if (kldload(KMOD_NAME) == -1) + fatal("Can't load %s kernel module: %s", + KMOD_NAME, strerror(errno)); + } + + bzero(&xbe_in, sizeof(xbe_in)); + bzero(&xbe_out, sizeof(xbe_out)); + xbe_in.xbe_version = IBE_VERSION; + + if (argc < 2) + usage("Error: requires at least one argument"); + + argc--, argv++; + cmd = demux_cmd(argc, argv); + if (cmd == -1) + usage("Error: Unknown command \"%s\"", argv[0]); + argc--, argv++; + + error = (*cmds[cmd].func)(argc, argv, &xbe_in); + if (error) + usage("Can't parse command-line for '%s' command", + cmds[cmd].name); + + if (cmd != CMD_LIST) { + xbe_inp = &xbe_in; + xbe_in_sz = sizeof(xbe_in); + } else + xbe_out_szp = &xbe_out_sz; + if (cmd == CMD_LOOKUP) { + xbe_out_sz = sizeof(xbe_out); + xbe_outp = &xbe_out; + xbe_out_szp = &xbe_out_sz; + } + + error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp, + xbe_inp, xbe_in_sz); + + if (error) + switch(errno) { + case EINVAL: + usage("Invalid interpreter name or --interpreter, " + "--magic, --mask, or --size argument value"); + break; + + case EEXIST: + usage("'%s' is not unique in activator list", + xbe_in.xbe_name); + break; + + case ENOENT: + usage("'%s' is not found in activator list", + xbe_in.xbe_name); + break; + + case ENOSPC: + fatal("Fatal: no more room in the activator list " + "(limited to %d enties)", IBE_MAX_ENTRIES); + break; + + case EPERM: + usage("Insufficient privileges for '%s' command", + cmds[cmd].name); + break; + + default: + fatal("Fatal: sysctlbyname() returned: %s", + strerror(errno)); + break; + } + + + if (cmd == CMD_LOOKUP) + printxbe(xbe_outp); + + if (cmd == CMD_LIST && xbe_out_sz > 0) { + xbe_outp = malloc(xbe_out_sz); + if (!xbe_outp) + fatal("Fatal: out of memory"); + while(1) { + size_t osize = xbe_out_sz; + error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, + &xbe_out_sz, NULL, 0); + + if (error == -1 && errno == ENOMEM && + xbe_out_sz == osize) { + /* + * Buffer too small. Increase it by one + * entry. + */ + xbe_out_sz += sizeof(xbe_out); + xbe_outp = realloc(xbe_outp, xbe_out_sz); + if (!xbe_outp) + fatal("Fatal: out of memory"); + } else + break; + } + if (error) { + free(xbe_outp); + fatal("Fatal: %s", strerror(errno)); + } + for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++) + printxbe(&xbe_outp[i]); + } + + return (error); +} + Index: usr.sbin/binmiscctl/binmiscctl.8 =================================================================== --- usr.sbin/binmiscctl/binmiscctl.8 (revision 0) +++ usr.sbin/binmiscctl/binmiscctl.8 (working copy) @@ -0,0 +1,186 @@ +.\"- +.\" Copyright (c) 2013 Stacey D. Son +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.\" Support for miscellaneous binary image activators +.\" +.Dd May 14, 2013 +.Dt 8 +.Os +.Sh NAME +.Nm binmiscctl +.Nd manage binary image activators +.Sh SYNOPSIS +.Nm +.Cm add +.Ar name +.Cm --interpreter +.Ar path +.Cm --magic +.Ar magic +.Cm --size +.Ar size +.Op --mask Ar mask +.Op --offset Ar offset +.Op --set-enabled +.Nm +.Cm remove +.Ar name +.Nm +.Cm disable +.Ar name +.Nm +.Cm enable +.Ar name +.Nm +.Cm lookup +.Ar name +.Nm +.Cm list +.Sh DESCRIPTION +The +.Nm +utility +is the management utility for configuring miscellaneous binaries image +activators in the kernel. It allows the adding, deleting, disabling, +enabling, and looking up interpreters. Also, all the interpreters can +be listed as well. +.Pp +The first argument on the command line indicates the operation to be +performed. +Operation must be one of the following: +.Bl -tag -width indent +.It Xo +.Cm add +.Ar name +.Cm --interpreter +.Ar path +.Cm --magic +.Ar magic +.Cm --size +.Ar size +.Op --mask Ar mask +.Op --offset Ar offset +.Op --set-enabled +.Xc +Add a new activator entry in the kernel. You must specify an +unique +.Ar name, +interpreter path and its arguments +.Ar path, +header +.Ar magic +bytes that uniquely identifies a suitable binary for the activator, +and the +.Ar size +of the +.Ar magic +in bytes. +.Pp +Optionally you may specify a +.Ar mask +to do a bitwise AND with the header bytes. + +This effectively allows you to ignore fields in the binary header that +do not uniquely indentfy binary file's type. +.Pp +An +.Ar offset +may be specified for the magic bytes using the +.Ar --offset +argument. By default the +.Ar offset +is zero. +.Pp +To set the activator entry enabled the +.Ar --set-enabled +option is used. The activator default state is disabled. +.Pp +The interpreter +.Ar path +may also arguments for the interpreter including +.Ar #a +which gets replaced by the old argv0 value in the interpreter string. +.It Cm remove Ar name +Remove the activator entry identified with +.Ar name . +.It Cm disable Ar name +Disable the activator entry identified with +.Ar name . +.It Cm enable Ar name +Enable the activator entry identified with +.Ar name . +.It Cm lookup Ar name +Lookup and print out the activator entry identified with +.Ar name . +.It Cm list +Take a snapshot and print all the activator entries currently configured. +.El +.Sh EXAMPLES +.Dl binmiscctl add llvmbc --interpreter ''/usr/bin/lli --fake-argv0=#a'' +--magic ''BC\\xc0\\xde'' +--size 4 --set-enabled +.Pp +Add an image activator to run the the llvm interpreter (lli) on bitcode +compiled files. +.Ar #a +gets replaced with the old argv0 value so that 'lli' can fake its argv0. +Set its state to enabled. +.Pp +.Dl binmiscctl disable llvmbc +.Pp +Set the state of the +.Ar llvmbc +image activator to disabled. +.Pp +.Dl binmiscctl enable llvmbc +.Pp +Set the state of the +.Ar llvmbc +image activator to enabled. +.Pp +.Dl binmiscctl remove llvmbc +.Pp +Delete the +.Ar llvmbc +image activator. +.Pp +.Dl binmiscctl lookup llvmbc +.Pp +Lookup and list the record for the +.Ar llvmbc +image activator. +.Sh SEE ALSO +.Xr execve 2 +.Xr lli 1 +.Sh HISTORY +The +.Cm binmiscctl +command was added in +.Fx 10.0 . +It was developed to support the imgact_binmisc kernel module. +.Sh AUTHORS +Stacey D Son Index: usr.sbin/binmiscctl/Makefile =================================================================== --- usr.sbin/binmiscctl/Makefile (revision 0) +++ usr.sbin/binmiscctl/Makefile (working copy) @@ -0,0 +1,10 @@ +# +# $FreeBSD$ +# + +.include + +PROG= binmiscctl +MAN= binmiscctl.8 + +.include Index: sys/modules/imgact_binmisc/Makefile =================================================================== --- sys/modules/imgact_binmisc/Makefile (revision 0) +++ sys/modules/imgact_binmisc/Makefile (working copy) @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../kern + +KMOD= imgact_binmisc +SRCS= imgact_binmisc.c + +.include Index: sys/modules/Makefile =================================================================== --- sys/modules/Makefile (revision 250181) +++ sys/modules/Makefile (working copy) @@ -145,6 +145,7 @@ if_vlan \ ${_igb} \ ${_iir} \ + imgact_binmisc \ ${_io} \ ${_ipdivert} \ ${_ipfilter} \ Index: sys/sys/imgact_binmisc.h =================================================================== --- sys/sys/imgact_binmisc.h (revision 0) +++ sys/sys/imgact_binmisc.h (working copy) @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 2013 Stacey D. Son + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _IMGACT_BINMISC_H_ +#define _IMGACT_BINMISC_H_ + +/** + * Miscellaneous binary interpreter image activator. + */ + +#include /* for MAXPATHLEN */ + +/* + * Imgact bin misc parameters. + */ +#define IBE_VERSION 1 /* struct ximgact_binmisc_entry version. */ +#define IBE_NAME_MAX 32 /* Max size for entry name. */ +#define IBE_MAGIC_MAX 256 /* Max size for header magic and mask. */ +#define IBE_ARG_LEN_MAX 256 /* Max space for optional interpreter command- + line argruments seperated by white space */ +#define IBE_INTERP_LEN_MAX (MAXPATHLEN + IBE_ARG_LEN_MAX) +#define IBE_MAX_ENTRIES 64 /* Max number of interpreter entries. */ + +/* + * Imgact bin misc interpreter entry flags. + */ +#define IBF_ENABLED 0x0001 /* Entry is active. */ +#define IBF_USE_MASK 0x0002 /* Use mask on header magic field. */ + +/* + * Used with sysctlbyname() to pass imgact bin misc entries in and out of the + * kernel. + */ +typedef struct ximgact_binmisc_entry { + uint32_t xbe_version; /* Struct version(IBE_VERSION) */ + uint32_t xbe_flags; /* Entry flags (IBF_*) */ + uint32_t xbe_moffset; /* Magic offset in header */ + uint32_t xbe_msize; /* Magic size */ + uint32_t spare[3]; /* Spare fields for future use */ + char xbe_name[IBE_NAME_MAX]; /* Unique interpreter name */ + char xbe_interpreter[IBE_INTERP_LEN_MAX]; /* Interpreter path + args */ + uint8_t xbe_magic[IBE_MAGIC_MAX]; /* Header Magic */ + uint8_t xbe_mask[IBE_MAGIC_MAX]; /* Magic Mask */ +} ximgact_binmisc_entry_t; + +/* + * sysctl() command names. + */ +#define IBE_SYSCTL_NAME "kern.binmisc" + +#define IBE_SYSCTL_NAME_ADD IBE_SYSCTL_NAME ".add" +#define IBE_SYSCTL_NAME_REMOVE IBE_SYSCTL_NAME ".remove" +#define IBE_SYSCTL_NAME_DISABLE IBE_SYSCTL_NAME ".disable" +#define IBE_SYSCTL_NAME_ENABLE IBE_SYSCTL_NAME ".enable" +#define IBE_SYSCTL_NAME_LOOKUP IBE_SYSCTL_NAME ".lookup" +#define IBE_SYSCTL_NAME_LIST IBE_SYSCTL_NAME ".list" + +#define KMOD_NAME "imgact_binmisc" + +/* + * Examples of manipulating he interpreter table using sysctlbyname(3): + * + * #include + * + * #define LLVM_MAGIC "BC\xc0\xde" + * + * #define MIPS64_ELF_MAGIC "\x7f\x45\x4c\x46\x02\x02\x01\x00\x00\x00" \ + * "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08" + * #define MIPS64_ELF_MASK "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff" \ + * "\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff" + * + * ximgact_binmisc_entry_t xbe, *xbep, out_xbe; + * size_t size = 0, osize; + * int error, i; + * + * // Add image activator for LLVM byte code + * bzero(&xbe, sizeof(xbe)); + * xbe.xbe_version = IBE_VERSION; + * xbe.xbe_flags = IBF_ENABLED; + * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); + * strlcpy(xbe.xbe_interpreter, "/usr/bin/lli --fake-arg0=#a", + * IBE_INTERP_LEN_MAX); + * xbe.xbe_moffset = 0; + * xbe.xbe_msize = 4; + * memcpy(xbe.xbe_magic, LLVM_MAGIC, xbe.xbe_msize); + * error = sysctlbyname(IBE_SYSCTL_NAME_ADD, NULL, NULL, &xbe, sizeof(xbe)); + * + * // Add image activator for mips64 ELF binaries to use qemu user mode + * bzero(&xbe, sizeof(xbe)); + * xbe.xbe_version = IBE_VERSION; + * xbe.xbe_flags = IBF_ENABLED | IBF_USE_MASK; + * strlcpy(xbe.xbe_name, "mips64elf", IBE_NAME_MAX); + * strlcpy(xbe.xbe_interpreter, "/usr/local/bin/qemu-mips64", + * IBE_INTERP_LEN_MAX); + * xbe.xbe_moffset = 0; + * xbe.xbe_msize = 20; + * memcpy(xbe.xbe_magic, MIPS64_ELF_MAGIC, xbe.xbe_msize); + * memcpy(xbe.xbe_mask, MIPS64_ELF_MASK, xbe.xbe_msize); + * sysctlbyname(IBE_SYSCTL_NAME_ADD, NULL, NULL, &xbe, sizeof(xbe)); + * + * // Disable (OR Enable OR Remove) image activator for LLVM byte code + * bzero(&xbe, sizeof(xbe)); + * xbe.xbe_version = IBE_VERSION; + * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); + * error = sysctlbyname(IBE_SYSCTL_NAME_DISABLE, NULL, NULL, &xbe, sizeof(xbe)); + * // OR sysctlbyname(IBE_SYSCTL_NAME_ENABLE", NULL, NULL, &xbe, sizeof(xbe)); + * // OR sysctlbyname(IBE_SYSCTL_NAME_REMOVE, NULL, NULL, &xbe, sizeof(xbe)); + * + * // Lookup image activator "llvm_bc" + * bzero(&xbe, sizeof(xbe)); + * xbe.xbe_version = IBE_VERSION; + * strlcpy(xbe.xbe_name, "llvm_bc", IBE_NAME_MAX); + * size = sizeof(out_xbe); + * error = sysctlbyname(IBE_SYSCTL_NAME_LOOKUP, &out_xbe, &size, &xbe, + * sizeof(xbe)); + * + * // Get all the currently configured image activators and report + * error = sysctlbyname(IBE_SYSCTL_NAME_LIST, NULL, &size, NULL, 0); + * if (0 == error && size > 0) { + * xbep = malloc(size); + * while(1) { + * osize = size; + * error = sysctlbyname("kern.binmisc.list", xbep, &size, NULL, 0); + * if (-1 == error && ENOMEM == errno && size == osize) { + * // The buffer too small and needs to grow + * size += sizeof(xbe); + * xbep = realloc(xbep, size); + * } else + * break; + * } + * } + * for(i = 0; i < (size / sizeof(xbe)); i++, xbep++) + * printf("name: %s interpreter: %s flags: %s %s\n", xbep->xbe_name, + * xbep->xbe_interpreter, (xbep->xbe_flags & IBF_ENABLED) ? + * "ENABLED" : "", (xbep->xbe_flags & IBF_ENABLED) ? "USE_MASK" : ""); + * + * The sysctlbyname() calls above may return the following errors in addition + * to the standard ones: + * + * [EINVAL] Invalid argument in the input ximgact_binmisc_entry_t structure. + * [EEXIST] Interpreter entry for given name already exist in kernel list. + * [ENOMEM] Allocating memory in the kernel failed or, in the case of + * kern.binmisc.list, the user buffer is too small. + * [ENOENT] Interpreter entry for given name is not found. + * [ENOSPC] Attempted to exceed maximum number of entries (IBE_MAX_ENTRIES). + */ + +#endif /* !_IMGACT_BINMISC_H_ */ Index: sys/kern/imgact_binmisc.c =================================================================== --- sys/kern/imgact_binmisc.c (revision 0) +++ sys/kern/imgact_binmisc.c (working copy) @@ -0,0 +1,766 @@ +/*- + * Copyright (c) 2013, Stacey D. Son + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Miscellaneous binary interpreter image activator. + * + * If the given target executable's header matches 'xbe_magic' field in the + * 'interpreter_list' then it will use the user-level interpreter specified in + * the 'xbe_interpreter' field to execute the binary. The 'xbe_magic' field may + * be adjusted to a given offset using the value in the 'xbe_moffset' field + * and bits of the header may be masked using the 'xbe_mask' field. The + * 'interpreter_list' entries are managed using sysctl(3) as described in the + * file. + */ + +/* + * Node of the interpreter list. + */ +typedef struct imgact_binmisc_entry { + char *ibe_name; + uint8_t *ibe_magic; + uint32_t ibe_moffset; + uint32_t ibe_msize; + uint8_t *ibe_mask; + uint8_t *ibe_interpreter; + uint32_t ibe_interp_argcnt; + uint32_t ibe_interp_length; + uint32_t ibe_flags; + SLIST_ENTRY(imgact_binmisc_entry) link; +} imgact_binmisc_entry_t; + +/* + * sysctl() commands. + */ +#define IBC_ADD 1 /* Add given entry. */ +#define IBC_REMOVE 2 /* Remove entry for a given name. */ +#define IBC_DISABLE 3 /* Disable entry for a given name. */ +#define IBC_ENABLE 4 /* Enable entry for a given name. */ +#define IBC_LOOKUP 5 /* Lookup and return entry for given name. */ +#define IBC_LIST 6 /* Get a snapshot of the interpretor list. */ + +/* + * Interpreter string macros. + * + * They all start with '#' followed by a single letter: + */ +#define ISM_POUND '#' /* "##" is the escape sequence for single #. */ +#define ISM_OLD_ARGV0 'a' /* "#a" is replaced with the old argv0. */ + +MALLOC_DEFINE(M_BINMISC, KMOD_NAME, "misc binary image activator"); + +/* The interpreter list. */ +static SLIST_HEAD(, imgact_binmisc_entry) interpreter_list = + SLIST_HEAD_INITIALIZER(interpreter_list); + +static int interp_list_entry_count = 0; + +static struct mtx interp_list_mtx; + +int imgact_binmisc_exec(struct image_params *imgp); + + +/* + * Populate the entry with the information about the interpreter. + */ +static void +imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe) +{ + uint32_t len = 0, argc = 1; + char t[IBE_INTERP_LEN_MAX]; + char *sp, *tp; + + bzero(t, sizeof(t)); + + /* + * Normalize interpreter string. Replace white space between args with + * single space. + */ + sp = str; tp = t; + while (*sp != '\0') { + if (*sp == ' ' || *sp == '\t') { + if (++len > IBE_INTERP_LEN_MAX) + break; + *tp++ = ' '; + argc++; + while (*sp == ' ' || *sp == '\t') + sp++; + continue; + } else { + *tp++ = *sp++; + len++; + } + } + *tp = '\0'; + len++; + + ibe->ibe_interpreter = malloc(len, M_BINMISC, M_WAITOK|M_ZERO); + + /* Populate all the ibe fields for the interpreter. */ + memcpy(ibe->ibe_interpreter, t, len); + ibe->ibe_interp_argcnt = argc; + ibe->ibe_interp_length = len; +} + +/* + * Allocate memory and populate a new entry for the interpreter table. + */ +static imgact_binmisc_entry_t * +imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe) +{ + imgact_binmisc_entry_t *ibe = NULL; + size_t namesz = min(strlen(xbe->xbe_name) + 1, IBE_NAME_MAX); + + mtx_assert(&interp_list_mtx, MA_NOTOWNED); + + ibe = malloc(sizeof(*ibe), M_BINMISC, M_WAITOK|M_ZERO); + + 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); + + ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO); + memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize); + + ibe->ibe_mask = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO); + memcpy(ibe->ibe_mask, xbe->xbe_mask, xbe->xbe_msize); + + ibe->ibe_moffset = xbe->xbe_moffset; + ibe->ibe_msize = xbe->xbe_msize; + ibe->ibe_flags = xbe->xbe_flags; + + return (ibe); +} + +/* + * Free the allocated memory for a given list item. + */ +static void +imgact_binmisc_destroy_entry(imgact_binmisc_entry_t *ibe) +{ + if (!ibe) + return; + if (ibe->ibe_mask) + free(ibe->ibe_magic, M_BINMISC); + if (ibe->ibe_mask) + free(ibe->ibe_mask, M_BINMISC); + if (ibe->ibe_interpreter) + free(ibe->ibe_interpreter, M_BINMISC); + if (ibe->ibe_name) + free(ibe->ibe_name, M_BINMISC); + if (ibe) + free(ibe, M_BINMISC); +} + +/* + * Find the interpreter in the list by the given name. Return NULL if not + * found. + */ +static imgact_binmisc_entry_t * +imgact_binmisc_find_entry(char *name) +{ + imgact_binmisc_entry_t *ibe; + + mtx_assert(&interp_list_mtx, MA_OWNED); + + SLIST_FOREACH(ibe, &interpreter_list, link) { + if (strncmp(name, ibe->ibe_name, IBE_NAME_MAX) == 0) + return (ibe); + } + + return (NULL); +} + +/* + * Add the given interpreter if it doesn't already exist. Return EEXIST + * if the name already exist in the interpreter list. + */ +static int +imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe) +{ + imgact_binmisc_entry_t *ibe; + char *p; + + if (xbe->xbe_msize > IBE_MAGIC_MAX) + return (EINVAL); + + for(p = xbe->xbe_name; *p != 0; p++) + if (!isascii((int)*p)) + return (EINVAL); + + for(p = xbe->xbe_interpreter; *p != 0; p++) + if (!isascii((int)*p)) + return (EINVAL); + + /* Make sure we don't have any invalid #'s. */ + p = xbe->xbe_interpreter; + while (1) { + p = strchr(p, '#'); + if (!p) + break; + + p++; + switch(*p) { + case ISM_POUND: + /* "##" */ + p++; + break; + + case ISM_OLD_ARGV0: + /* "#a" */ + p++; + break; + + case 0: + default: + /* Anything besides the above is invalid. */ + return (EINVAL); + } + } + + mtx_lock(&interp_list_mtx); + if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) { + mtx_unlock(&interp_list_mtx); + return (EEXIST); + } + mtx_unlock(&interp_list_mtx); + + ibe = imgact_binmisc_new_entry(xbe); + if (!ibe) + return (ENOMEM); + + mtx_lock(&interp_list_mtx); + SLIST_INSERT_HEAD(&interpreter_list, ibe, link); + interp_list_entry_count++; + mtx_unlock(&interp_list_mtx); + + return (0); +} + +/* + * Remove the interpreter in the list with the given name. Return ENOENT + * if not found. + */ +static int +imgact_binmisc_remove_entry(char *name) +{ + imgact_binmisc_entry_t *ibe; + + mtx_lock(&interp_list_mtx); + if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { + mtx_unlock(&interp_list_mtx); + return (ENOENT); + } + SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link); + interp_list_entry_count--; + mtx_unlock(&interp_list_mtx); + + imgact_binmisc_destroy_entry(ibe); + + return (0); +} + +/* + * Disable the interpreter in the list with the given name. Return ENOENT + * if not found. + */ +static int +imgact_binmisc_disable_entry(char *name) +{ + imgact_binmisc_entry_t *ibe; + + mtx_lock(&interp_list_mtx); + if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { + mtx_unlock(&interp_list_mtx); + return (ENOENT); + } + + ibe->ibe_flags &= ~IBF_ENABLED; + mtx_unlock(&interp_list_mtx); + + return (0); +} + +/* + * Enable the interpreter in the list with the given name. Return ENOENT + * if not found. + */ +static int +imgact_binmisc_enable_entry(char *name) +{ + imgact_binmisc_entry_t *ibe; + + mtx_lock(&interp_list_mtx); + if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { + mtx_unlock(&interp_list_mtx); + return (ENOENT); + } + + ibe->ibe_flags |= IBF_ENABLED; + mtx_unlock(&interp_list_mtx); + + return (0); +} + +static int +imgact_binmisc_populate_xbe(ximgact_binmisc_entry_t *xbe, + imgact_binmisc_entry_t *ibe) +{ + uint32_t i; + + mtx_assert(&interp_list_mtx, MA_OWNED); + + bzero(xbe, sizeof(*xbe)); + strlcpy(xbe->xbe_name, ibe->ibe_name, IBE_NAME_MAX); + + /* Copy interpreter string. Replace NULL breaks with space. */ + memcpy(xbe->xbe_interpreter, ibe->ibe_interpreter, + ibe->ibe_interp_length); + for(i = 0; i < (ibe->ibe_interp_length - 1); i++) + if (xbe->xbe_interpreter[i] == '\0') + xbe->xbe_interpreter[i] = ' '; + + memcpy(xbe->xbe_magic, ibe->ibe_magic, ibe->ibe_msize); + memcpy(xbe->xbe_mask, ibe->ibe_mask, ibe->ibe_msize); + xbe->xbe_version = IBE_VERSION; + xbe->xbe_flags = ibe->ibe_flags; + xbe->xbe_moffset = ibe->ibe_moffset; + xbe->xbe_msize = ibe->ibe_msize; + + return (0); +} + +/* + * Retrieve the interpreter with the give name and populate the + * ximgact_binmisc_entry structure. Return ENOENT if not found. + */ +static int +imgact_binmisc_lookup_entry(char *name, ximgact_binmisc_entry_t *xbe) +{ + imgact_binmisc_entry_t *ibe; + int error = 0; + + mtx_lock(&interp_list_mtx); + if ((ibe = imgact_binmisc_find_entry(name)) == NULL) { + mtx_unlock(&interp_list_mtx); + return (ENOENT); + } + + error = imgact_binmisc_populate_xbe(xbe, ibe); + mtx_unlock(&interp_list_mtx); + + return (error); +} + +/* + * Get a snapshot of all the interpreter entries in the list. + */ +static int +imgact_binmisc_get_all_entries(struct sysctl_req *req) +{ + ximgact_binmisc_entry_t *xbe, *xbep; + imgact_binmisc_entry_t *ibe; + int error = 0, count; + + mtx_lock(&interp_list_mtx); + count = interp_list_entry_count; + /* Don't block in malloc() while holding lock. */ + xbe = malloc(sizeof(*xbe) * count, M_BINMISC, M_NOWAIT|M_ZERO); + if (!xbe) { + mtx_unlock(&interp_list_mtx); + return (ENOMEM); + } + + xbep = xbe; + SLIST_FOREACH(ibe, &interpreter_list, link) { + error = imgact_binmisc_populate_xbe(xbep++, ibe); + if (error) + break; + } + mtx_unlock(&interp_list_mtx); + + if (!error) + error = SYSCTL_OUT(req, xbe, sizeof(*xbe) * count); + + free(xbe, M_BINMISC); + return (error); +} + +/* + * sysctl() handler for munipulating interpretor table. + * Not MP safe (locked by sysctl). + */ +static int +sysctl_kern_binmisc(SYSCTL_HANDLER_ARGS) +{ + ximgact_binmisc_entry_t xbe; + int error = 0; + + switch(arg2) { + case IBC_ADD: + /* Add an entry. Limited to IBE_MAX_ENTRIES. */ + error = SYSCTL_IN(req, &xbe, sizeof(xbe)); + if (error) + return (error); + if (IBE_VERSION != xbe.xbe_version) + return (EINVAL); + if (interp_list_entry_count == IBE_MAX_ENTRIES) + return (ENOSPC); + error = imgact_binmisc_add_entry(&xbe); + break; + + case IBC_REMOVE: + /* Remove an entry. */ + error = SYSCTL_IN(req, &xbe, sizeof(xbe)); + if (error) + return (error); + if (IBE_VERSION != xbe.xbe_version) + return (EINVAL); + error = imgact_binmisc_remove_entry(xbe.xbe_name); + break; + + case IBC_DISABLE: + /* Disable an entry. */ + error = SYSCTL_IN(req, &xbe, sizeof(xbe)); + if (error) + return (error); + if (IBE_VERSION != xbe.xbe_version) + return (EINVAL); + error = imgact_binmisc_disable_entry(xbe.xbe_name); + break; + + case IBC_ENABLE: + /* Enable an entry. */ + error = SYSCTL_IN(req, &xbe, sizeof(xbe)); + if (error) + return (error); + if (IBE_VERSION != xbe.xbe_version) + return (EINVAL); + error = imgact_binmisc_enable_entry(xbe.xbe_name); + break; + + case IBC_LOOKUP: + /* Lookup an entry. */ + error = SYSCTL_IN(req, &xbe, sizeof(xbe)); + if (error) + return (error); + if (IBE_VERSION != xbe.xbe_version) + return (EINVAL); + error = imgact_binmisc_lookup_entry(xbe.xbe_name, &xbe); + if (!error) + error = SYSCTL_OUT(req, &xbe, sizeof(xbe)); + break; + + case IBC_LIST: + /* Return a snapshot of the interpretor list. */ + + if (!req->oldptr) { + /* No pointer then just return the list size. */ + error = SYSCTL_OUT(req, 0, interp_list_entry_count * + sizeof(ximgact_binmisc_entry_t)); + return (error); + } else + if (!req->oldlen) + return (EINVAL); + + error = imgact_binmisc_get_all_entries(req); + break; + + default: + return (EINVAL); + } + + return (error); +} + +SYSCTL_NODE(_kern, OID_AUTO, binmisc, CTLFLAG_RW, 0, + "Image activator for miscellaneous binaries"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, add, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ADD, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Add an activator entry"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, remove, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_REMOVE, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Remove an activator entry"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, disable, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_DISABLE, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Disable an activator entry"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, enable, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ENABLE, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Enable an activator entry"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, lookup, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_ANYBODY, NULL, IBC_LOOKUP, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Lookup an activator entry"); + +SYSCTL_PROC(_kern_binmisc, OID_AUTO, list, + CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RD|CTLFLAG_ANYBODY, NULL, IBC_LIST, + sysctl_kern_binmisc, "S,ximgact_binmisc_entry", + "Get snapshot of all the activator entries"); + +static imgact_binmisc_entry_t * +imgact_binmisc_find_interpreter(const char *image_header) +{ + imgact_binmisc_entry_t *ibe; + const char *p; + int i; + size_t sz; + + mtx_assert(&interp_list_mtx, MA_OWNED); + + SLIST_FOREACH(ibe, &interpreter_list, link) { + if (!(IBF_ENABLED & ibe->ibe_flags)) + continue; + + p = image_header + ibe->ibe_moffset; + sz = ibe->ibe_msize; + if (IBF_USE_MASK & ibe->ibe_flags) { + /* Compare using mask. */ + for (i = 0; i < sz; i++) + if ((*p++ ^ ibe->ibe_magic[i]) & + ibe->ibe_mask[i]) + break; + } else { + for (i = 0; i < sz; i++) + if (*p++ ^ ibe->ibe_magic[i]) + break; + } + if (i == ibe->ibe_msize) + return (ibe); + } + return (NULL); +} + +int +imgact_binmisc_exec(struct image_params *imgp) +{ + const char *image_header = imgp->image_header; + const char *fname = NULL; + int error = 0; + size_t offset, l; + imgact_binmisc_entry_t *ibe; + struct sbuf *sname; + char *s, *d; + + /* Do we have an interpreter for the given image header? */ + mtx_lock(&interp_list_mtx); + if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) { + mtx_unlock(&interp_list_mtx); + return (-1); + } + + /* No interpreter nesting allowed. */ + if (imgp->interpreted) { + mtx_unlock(&interp_list_mtx); + return (ENOEXEC); + } + + imgp->interpreted = 1; + + if (imgp->args->fname != NULL) { + fname = imgp->args->fname; + sname = NULL; + } else { + /* Use the fdescfs(5) path for fexecve(2). */ + sname = sbuf_new_auto(); + sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd); + sbuf_finish(sname); + fname = sbuf_data(sname); + } + + + /* + * We need to "push" the interpreter in the arg[] list. To do this, + * we first shift all the other values in the `begin_argv' area to + * provide the exact amount of room for the values added. Set up + * `offset' as the number of bytes to be added to the `begin_argv' + * area. + */ + offset = ibe->ibe_interp_length; + + /* Adjust the offset for #'s. */ + s = ibe->ibe_interpreter; + while (1) { + s = strchr(s, '#'); + if (!s) + break; + + s++; + switch(*s) { + case ISM_POUND: + /* "##" -> "#": reduce offset by one. */ + offset--; + break; + + case ISM_OLD_ARGV0: + /* "#a" -> (old argv0): increase offset to fit fname */ + offset += strlen(fname) - 2; + break; + + default: + /* Hmm... This shouldn't happen. */ + mtx_unlock(&interp_list_mtx); + printf("%s: Unknown macro #%c sequence in " + "interpreter string\n", KMOD_NAME, *(s + 1)); + error = EINVAL; + goto done; + } + s++; + } + + /* Check to make sure we won't overrun the stringspace. */ + if (offset > imgp->args->stringspace) { + mtx_unlock(&interp_list_mtx); + error = E2BIG; + goto done; + } + + /* Make room for the interpreter */ + bcopy(imgp->args->begin_argv, imgp->args->begin_argv + offset, + imgp->args->endp - imgp->args->begin_argv); + + /* Adjust everything by the offset. */ + imgp->args->begin_envv += offset; + imgp->args->endp += offset; + imgp->args->stringspace -= offset; + + /* Add the new argument(s) in the count. */ + imgp->args->argc += ibe->ibe_interp_argcnt; + + /* + * The original arg[] list has been shifted appropriately. Copy in + * the interpreter path. + */ + s = ibe->ibe_interpreter; + d = imgp->args->begin_argv; + while(*s != '\0') { + switch (*s) { + case '#': + /* Handle "#" in interpreter string. */ + s++; + switch(*s) { + case ISM_POUND: + /* "##": Replace with a single '#' */ + *d++ = '#'; + break; + + case ISM_OLD_ARGV0: + /* "#a": Replace with old arg0 (fname). */ + if ((l = strlen(fname)) != 0) { + memcpy(d, fname, l); + d += l; + } + break; + + default: + /* Shouldn't happen but skip it if it does. */ + break; + } + break; + + case ' ': + /* Replace space with NUL to seperate arguments. */ + *d++ = '\0'; + break; + + default: + *d++ = *s; + break; + } + s++; + } + *d = '\0'; + mtx_unlock(&interp_list_mtx); + + if (!error) + imgp->interpreter_name = imgp->args->begin_argv; + + +done: + if (sname) + sbuf_delete(sname); + return (error); +} + +static void +imgact_binmisc_init(void *arg) +{ + + mtx_init(&interp_list_mtx, KMOD_NAME, NULL, MTX_DEF); +} + +static void +imgact_binmisc_fini(void *arg) +{ + imgact_binmisc_entry_t *ibe, *ibe_tmp; + + /* Free all the interpreters. */ + mtx_lock(&interp_list_mtx); + SLIST_FOREACH_SAFE(ibe, &interpreter_list, link, ibe_tmp) { + SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, + link); + imgact_binmisc_destroy_entry(ibe); + } + mtx_unlock(&interp_list_mtx); + + mtx_destroy(&interp_list_mtx); +} + +SYSINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_init, 0); +SYSUNINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_fini, 0); + +/* + * Tell kern_execve.c about it, with a little help from the linker. + */ +static struct execsw imgact_binmisc_execsw = { imgact_binmisc_exec, KMOD_NAME }; +EXEC_SET(imgact_binmisc, imgact_binmisc_execsw);