1264269Ssbruno/*-
2264269Ssbruno * Copyright (c) 2013, Stacey D. Son
3264269Ssbruno * All rights reserved.
4264269Ssbruno *
5264269Ssbruno * Redistribution and use in source and binary forms, with or without
6264269Ssbruno * modification, are permitted provided that the following conditions
7264269Ssbruno * are met:
8264269Ssbruno * 1. Redistributions of source code must retain the above copyright
9264269Ssbruno *    notice, this list of conditions and the following disclaimer.
10264269Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
11264269Ssbruno *    notice, this list of conditions and the following disclaimer in the
12264269Ssbruno *    documentation and/or other materials provided with the distribution.
13264269Ssbruno *
14264269Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15264269Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16264269Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17264269Ssbruno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18264269Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19264269Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20264269Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21264269Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22264269Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23264269Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24264269Ssbruno * SUCH DAMAGE.
25264269Ssbruno */
26264269Ssbruno
27264269Ssbruno#include <sys/cdefs.h>
28264269Ssbruno__FBSDID("$FreeBSD$");
29264269Ssbruno
30264269Ssbruno#include <sys/param.h>
31264269Ssbruno#include <sys/ctype.h>
32264269Ssbruno#include <sys/sbuf.h>
33264269Ssbruno#include <sys/systm.h>
34264269Ssbruno#include <sys/sysproto.h>
35264269Ssbruno#include <sys/exec.h>
36264269Ssbruno#include <sys/imgact.h>
37264269Ssbruno#include <sys/imgact_binmisc.h>
38264269Ssbruno#include <sys/kernel.h>
39264269Ssbruno#include <sys/libkern.h>
40264269Ssbruno#include <sys/lock.h>
41264269Ssbruno#include <sys/malloc.h>
42264269Ssbruno#include <sys/mutex.h>
43264269Ssbruno#include <sys/sysctl.h>
44264269Ssbruno
45264269Ssbruno/**
46264269Ssbruno * Miscellaneous binary interpreter image activator.
47264269Ssbruno *
48264269Ssbruno * If the given target executable's header matches 'xbe_magic' field in the
49264269Ssbruno * 'interpreter_list' then it will use the user-level interpreter specified in
50264269Ssbruno * the 'xbe_interpreter' field to execute the binary. The 'xbe_magic' field may
51264269Ssbruno * be adjusted to a given offset using the value in the 'xbe_moffset' field
52264269Ssbruno * and bits of the header may be masked using the 'xbe_mask' field.  The
53264269Ssbruno * 'interpreter_list' entries are managed using sysctl(3) as described in the
54264269Ssbruno * <sys/imgact_binmisc.h> file.
55264269Ssbruno */
56264269Ssbruno
57264269Ssbruno/*
58264269Ssbruno * Node of the interpreter list.
59264269Ssbruno */
60264269Ssbrunotypedef struct imgact_binmisc_entry {
61264269Ssbruno	char				 *ibe_name;
62264269Ssbruno	uint8_t				 *ibe_magic;
63264269Ssbruno	uint32_t			  ibe_moffset;
64264269Ssbruno	uint32_t			  ibe_msize;
65264269Ssbruno	uint8_t				 *ibe_mask;
66264269Ssbruno	uint8_t				 *ibe_interpreter;
67264269Ssbruno	uint32_t			  ibe_interp_argcnt;
68264269Ssbruno	uint32_t			  ibe_interp_length;
69264269Ssbruno	uint32_t			  ibe_flags;
70264269Ssbruno	SLIST_ENTRY(imgact_binmisc_entry) link;
71264269Ssbruno} imgact_binmisc_entry_t;
72264269Ssbruno
73264269Ssbruno/*
74264269Ssbruno * sysctl() commands.
75264269Ssbruno */
76264269Ssbruno#define IBC_ADD		1	/* Add given entry. */
77264269Ssbruno#define IBC_REMOVE	2	/* Remove entry for a given name. */
78264269Ssbruno#define IBC_DISABLE	3	/* Disable entry for a given name. */
79264269Ssbruno#define IBC_ENABLE	4	/* Enable entry for a given name. */
80264269Ssbruno#define IBC_LOOKUP	5	/* Lookup and return entry for given name. */
81264269Ssbruno#define IBC_LIST	6	/* Get a snapshot of the interpretor list. */
82264269Ssbruno
83264269Ssbruno/*
84264269Ssbruno * Interpreter string macros.
85264269Ssbruno *
86264269Ssbruno * They all start with '#' followed by a single letter:
87264269Ssbruno */
88264269Ssbruno#define	ISM_POUND	'#'	/* "##" is the escape sequence for single #. */
89264269Ssbruno#define	ISM_OLD_ARGV0	'a'	/* "#a" is replaced with the old argv0. */
90264269Ssbruno
91264269SsbrunoMALLOC_DEFINE(M_BINMISC, KMOD_NAME, "misc binary image activator");
92264269Ssbruno
93264269Ssbruno/* The interpreter list. */
94264269Ssbrunostatic SLIST_HEAD(, imgact_binmisc_entry) interpreter_list =
95264269Ssbruno	SLIST_HEAD_INITIALIZER(interpreter_list);
96264269Ssbruno
97264269Ssbrunostatic int interp_list_entry_count = 0;
98264269Ssbruno
99264269Ssbrunostatic struct mtx interp_list_mtx;
100264269Ssbruno
101264269Ssbrunoint imgact_binmisc_exec(struct image_params *imgp);
102264269Ssbruno
103264269Ssbruno
104264269Ssbruno/*
105264269Ssbruno * Populate the entry with the information about the interpreter.
106264269Ssbruno */
107264269Ssbrunostatic void
108264269Ssbrunoimgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe)
109264269Ssbruno{
110264269Ssbruno	uint32_t len = 0, argc = 1;
111264269Ssbruno	char t[IBE_INTERP_LEN_MAX];
112264269Ssbruno	char *sp, *tp;
113264269Ssbruno
114264269Ssbruno	bzero(t, sizeof(t));
115264269Ssbruno
116264269Ssbruno	/*
117264269Ssbruno	 * Normalize interpreter string. Replace white space between args with
118264269Ssbruno	 * single space.
119264269Ssbruno	 */
120264269Ssbruno	sp = str; tp = t;
121264269Ssbruno	while (*sp != '\0') {
122264269Ssbruno		if (*sp == ' ' || *sp == '\t') {
123264269Ssbruno			if (++len > IBE_INTERP_LEN_MAX)
124264269Ssbruno				break;
125264269Ssbruno			*tp++ = ' ';
126264269Ssbruno			argc++;
127264269Ssbruno			while (*sp == ' ' || *sp == '\t')
128264269Ssbruno				sp++;
129264269Ssbruno			continue;
130264269Ssbruno		} else {
131264269Ssbruno			*tp++ = *sp++;
132264269Ssbruno			len++;
133264269Ssbruno		}
134264269Ssbruno	}
135264269Ssbruno	*tp = '\0';
136264269Ssbruno	len++;
137264269Ssbruno
138264269Ssbruno	ibe->ibe_interpreter = malloc(len, M_BINMISC, M_WAITOK|M_ZERO);
139264269Ssbruno
140264269Ssbruno	/* Populate all the ibe fields for the interpreter. */
141264269Ssbruno	memcpy(ibe->ibe_interpreter, t, len);
142264269Ssbruno	ibe->ibe_interp_argcnt = argc;
143264269Ssbruno	ibe->ibe_interp_length = len;
144264269Ssbruno}
145264269Ssbruno
146264269Ssbruno/*
147264269Ssbruno * Allocate memory and populate a new entry for the interpreter table.
148264269Ssbruno */
149264269Ssbrunostatic imgact_binmisc_entry_t *
150264269Ssbrunoimgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe)
151264269Ssbruno{
152264269Ssbruno	imgact_binmisc_entry_t *ibe = NULL;
153264269Ssbruno	size_t namesz = min(strlen(xbe->xbe_name) + 1, IBE_NAME_MAX);
154264269Ssbruno
155264269Ssbruno	mtx_assert(&interp_list_mtx, MA_NOTOWNED);
156264269Ssbruno
157264269Ssbruno	ibe = malloc(sizeof(*ibe), M_BINMISC, M_WAITOK|M_ZERO);
158264269Ssbruno
159264269Ssbruno	ibe->ibe_name = malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO);
160264269Ssbruno	strlcpy(ibe->ibe_name, xbe->xbe_name, namesz);
161264269Ssbruno
162264269Ssbruno	imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe);
163264269Ssbruno
164264269Ssbruno	ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO);
165264269Ssbruno	memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize);
166264269Ssbruno
167264269Ssbruno	ibe->ibe_mask = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO);
168264269Ssbruno	memcpy(ibe->ibe_mask, xbe->xbe_mask, xbe->xbe_msize);
169264269Ssbruno
170264269Ssbruno	ibe->ibe_moffset = xbe->xbe_moffset;
171264269Ssbruno	ibe->ibe_msize = xbe->xbe_msize;
172264269Ssbruno	ibe->ibe_flags = xbe->xbe_flags;
173264269Ssbruno
174264269Ssbruno	return (ibe);
175264269Ssbruno}
176264269Ssbruno
177264269Ssbruno/*
178264269Ssbruno * Free the allocated memory for a given list item.
179264269Ssbruno */
180264269Ssbrunostatic void
181264269Ssbrunoimgact_binmisc_destroy_entry(imgact_binmisc_entry_t *ibe)
182264269Ssbruno{
183264269Ssbruno	if (!ibe)
184264269Ssbruno		return;
185266272Ssbruno	if (ibe->ibe_magic)
186264269Ssbruno		free(ibe->ibe_magic, M_BINMISC);
187264269Ssbruno	if (ibe->ibe_mask)
188264269Ssbruno		free(ibe->ibe_mask, M_BINMISC);
189264269Ssbruno	if (ibe->ibe_interpreter)
190264269Ssbruno		free(ibe->ibe_interpreter, M_BINMISC);
191264269Ssbruno	if (ibe->ibe_name)
192264269Ssbruno		free(ibe->ibe_name, M_BINMISC);
193264269Ssbruno	if (ibe)
194264269Ssbruno		free(ibe, M_BINMISC);
195264269Ssbruno}
196264269Ssbruno
197264269Ssbruno/*
198264269Ssbruno * Find the interpreter in the list by the given name.  Return NULL if not
199264269Ssbruno * found.
200264269Ssbruno */
201264269Ssbrunostatic imgact_binmisc_entry_t *
202264269Ssbrunoimgact_binmisc_find_entry(char *name)
203264269Ssbruno{
204264269Ssbruno	imgact_binmisc_entry_t *ibe;
205264269Ssbruno
206264269Ssbruno	mtx_assert(&interp_list_mtx, MA_OWNED);
207264269Ssbruno
208264269Ssbruno	SLIST_FOREACH(ibe, &interpreter_list, link) {
209264269Ssbruno		if (strncmp(name, ibe->ibe_name, IBE_NAME_MAX) == 0)
210264269Ssbruno			return (ibe);
211264269Ssbruno	}
212264269Ssbruno
213264269Ssbruno	return (NULL);
214264269Ssbruno}
215264269Ssbruno
216264269Ssbruno/*
217264269Ssbruno * Add the given interpreter if it doesn't already exist.  Return EEXIST
218264269Ssbruno * if the name already exist in the interpreter list.
219264269Ssbruno */
220264269Ssbrunostatic int
221264269Ssbrunoimgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe)
222264269Ssbruno{
223264269Ssbruno	imgact_binmisc_entry_t *ibe;
224264269Ssbruno	char *p;
225264269Ssbruno
226264269Ssbruno	if (xbe->xbe_msize > IBE_MAGIC_MAX)
227264269Ssbruno		return (EINVAL);
228264269Ssbruno
229264269Ssbruno	for(p = xbe->xbe_name; *p != 0; p++)
230264269Ssbruno		if (!isascii((int)*p))
231264269Ssbruno			return (EINVAL);
232264269Ssbruno
233264269Ssbruno	for(p = xbe->xbe_interpreter; *p != 0; p++)
234264269Ssbruno		if (!isascii((int)*p))
235264269Ssbruno			return (EINVAL);
236264269Ssbruno
237264269Ssbruno	/* Make sure we don't have any invalid #'s. */
238264269Ssbruno	p = xbe->xbe_interpreter;
239264269Ssbruno	while (1) {
240264269Ssbruno		p = strchr(p, '#');
241264269Ssbruno		if (!p)
242264269Ssbruno			break;
243264269Ssbruno
244264269Ssbruno		p++;
245264269Ssbruno		switch(*p) {
246264269Ssbruno		case ISM_POUND:
247264269Ssbruno			/* "##" */
248264269Ssbruno			p++;
249264269Ssbruno			break;
250264269Ssbruno
251264269Ssbruno		case ISM_OLD_ARGV0:
252264269Ssbruno			/* "#a" */
253264269Ssbruno			p++;
254264269Ssbruno			break;
255264269Ssbruno
256264269Ssbruno		case 0:
257264269Ssbruno		default:
258264269Ssbruno			/* Anything besides the above is invalid. */
259264269Ssbruno			return (EINVAL);
260264269Ssbruno		}
261264269Ssbruno	}
262264269Ssbruno
263264269Ssbruno	mtx_lock(&interp_list_mtx);
264264269Ssbruno	if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) {
265264269Ssbruno		mtx_unlock(&interp_list_mtx);
266264269Ssbruno		return (EEXIST);
267264269Ssbruno	}
268264269Ssbruno	mtx_unlock(&interp_list_mtx);
269264269Ssbruno
270264269Ssbruno	ibe = imgact_binmisc_new_entry(xbe);
271264269Ssbruno	if (!ibe)
272264269Ssbruno		return (ENOMEM);
273264269Ssbruno
274264269Ssbruno	mtx_lock(&interp_list_mtx);
275264269Ssbruno	SLIST_INSERT_HEAD(&interpreter_list, ibe, link);
276264269Ssbruno	interp_list_entry_count++;
277264269Ssbruno	mtx_unlock(&interp_list_mtx);
278264269Ssbruno
279264269Ssbruno	return (0);
280264269Ssbruno}
281264269Ssbruno
282264269Ssbruno/*
283264269Ssbruno * Remove the interpreter in the list with the given name. Return ENOENT
284264269Ssbruno * if not found.
285264269Ssbruno */
286264269Ssbrunostatic int
287264269Ssbrunoimgact_binmisc_remove_entry(char *name)
288264269Ssbruno{
289264269Ssbruno	imgact_binmisc_entry_t *ibe;
290264269Ssbruno
291264269Ssbruno	mtx_lock(&interp_list_mtx);
292264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
293264269Ssbruno		mtx_unlock(&interp_list_mtx);
294264269Ssbruno		return (ENOENT);
295264269Ssbruno	}
296264269Ssbruno	SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link);
297264269Ssbruno	interp_list_entry_count--;
298264269Ssbruno	mtx_unlock(&interp_list_mtx);
299264269Ssbruno
300264269Ssbruno	imgact_binmisc_destroy_entry(ibe);
301264269Ssbruno
302264269Ssbruno	return (0);
303264269Ssbruno}
304264269Ssbruno
305264269Ssbruno/*
306264269Ssbruno * Disable the interpreter in the list with the given name. Return ENOENT
307264269Ssbruno * if not found.
308264269Ssbruno */
309264269Ssbrunostatic int
310264269Ssbrunoimgact_binmisc_disable_entry(char *name)
311264269Ssbruno{
312264269Ssbruno	imgact_binmisc_entry_t *ibe;
313264269Ssbruno
314264269Ssbruno	mtx_lock(&interp_list_mtx);
315264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
316264269Ssbruno		mtx_unlock(&interp_list_mtx);
317264269Ssbruno		return (ENOENT);
318264269Ssbruno	}
319264269Ssbruno
320264269Ssbruno	ibe->ibe_flags &= ~IBF_ENABLED;
321264269Ssbruno	mtx_unlock(&interp_list_mtx);
322264269Ssbruno
323264269Ssbruno	return (0);
324264269Ssbruno}
325264269Ssbruno
326264269Ssbruno/*
327264269Ssbruno * Enable the interpreter in the list with the given name. Return ENOENT
328264269Ssbruno * if not found.
329264269Ssbruno */
330264269Ssbrunostatic int
331264269Ssbrunoimgact_binmisc_enable_entry(char *name)
332264269Ssbruno{
333264269Ssbruno	imgact_binmisc_entry_t *ibe;
334264269Ssbruno
335264269Ssbruno	mtx_lock(&interp_list_mtx);
336264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
337264269Ssbruno		mtx_unlock(&interp_list_mtx);
338264269Ssbruno		return (ENOENT);
339264269Ssbruno	}
340264269Ssbruno
341264269Ssbruno	ibe->ibe_flags |= IBF_ENABLED;
342264269Ssbruno	mtx_unlock(&interp_list_mtx);
343264269Ssbruno
344264269Ssbruno	return (0);
345264269Ssbruno}
346264269Ssbruno
347264269Ssbrunostatic int
348264269Ssbrunoimgact_binmisc_populate_xbe(ximgact_binmisc_entry_t *xbe,
349264269Ssbruno    imgact_binmisc_entry_t *ibe)
350264269Ssbruno{
351264269Ssbruno	uint32_t i;
352264269Ssbruno
353264269Ssbruno	mtx_assert(&interp_list_mtx, MA_OWNED);
354264269Ssbruno
355264269Ssbruno	bzero(xbe, sizeof(*xbe));
356264269Ssbruno	strlcpy(xbe->xbe_name, ibe->ibe_name, IBE_NAME_MAX);
357264269Ssbruno
358264269Ssbruno	/* Copy interpreter string.  Replace NULL breaks with space. */
359264269Ssbruno	memcpy(xbe->xbe_interpreter, ibe->ibe_interpreter,
360264269Ssbruno	    ibe->ibe_interp_length);
361264269Ssbruno	for(i = 0; i < (ibe->ibe_interp_length - 1); i++)
362264269Ssbruno		if (xbe->xbe_interpreter[i] == '\0')
363264269Ssbruno			xbe->xbe_interpreter[i] = ' ';
364264269Ssbruno
365264269Ssbruno	memcpy(xbe->xbe_magic, ibe->ibe_magic, ibe->ibe_msize);
366264269Ssbruno	memcpy(xbe->xbe_mask, ibe->ibe_mask, ibe->ibe_msize);
367264269Ssbruno	xbe->xbe_version = IBE_VERSION;
368264269Ssbruno	xbe->xbe_flags = ibe->ibe_flags;
369264269Ssbruno	xbe->xbe_moffset = ibe->ibe_moffset;
370264269Ssbruno	xbe->xbe_msize = ibe->ibe_msize;
371264269Ssbruno
372264269Ssbruno	return (0);
373264269Ssbruno}
374264269Ssbruno
375264269Ssbruno/*
376264269Ssbruno * Retrieve the interpreter with the give name and populate the
377264269Ssbruno * ximgact_binmisc_entry structure.  Return ENOENT if not found.
378264269Ssbruno */
379264269Ssbrunostatic int
380264269Ssbrunoimgact_binmisc_lookup_entry(char *name, ximgact_binmisc_entry_t *xbe)
381264269Ssbruno{
382264269Ssbruno	imgact_binmisc_entry_t *ibe;
383264269Ssbruno	int error = 0;
384264269Ssbruno
385264269Ssbruno	mtx_lock(&interp_list_mtx);
386264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
387264269Ssbruno		mtx_unlock(&interp_list_mtx);
388264269Ssbruno		return (ENOENT);
389264269Ssbruno	}
390264269Ssbruno
391264269Ssbruno	error = imgact_binmisc_populate_xbe(xbe, ibe);
392264269Ssbruno	mtx_unlock(&interp_list_mtx);
393264269Ssbruno
394264269Ssbruno	return (error);
395264269Ssbruno}
396264269Ssbruno
397264269Ssbruno/*
398264269Ssbruno * Get a snapshot of all the interpreter entries in the list.
399264269Ssbruno */
400264269Ssbrunostatic int
401264269Ssbrunoimgact_binmisc_get_all_entries(struct sysctl_req *req)
402264269Ssbruno{
403264269Ssbruno	ximgact_binmisc_entry_t *xbe, *xbep;
404264269Ssbruno	imgact_binmisc_entry_t *ibe;
405264269Ssbruno	int error = 0, count;
406264269Ssbruno
407264269Ssbruno	mtx_lock(&interp_list_mtx);
408264269Ssbruno	count = interp_list_entry_count;
409264269Ssbruno	/* Don't block in malloc() while holding lock. */
410264269Ssbruno	xbe = malloc(sizeof(*xbe) * count, M_BINMISC, M_NOWAIT|M_ZERO);
411264269Ssbruno	if (!xbe) {
412264269Ssbruno		mtx_unlock(&interp_list_mtx);
413264269Ssbruno		return (ENOMEM);
414264269Ssbruno	}
415264269Ssbruno
416264269Ssbruno	xbep = xbe;
417264269Ssbruno	SLIST_FOREACH(ibe, &interpreter_list, link) {
418264269Ssbruno		error = imgact_binmisc_populate_xbe(xbep++, ibe);
419264269Ssbruno		if (error)
420264269Ssbruno			break;
421264269Ssbruno	}
422264269Ssbruno	mtx_unlock(&interp_list_mtx);
423264269Ssbruno
424264269Ssbruno	if (!error)
425264269Ssbruno		error = SYSCTL_OUT(req, xbe, sizeof(*xbe) * count);
426264269Ssbruno
427264269Ssbruno	free(xbe, M_BINMISC);
428264269Ssbruno	return (error);
429264269Ssbruno}
430264269Ssbruno
431264269Ssbruno/*
432264269Ssbruno * sysctl() handler for munipulating interpretor table.
433264269Ssbruno * Not MP safe (locked by sysctl).
434264269Ssbruno */
435264269Ssbrunostatic int
436264269Ssbrunosysctl_kern_binmisc(SYSCTL_HANDLER_ARGS)
437264269Ssbruno{
438264269Ssbruno	ximgact_binmisc_entry_t xbe;
439264269Ssbruno	int error = 0;
440264269Ssbruno
441264269Ssbruno	switch(arg2) {
442264269Ssbruno	case IBC_ADD:
443264269Ssbruno		/* Add an entry. Limited to IBE_MAX_ENTRIES. */
444264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
445264269Ssbruno		if (error)
446264269Ssbruno			return (error);
447264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
448264269Ssbruno			return (EINVAL);
449264269Ssbruno		if (interp_list_entry_count == IBE_MAX_ENTRIES)
450264269Ssbruno			return (ENOSPC);
451264269Ssbruno		error = imgact_binmisc_add_entry(&xbe);
452264269Ssbruno		break;
453264269Ssbruno
454264269Ssbruno	case IBC_REMOVE:
455264269Ssbruno		/* Remove an entry. */
456264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
457264269Ssbruno		if (error)
458264269Ssbruno			return (error);
459264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
460264269Ssbruno			return (EINVAL);
461264269Ssbruno		error = imgact_binmisc_remove_entry(xbe.xbe_name);
462264269Ssbruno		break;
463264269Ssbruno
464264269Ssbruno	case IBC_DISABLE:
465264269Ssbruno		/* Disable an entry. */
466264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
467264269Ssbruno		if (error)
468264269Ssbruno			return (error);
469264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
470264269Ssbruno			return (EINVAL);
471264269Ssbruno		error = imgact_binmisc_disable_entry(xbe.xbe_name);
472264269Ssbruno		break;
473264269Ssbruno
474264269Ssbruno	case IBC_ENABLE:
475264269Ssbruno		/* Enable an entry. */
476264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
477264269Ssbruno		if (error)
478264269Ssbruno			return (error);
479264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
480264269Ssbruno			return (EINVAL);
481264269Ssbruno		error = imgact_binmisc_enable_entry(xbe.xbe_name);
482264269Ssbruno		break;
483264269Ssbruno
484264269Ssbruno	case IBC_LOOKUP:
485264269Ssbruno		/* Lookup an entry. */
486264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
487264269Ssbruno		if (error)
488264269Ssbruno			return (error);
489264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
490264269Ssbruno			return (EINVAL);
491264269Ssbruno		error = imgact_binmisc_lookup_entry(xbe.xbe_name, &xbe);
492264269Ssbruno		if (!error)
493264269Ssbruno			error = SYSCTL_OUT(req, &xbe, sizeof(xbe));
494264269Ssbruno		break;
495264269Ssbruno
496264269Ssbruno	case IBC_LIST:
497264269Ssbruno		/* Return a snapshot of the interpretor list. */
498264269Ssbruno
499264269Ssbruno		if (!req->oldptr) {
500264269Ssbruno			/* No pointer then just return the list size. */
501264269Ssbruno			error = SYSCTL_OUT(req, 0, interp_list_entry_count *
502264269Ssbruno			    sizeof(ximgact_binmisc_entry_t));
503264269Ssbruno			return (error);
504264269Ssbruno		} else
505264269Ssbruno			if (!req->oldlen)
506264269Ssbruno				return (EINVAL);
507264269Ssbruno
508264269Ssbruno		error = imgact_binmisc_get_all_entries(req);
509264269Ssbruno		break;
510264269Ssbruno
511264269Ssbruno	default:
512264269Ssbruno		return (EINVAL);
513264269Ssbruno	}
514264269Ssbruno
515264269Ssbruno	return (error);
516264269Ssbruno}
517264269Ssbruno
518264269SsbrunoSYSCTL_NODE(_kern, OID_AUTO, binmisc, CTLFLAG_RW, 0,
519264269Ssbruno    "Image activator for miscellaneous binaries");
520264269Ssbruno
521264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, add,
522264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ADD,
523264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
524264269Ssbruno    "Add an activator entry");
525264269Ssbruno
526264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, remove,
527264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_REMOVE,
528264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
529264269Ssbruno    "Remove an activator entry");
530264269Ssbruno
531264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, disable,
532264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_DISABLE,
533264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
534264269Ssbruno    "Disable an activator entry");
535264269Ssbruno
536264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, enable,
537264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ENABLE,
538264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
539264269Ssbruno    "Enable an activator entry");
540264269Ssbruno
541264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, lookup,
542264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_ANYBODY, NULL, IBC_LOOKUP,
543264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
544264269Ssbruno    "Lookup an activator entry");
545264269Ssbruno
546264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, list,
547264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RD|CTLFLAG_ANYBODY, NULL, IBC_LIST,
548264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
549264269Ssbruno    "Get snapshot of all the activator entries");
550264269Ssbruno
551264269Ssbrunostatic imgact_binmisc_entry_t *
552264269Ssbrunoimgact_binmisc_find_interpreter(const char *image_header)
553264269Ssbruno{
554264269Ssbruno	imgact_binmisc_entry_t *ibe;
555264269Ssbruno	const char *p;
556264269Ssbruno	int i;
557264269Ssbruno	size_t sz;
558264269Ssbruno
559264269Ssbruno	mtx_assert(&interp_list_mtx, MA_OWNED);
560264269Ssbruno
561264269Ssbruno	SLIST_FOREACH(ibe, &interpreter_list, link) {
562264269Ssbruno		if (!(IBF_ENABLED & ibe->ibe_flags))
563264269Ssbruno			continue;
564264269Ssbruno
565264269Ssbruno		p = image_header + ibe->ibe_moffset;
566264269Ssbruno		sz = ibe->ibe_msize;
567264269Ssbruno		if (IBF_USE_MASK & ibe->ibe_flags) {
568264269Ssbruno			/* Compare using mask. */
569264269Ssbruno			for (i = 0; i < sz; i++)
570264269Ssbruno				if ((*p++ ^ ibe->ibe_magic[i]) &
571264269Ssbruno				    ibe->ibe_mask[i])
572264269Ssbruno					break;
573264269Ssbruno		} else {
574264269Ssbruno			for (i = 0; i < sz; i++)
575264269Ssbruno				if (*p++ ^ ibe->ibe_magic[i])
576264269Ssbruno					break;
577264269Ssbruno		}
578264269Ssbruno		if (i == ibe->ibe_msize)
579264269Ssbruno			return (ibe);
580264269Ssbruno	}
581264269Ssbruno	return (NULL);
582264269Ssbruno}
583264269Ssbruno
584264269Ssbrunoint
585264269Ssbrunoimgact_binmisc_exec(struct image_params *imgp)
586264269Ssbruno{
587264269Ssbruno	const char *image_header = imgp->image_header;
588264269Ssbruno	const char *fname = NULL;
589264269Ssbruno	int error = 0;
590264269Ssbruno	size_t offset, l;
591264269Ssbruno	imgact_binmisc_entry_t *ibe;
592264269Ssbruno	struct sbuf *sname;
593264269Ssbruno	char *s, *d;
594264269Ssbruno
595264269Ssbruno	/* Do we have an interpreter for the given image header? */
596264269Ssbruno	mtx_lock(&interp_list_mtx);
597264269Ssbruno	if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) {
598264269Ssbruno		mtx_unlock(&interp_list_mtx);
599264269Ssbruno		return (-1);
600264269Ssbruno	}
601264269Ssbruno
602264269Ssbruno	/* No interpreter nesting allowed. */
603272450Ssbruno	if (imgp->interpreted & IMGACT_BINMISC) {
604264269Ssbruno		mtx_unlock(&interp_list_mtx);
605264269Ssbruno		return (ENOEXEC);
606264269Ssbruno	}
607264269Ssbruno
608272450Ssbruno	imgp->interpreted |= IMGACT_BINMISC;
609264269Ssbruno
610264269Ssbruno	if (imgp->args->fname != NULL) {
611264269Ssbruno		fname = imgp->args->fname;
612264269Ssbruno		sname = NULL;
613264269Ssbruno	} else {
614264269Ssbruno		/* Use the fdescfs(5) path for fexecve(2). */
615264269Ssbruno		sname = sbuf_new_auto();
616264269Ssbruno		sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd);
617264269Ssbruno		sbuf_finish(sname);
618264269Ssbruno		fname = sbuf_data(sname);
619264269Ssbruno	}
620264269Ssbruno
621264269Ssbruno
622264269Ssbruno	/*
623264269Ssbruno	 * We need to "push" the interpreter in the arg[] list.  To do this,
624264269Ssbruno	 * we first shift all the other values in the `begin_argv' area to
625264269Ssbruno	 * provide the exact amount of room for the values added.  Set up
626264269Ssbruno	 * `offset' as the number of bytes to be added to the `begin_argv'
627264269Ssbruno	 * area.
628264269Ssbruno	 */
629264269Ssbruno	offset = ibe->ibe_interp_length;
630264269Ssbruno
631264269Ssbruno	/* Adjust the offset for #'s. */
632264269Ssbruno	s = ibe->ibe_interpreter;
633264269Ssbruno	while (1) {
634264269Ssbruno		s = strchr(s, '#');
635264269Ssbruno		if (!s)
636264269Ssbruno			break;
637264269Ssbruno
638264269Ssbruno		s++;
639264269Ssbruno		switch(*s) {
640264269Ssbruno		case ISM_POUND:
641264269Ssbruno			/* "##" -> "#": reduce offset by one. */
642264269Ssbruno			offset--;
643264269Ssbruno			break;
644264269Ssbruno
645264269Ssbruno		case ISM_OLD_ARGV0:
646264269Ssbruno			/* "#a" -> (old argv0): increase offset to fit fname */
647264269Ssbruno			offset += strlen(fname) - 2;
648264269Ssbruno			break;
649264269Ssbruno
650264269Ssbruno		default:
651264269Ssbruno			/* Hmm... This shouldn't happen. */
652264269Ssbruno			mtx_unlock(&interp_list_mtx);
653264269Ssbruno			printf("%s: Unknown macro #%c sequence in "
654264269Ssbruno			    "interpreter string\n", KMOD_NAME, *(s + 1));
655264269Ssbruno			error = EINVAL;
656264269Ssbruno			goto done;
657264269Ssbruno		}
658264269Ssbruno		s++;
659264269Ssbruno	}
660264269Ssbruno
661264269Ssbruno	/* Check to make sure we won't overrun the stringspace. */
662264269Ssbruno	if (offset > imgp->args->stringspace) {
663264269Ssbruno		mtx_unlock(&interp_list_mtx);
664264269Ssbruno		error = E2BIG;
665264269Ssbruno		goto done;
666264269Ssbruno	}
667264269Ssbruno
668264269Ssbruno	/* Make room for the interpreter */
669264269Ssbruno	bcopy(imgp->args->begin_argv, imgp->args->begin_argv + offset,
670264269Ssbruno	    imgp->args->endp - imgp->args->begin_argv);
671264269Ssbruno
672264269Ssbruno	/* Adjust everything by the offset. */
673264269Ssbruno	imgp->args->begin_envv += offset;
674264269Ssbruno	imgp->args->endp += offset;
675264269Ssbruno	imgp->args->stringspace -= offset;
676264269Ssbruno
677264269Ssbruno	/* Add the new argument(s) in the count. */
678264269Ssbruno	imgp->args->argc += ibe->ibe_interp_argcnt;
679264269Ssbruno
680264269Ssbruno	/*
681264269Ssbruno	 * The original arg[] list has been shifted appropriately.  Copy in
682264269Ssbruno	 * the interpreter path.
683264269Ssbruno	 */
684264269Ssbruno	s = ibe->ibe_interpreter;
685264269Ssbruno	d = imgp->args->begin_argv;
686264269Ssbruno	while(*s != '\0') {
687264269Ssbruno		switch (*s) {
688264269Ssbruno		case '#':
689264269Ssbruno			/* Handle "#" in interpreter string. */
690264269Ssbruno			s++;
691264269Ssbruno			switch(*s) {
692264269Ssbruno			case ISM_POUND:
693264269Ssbruno				/* "##": Replace with a single '#' */
694264269Ssbruno				*d++ = '#';
695264269Ssbruno				break;
696264269Ssbruno
697264269Ssbruno			case ISM_OLD_ARGV0:
698264269Ssbruno				/* "#a": Replace with old arg0 (fname). */
699264269Ssbruno				if ((l = strlen(fname)) != 0) {
700264269Ssbruno					memcpy(d, fname, l);
701264269Ssbruno					d += l;
702264269Ssbruno				}
703264269Ssbruno				break;
704264269Ssbruno
705264269Ssbruno			default:
706264269Ssbruno				/* Shouldn't happen but skip it if it does. */
707264269Ssbruno				break;
708264269Ssbruno			}
709264269Ssbruno			break;
710264269Ssbruno
711264269Ssbruno		case ' ':
712264269Ssbruno			/* Replace space with NUL to seperate arguments. */
713264269Ssbruno			*d++ = '\0';
714264269Ssbruno			break;
715264269Ssbruno
716264269Ssbruno		default:
717264269Ssbruno			*d++ = *s;
718264269Ssbruno			break;
719264269Ssbruno		}
720264269Ssbruno		s++;
721264269Ssbruno	}
722264269Ssbruno	*d = '\0';
723264269Ssbruno	mtx_unlock(&interp_list_mtx);
724264269Ssbruno
725264269Ssbruno	if (!error)
726264269Ssbruno		imgp->interpreter_name = imgp->args->begin_argv;
727264269Ssbruno
728264269Ssbruno
729264269Ssbrunodone:
730264269Ssbruno	if (sname)
731264269Ssbruno		sbuf_delete(sname);
732264269Ssbruno	return (error);
733264269Ssbruno}
734264269Ssbruno
735264269Ssbrunostatic void
736264269Ssbrunoimgact_binmisc_init(void *arg)
737264269Ssbruno{
738264269Ssbruno
739264269Ssbruno	mtx_init(&interp_list_mtx, KMOD_NAME, NULL, MTX_DEF);
740264269Ssbruno}
741264269Ssbruno
742264269Ssbrunostatic void
743264269Ssbrunoimgact_binmisc_fini(void *arg)
744264269Ssbruno{
745264269Ssbruno	imgact_binmisc_entry_t *ibe, *ibe_tmp;
746264269Ssbruno
747264269Ssbruno	/* Free all the interpreters. */
748264269Ssbruno	mtx_lock(&interp_list_mtx);
749264269Ssbruno	SLIST_FOREACH_SAFE(ibe, &interpreter_list, link, ibe_tmp) {
750264269Ssbruno		SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry,
751264269Ssbruno		    link);
752264269Ssbruno		imgact_binmisc_destroy_entry(ibe);
753264269Ssbruno	}
754264269Ssbruno	mtx_unlock(&interp_list_mtx);
755264269Ssbruno
756264269Ssbruno	mtx_destroy(&interp_list_mtx);
757264269Ssbruno}
758264269Ssbruno
759264269SsbrunoSYSINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_init, 0);
760264269SsbrunoSYSUNINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_fini, 0);
761264269Ssbruno
762264269Ssbruno/*
763264269Ssbruno * Tell kern_execve.c about it, with a little help from the linker.
764264269Ssbruno */
765264269Ssbrunostatic struct execsw imgact_binmisc_execsw = { imgact_binmisc_exec, KMOD_NAME };
766264269SsbrunoEXEC_SET(imgact_binmisc, imgact_binmisc_execsw);
767