1297588Ssbruno/*
2297588Ssbruno * Copyright (c) 2013-16, 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;
225297588Ssbruno	int cnt;
226264269Ssbruno
227264269Ssbruno	if (xbe->xbe_msize > IBE_MAGIC_MAX)
228264269Ssbruno		return (EINVAL);
229264269Ssbruno
230297588Ssbruno	for(cnt = 0, p = xbe->xbe_name; *p != 0; cnt++, p++)
231297588Ssbruno		if (cnt >= IBE_NAME_MAX || !isascii((int)*p))
232264269Ssbruno			return (EINVAL);
233264269Ssbruno
234297588Ssbruno	for(cnt = 0, p = xbe->xbe_interpreter; *p != 0; cnt++, p++)
235297588Ssbruno		if (cnt >= IBE_INTERP_LEN_MAX || !isascii((int)*p))
236264269Ssbruno			return (EINVAL);
237264269Ssbruno
238264269Ssbruno	/* Make sure we don't have any invalid #'s. */
239264269Ssbruno	p = xbe->xbe_interpreter;
240264269Ssbruno	while (1) {
241264269Ssbruno		p = strchr(p, '#');
242264269Ssbruno		if (!p)
243264269Ssbruno			break;
244264269Ssbruno
245264269Ssbruno		p++;
246264269Ssbruno		switch(*p) {
247264269Ssbruno		case ISM_POUND:
248264269Ssbruno			/* "##" */
249264269Ssbruno			p++;
250264269Ssbruno			break;
251264269Ssbruno
252264269Ssbruno		case ISM_OLD_ARGV0:
253264269Ssbruno			/* "#a" */
254264269Ssbruno			p++;
255264269Ssbruno			break;
256264269Ssbruno
257264269Ssbruno		case 0:
258264269Ssbruno		default:
259264269Ssbruno			/* Anything besides the above is invalid. */
260264269Ssbruno			return (EINVAL);
261264269Ssbruno		}
262264269Ssbruno	}
263264269Ssbruno
264264269Ssbruno	mtx_lock(&interp_list_mtx);
265264269Ssbruno	if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) {
266264269Ssbruno		mtx_unlock(&interp_list_mtx);
267264269Ssbruno		return (EEXIST);
268264269Ssbruno	}
269264269Ssbruno	mtx_unlock(&interp_list_mtx);
270264269Ssbruno
271264269Ssbruno	ibe = imgact_binmisc_new_entry(xbe);
272264269Ssbruno
273264269Ssbruno	mtx_lock(&interp_list_mtx);
274264269Ssbruno	SLIST_INSERT_HEAD(&interpreter_list, ibe, link);
275264269Ssbruno	interp_list_entry_count++;
276264269Ssbruno	mtx_unlock(&interp_list_mtx);
277264269Ssbruno
278264269Ssbruno	return (0);
279264269Ssbruno}
280264269Ssbruno
281264269Ssbruno/*
282264269Ssbruno * Remove the interpreter in the list with the given name. Return ENOENT
283264269Ssbruno * if not found.
284264269Ssbruno */
285264269Ssbrunostatic int
286264269Ssbrunoimgact_binmisc_remove_entry(char *name)
287264269Ssbruno{
288264269Ssbruno	imgact_binmisc_entry_t *ibe;
289264269Ssbruno
290264269Ssbruno	mtx_lock(&interp_list_mtx);
291264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
292264269Ssbruno		mtx_unlock(&interp_list_mtx);
293264269Ssbruno		return (ENOENT);
294264269Ssbruno	}
295264269Ssbruno	SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link);
296264269Ssbruno	interp_list_entry_count--;
297264269Ssbruno	mtx_unlock(&interp_list_mtx);
298264269Ssbruno
299264269Ssbruno	imgact_binmisc_destroy_entry(ibe);
300264269Ssbruno
301264269Ssbruno	return (0);
302264269Ssbruno}
303264269Ssbruno
304264269Ssbruno/*
305264269Ssbruno * Disable the interpreter in the list with the given name. Return ENOENT
306264269Ssbruno * if not found.
307264269Ssbruno */
308264269Ssbrunostatic int
309264269Ssbrunoimgact_binmisc_disable_entry(char *name)
310264269Ssbruno{
311264269Ssbruno	imgact_binmisc_entry_t *ibe;
312264269Ssbruno
313264269Ssbruno	mtx_lock(&interp_list_mtx);
314264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
315264269Ssbruno		mtx_unlock(&interp_list_mtx);
316264269Ssbruno		return (ENOENT);
317264269Ssbruno	}
318264269Ssbruno
319264269Ssbruno	ibe->ibe_flags &= ~IBF_ENABLED;
320264269Ssbruno	mtx_unlock(&interp_list_mtx);
321264269Ssbruno
322264269Ssbruno	return (0);
323264269Ssbruno}
324264269Ssbruno
325264269Ssbruno/*
326264269Ssbruno * Enable the interpreter in the list with the given name. Return ENOENT
327264269Ssbruno * if not found.
328264269Ssbruno */
329264269Ssbrunostatic int
330264269Ssbrunoimgact_binmisc_enable_entry(char *name)
331264269Ssbruno{
332264269Ssbruno	imgact_binmisc_entry_t *ibe;
333264269Ssbruno
334264269Ssbruno	mtx_lock(&interp_list_mtx);
335264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
336264269Ssbruno		mtx_unlock(&interp_list_mtx);
337264269Ssbruno		return (ENOENT);
338264269Ssbruno	}
339264269Ssbruno
340264269Ssbruno	ibe->ibe_flags |= IBF_ENABLED;
341264269Ssbruno	mtx_unlock(&interp_list_mtx);
342264269Ssbruno
343264269Ssbruno	return (0);
344264269Ssbruno}
345264269Ssbruno
346264269Ssbrunostatic int
347264269Ssbrunoimgact_binmisc_populate_xbe(ximgact_binmisc_entry_t *xbe,
348264269Ssbruno    imgact_binmisc_entry_t *ibe)
349264269Ssbruno{
350264269Ssbruno	uint32_t i;
351264269Ssbruno
352264269Ssbruno	mtx_assert(&interp_list_mtx, MA_OWNED);
353264269Ssbruno
354264269Ssbruno	bzero(xbe, sizeof(*xbe));
355264269Ssbruno	strlcpy(xbe->xbe_name, ibe->ibe_name, IBE_NAME_MAX);
356264269Ssbruno
357264269Ssbruno	/* Copy interpreter string.  Replace NULL breaks with space. */
358264269Ssbruno	memcpy(xbe->xbe_interpreter, ibe->ibe_interpreter,
359264269Ssbruno	    ibe->ibe_interp_length);
360264269Ssbruno	for(i = 0; i < (ibe->ibe_interp_length - 1); i++)
361264269Ssbruno		if (xbe->xbe_interpreter[i] == '\0')
362264269Ssbruno			xbe->xbe_interpreter[i] = ' ';
363264269Ssbruno
364264269Ssbruno	memcpy(xbe->xbe_magic, ibe->ibe_magic, ibe->ibe_msize);
365264269Ssbruno	memcpy(xbe->xbe_mask, ibe->ibe_mask, ibe->ibe_msize);
366264269Ssbruno	xbe->xbe_version = IBE_VERSION;
367264269Ssbruno	xbe->xbe_flags = ibe->ibe_flags;
368264269Ssbruno	xbe->xbe_moffset = ibe->ibe_moffset;
369264269Ssbruno	xbe->xbe_msize = ibe->ibe_msize;
370264269Ssbruno
371264269Ssbruno	return (0);
372264269Ssbruno}
373264269Ssbruno
374264269Ssbruno/*
375264269Ssbruno * Retrieve the interpreter with the give name and populate the
376264269Ssbruno * ximgact_binmisc_entry structure.  Return ENOENT if not found.
377264269Ssbruno */
378264269Ssbrunostatic int
379264269Ssbrunoimgact_binmisc_lookup_entry(char *name, ximgact_binmisc_entry_t *xbe)
380264269Ssbruno{
381264269Ssbruno	imgact_binmisc_entry_t *ibe;
382264269Ssbruno	int error = 0;
383264269Ssbruno
384264269Ssbruno	mtx_lock(&interp_list_mtx);
385264269Ssbruno	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
386264269Ssbruno		mtx_unlock(&interp_list_mtx);
387264269Ssbruno		return (ENOENT);
388264269Ssbruno	}
389264269Ssbruno
390264269Ssbruno	error = imgact_binmisc_populate_xbe(xbe, ibe);
391264269Ssbruno	mtx_unlock(&interp_list_mtx);
392264269Ssbruno
393264269Ssbruno	return (error);
394264269Ssbruno}
395264269Ssbruno
396264269Ssbruno/*
397264269Ssbruno * Get a snapshot of all the interpreter entries in the list.
398264269Ssbruno */
399264269Ssbrunostatic int
400264269Ssbrunoimgact_binmisc_get_all_entries(struct sysctl_req *req)
401264269Ssbruno{
402264269Ssbruno	ximgact_binmisc_entry_t *xbe, *xbep;
403264269Ssbruno	imgact_binmisc_entry_t *ibe;
404264269Ssbruno	int error = 0, count;
405264269Ssbruno
406264269Ssbruno	mtx_lock(&interp_list_mtx);
407264269Ssbruno	count = interp_list_entry_count;
408264269Ssbruno	/* Don't block in malloc() while holding lock. */
409264269Ssbruno	xbe = malloc(sizeof(*xbe) * count, M_BINMISC, M_NOWAIT|M_ZERO);
410264269Ssbruno	if (!xbe) {
411264269Ssbruno		mtx_unlock(&interp_list_mtx);
412264269Ssbruno		return (ENOMEM);
413264269Ssbruno	}
414264269Ssbruno
415264269Ssbruno	xbep = xbe;
416264269Ssbruno	SLIST_FOREACH(ibe, &interpreter_list, link) {
417264269Ssbruno		error = imgact_binmisc_populate_xbe(xbep++, ibe);
418264269Ssbruno		if (error)
419264269Ssbruno			break;
420264269Ssbruno	}
421264269Ssbruno	mtx_unlock(&interp_list_mtx);
422264269Ssbruno
423264269Ssbruno	if (!error)
424264269Ssbruno		error = SYSCTL_OUT(req, xbe, sizeof(*xbe) * count);
425264269Ssbruno
426264269Ssbruno	free(xbe, M_BINMISC);
427264269Ssbruno	return (error);
428264269Ssbruno}
429264269Ssbruno
430264269Ssbruno/*
431264269Ssbruno * sysctl() handler for munipulating interpretor table.
432264269Ssbruno * Not MP safe (locked by sysctl).
433264269Ssbruno */
434264269Ssbrunostatic int
435264269Ssbrunosysctl_kern_binmisc(SYSCTL_HANDLER_ARGS)
436264269Ssbruno{
437264269Ssbruno	ximgact_binmisc_entry_t xbe;
438264269Ssbruno	int error = 0;
439264269Ssbruno
440264269Ssbruno	switch(arg2) {
441264269Ssbruno	case IBC_ADD:
442264269Ssbruno		/* Add an entry. Limited to IBE_MAX_ENTRIES. */
443264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
444264269Ssbruno		if (error)
445264269Ssbruno			return (error);
446264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
447264269Ssbruno			return (EINVAL);
448264269Ssbruno		if (interp_list_entry_count == IBE_MAX_ENTRIES)
449264269Ssbruno			return (ENOSPC);
450264269Ssbruno		error = imgact_binmisc_add_entry(&xbe);
451264269Ssbruno		break;
452264269Ssbruno
453264269Ssbruno	case IBC_REMOVE:
454264269Ssbruno		/* Remove an entry. */
455264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
456264269Ssbruno		if (error)
457264269Ssbruno			return (error);
458264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
459264269Ssbruno			return (EINVAL);
460264269Ssbruno		error = imgact_binmisc_remove_entry(xbe.xbe_name);
461264269Ssbruno		break;
462264269Ssbruno
463264269Ssbruno	case IBC_DISABLE:
464264269Ssbruno		/* Disable an entry. */
465264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
466264269Ssbruno		if (error)
467264269Ssbruno			return (error);
468264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
469264269Ssbruno			return (EINVAL);
470264269Ssbruno		error = imgact_binmisc_disable_entry(xbe.xbe_name);
471264269Ssbruno		break;
472264269Ssbruno
473264269Ssbruno	case IBC_ENABLE:
474264269Ssbruno		/* Enable an entry. */
475264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
476264269Ssbruno		if (error)
477264269Ssbruno			return (error);
478264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
479264269Ssbruno			return (EINVAL);
480264269Ssbruno		error = imgact_binmisc_enable_entry(xbe.xbe_name);
481264269Ssbruno		break;
482264269Ssbruno
483264269Ssbruno	case IBC_LOOKUP:
484264269Ssbruno		/* Lookup an entry. */
485264269Ssbruno		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
486264269Ssbruno		if (error)
487264269Ssbruno			return (error);
488264269Ssbruno		if (IBE_VERSION != xbe.xbe_version)
489264269Ssbruno			return (EINVAL);
490264269Ssbruno		error = imgact_binmisc_lookup_entry(xbe.xbe_name, &xbe);
491264269Ssbruno		if (!error)
492264269Ssbruno			error = SYSCTL_OUT(req, &xbe, sizeof(xbe));
493264269Ssbruno		break;
494264269Ssbruno
495264269Ssbruno	case IBC_LIST:
496264269Ssbruno		/* Return a snapshot of the interpretor list. */
497264269Ssbruno
498264269Ssbruno		if (!req->oldptr) {
499264269Ssbruno			/* No pointer then just return the list size. */
500264269Ssbruno			error = SYSCTL_OUT(req, 0, interp_list_entry_count *
501264269Ssbruno			    sizeof(ximgact_binmisc_entry_t));
502264269Ssbruno			return (error);
503264269Ssbruno		} else
504264269Ssbruno			if (!req->oldlen)
505264269Ssbruno				return (EINVAL);
506264269Ssbruno
507264269Ssbruno		error = imgact_binmisc_get_all_entries(req);
508264269Ssbruno		break;
509264269Ssbruno
510264269Ssbruno	default:
511264269Ssbruno		return (EINVAL);
512264269Ssbruno	}
513264269Ssbruno
514264269Ssbruno	return (error);
515264269Ssbruno}
516264269Ssbruno
517264269SsbrunoSYSCTL_NODE(_kern, OID_AUTO, binmisc, CTLFLAG_RW, 0,
518264269Ssbruno    "Image activator for miscellaneous binaries");
519264269Ssbruno
520264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, add,
521264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ADD,
522264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
523264269Ssbruno    "Add an activator entry");
524264269Ssbruno
525264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, remove,
526264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_REMOVE,
527264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
528264269Ssbruno    "Remove an activator entry");
529264269Ssbruno
530264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, disable,
531264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_DISABLE,
532264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
533264269Ssbruno    "Disable an activator entry");
534264269Ssbruno
535264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, enable,
536264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ENABLE,
537264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
538264269Ssbruno    "Enable an activator entry");
539264269Ssbruno
540264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, lookup,
541264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_ANYBODY, NULL, IBC_LOOKUP,
542264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
543264269Ssbruno    "Lookup an activator entry");
544264269Ssbruno
545264269SsbrunoSYSCTL_PROC(_kern_binmisc, OID_AUTO, list,
546264269Ssbruno    CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RD|CTLFLAG_ANYBODY, NULL, IBC_LIST,
547264269Ssbruno    sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
548264269Ssbruno    "Get snapshot of all the activator entries");
549264269Ssbruno
550264269Ssbrunostatic imgact_binmisc_entry_t *
551264269Ssbrunoimgact_binmisc_find_interpreter(const char *image_header)
552264269Ssbruno{
553264269Ssbruno	imgact_binmisc_entry_t *ibe;
554264269Ssbruno	const char *p;
555264269Ssbruno	int i;
556264269Ssbruno	size_t sz;
557264269Ssbruno
558264269Ssbruno	mtx_assert(&interp_list_mtx, MA_OWNED);
559264269Ssbruno
560264269Ssbruno	SLIST_FOREACH(ibe, &interpreter_list, link) {
561264269Ssbruno		if (!(IBF_ENABLED & ibe->ibe_flags))
562264269Ssbruno			continue;
563264269Ssbruno
564264269Ssbruno		p = image_header + ibe->ibe_moffset;
565264269Ssbruno		sz = ibe->ibe_msize;
566264269Ssbruno		if (IBF_USE_MASK & ibe->ibe_flags) {
567264269Ssbruno			/* Compare using mask. */
568264269Ssbruno			for (i = 0; i < sz; i++)
569264269Ssbruno				if ((*p++ ^ ibe->ibe_magic[i]) &
570264269Ssbruno				    ibe->ibe_mask[i])
571264269Ssbruno					break;
572264269Ssbruno		} else {
573264269Ssbruno			for (i = 0; i < sz; i++)
574264269Ssbruno				if (*p++ ^ ibe->ibe_magic[i])
575264269Ssbruno					break;
576264269Ssbruno		}
577264269Ssbruno		if (i == ibe->ibe_msize)
578264269Ssbruno			return (ibe);
579264269Ssbruno	}
580264269Ssbruno	return (NULL);
581264269Ssbruno}
582264269Ssbruno
583264269Ssbrunoint
584264269Ssbrunoimgact_binmisc_exec(struct image_params *imgp)
585264269Ssbruno{
586264269Ssbruno	const char *image_header = imgp->image_header;
587264269Ssbruno	const char *fname = NULL;
588264269Ssbruno	int error = 0;
589264269Ssbruno	size_t offset, l;
590264269Ssbruno	imgact_binmisc_entry_t *ibe;
591264269Ssbruno	struct sbuf *sname;
592264269Ssbruno	char *s, *d;
593264269Ssbruno
594264269Ssbruno	/* Do we have an interpreter for the given image header? */
595264269Ssbruno	mtx_lock(&interp_list_mtx);
596264269Ssbruno	if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) {
597264269Ssbruno		mtx_unlock(&interp_list_mtx);
598264269Ssbruno		return (-1);
599264269Ssbruno	}
600264269Ssbruno
601264269Ssbruno	/* No interpreter nesting allowed. */
602272450Ssbruno	if (imgp->interpreted & IMGACT_BINMISC) {
603264269Ssbruno		mtx_unlock(&interp_list_mtx);
604264269Ssbruno		return (ENOEXEC);
605264269Ssbruno	}
606264269Ssbruno
607272450Ssbruno	imgp->interpreted |= IMGACT_BINMISC;
608264269Ssbruno
609264269Ssbruno	if (imgp->args->fname != NULL) {
610264269Ssbruno		fname = imgp->args->fname;
611264269Ssbruno		sname = NULL;
612264269Ssbruno	} else {
613264269Ssbruno		/* Use the fdescfs(5) path for fexecve(2). */
614264269Ssbruno		sname = sbuf_new_auto();
615264269Ssbruno		sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd);
616264269Ssbruno		sbuf_finish(sname);
617264269Ssbruno		fname = sbuf_data(sname);
618264269Ssbruno	}
619264269Ssbruno
620264269Ssbruno
621264269Ssbruno	/*
622264269Ssbruno	 * We need to "push" the interpreter in the arg[] list.  To do this,
623264269Ssbruno	 * we first shift all the other values in the `begin_argv' area to
624264269Ssbruno	 * provide the exact amount of room for the values added.  Set up
625264269Ssbruno	 * `offset' as the number of bytes to be added to the `begin_argv'
626264269Ssbruno	 * area.
627264269Ssbruno	 */
628264269Ssbruno	offset = ibe->ibe_interp_length;
629264269Ssbruno
630264269Ssbruno	/* Adjust the offset for #'s. */
631264269Ssbruno	s = ibe->ibe_interpreter;
632264269Ssbruno	while (1) {
633264269Ssbruno		s = strchr(s, '#');
634264269Ssbruno		if (!s)
635264269Ssbruno			break;
636264269Ssbruno
637264269Ssbruno		s++;
638264269Ssbruno		switch(*s) {
639264269Ssbruno		case ISM_POUND:
640264269Ssbruno			/* "##" -> "#": reduce offset by one. */
641264269Ssbruno			offset--;
642264269Ssbruno			break;
643264269Ssbruno
644264269Ssbruno		case ISM_OLD_ARGV0:
645264269Ssbruno			/* "#a" -> (old argv0): increase offset to fit fname */
646264269Ssbruno			offset += strlen(fname) - 2;
647264269Ssbruno			break;
648264269Ssbruno
649264269Ssbruno		default:
650264269Ssbruno			/* Hmm... This shouldn't happen. */
651264269Ssbruno			mtx_unlock(&interp_list_mtx);
652264269Ssbruno			printf("%s: Unknown macro #%c sequence in "
653264269Ssbruno			    "interpreter string\n", KMOD_NAME, *(s + 1));
654264269Ssbruno			error = EINVAL;
655264269Ssbruno			goto done;
656264269Ssbruno		}
657264269Ssbruno		s++;
658264269Ssbruno	}
659264269Ssbruno
660264269Ssbruno	/* Check to make sure we won't overrun the stringspace. */
661264269Ssbruno	if (offset > imgp->args->stringspace) {
662264269Ssbruno		mtx_unlock(&interp_list_mtx);
663264269Ssbruno		error = E2BIG;
664264269Ssbruno		goto done;
665264269Ssbruno	}
666264269Ssbruno
667264269Ssbruno	/* Make room for the interpreter */
668264269Ssbruno	bcopy(imgp->args->begin_argv, imgp->args->begin_argv + offset,
669264269Ssbruno	    imgp->args->endp - imgp->args->begin_argv);
670264269Ssbruno
671264269Ssbruno	/* Adjust everything by the offset. */
672264269Ssbruno	imgp->args->begin_envv += offset;
673264269Ssbruno	imgp->args->endp += offset;
674264269Ssbruno	imgp->args->stringspace -= offset;
675264269Ssbruno
676264269Ssbruno	/* Add the new argument(s) in the count. */
677264269Ssbruno	imgp->args->argc += ibe->ibe_interp_argcnt;
678264269Ssbruno
679264269Ssbruno	/*
680264269Ssbruno	 * The original arg[] list has been shifted appropriately.  Copy in
681264269Ssbruno	 * the interpreter path.
682264269Ssbruno	 */
683264269Ssbruno	s = ibe->ibe_interpreter;
684264269Ssbruno	d = imgp->args->begin_argv;
685264269Ssbruno	while(*s != '\0') {
686264269Ssbruno		switch (*s) {
687264269Ssbruno		case '#':
688264269Ssbruno			/* Handle "#" in interpreter string. */
689264269Ssbruno			s++;
690264269Ssbruno			switch(*s) {
691264269Ssbruno			case ISM_POUND:
692264269Ssbruno				/* "##": Replace with a single '#' */
693264269Ssbruno				*d++ = '#';
694264269Ssbruno				break;
695264269Ssbruno
696264269Ssbruno			case ISM_OLD_ARGV0:
697264269Ssbruno				/* "#a": Replace with old arg0 (fname). */
698264269Ssbruno				if ((l = strlen(fname)) != 0) {
699264269Ssbruno					memcpy(d, fname, l);
700264269Ssbruno					d += l;
701264269Ssbruno				}
702264269Ssbruno				break;
703264269Ssbruno
704264269Ssbruno			default:
705264269Ssbruno				/* Shouldn't happen but skip it if it does. */
706264269Ssbruno				break;
707264269Ssbruno			}
708264269Ssbruno			break;
709264269Ssbruno
710264269Ssbruno		case ' ':
711302234Sbdrewery			/* Replace space with NUL to separate arguments. */
712264269Ssbruno			*d++ = '\0';
713264269Ssbruno			break;
714264269Ssbruno
715264269Ssbruno		default:
716264269Ssbruno			*d++ = *s;
717264269Ssbruno			break;
718264269Ssbruno		}
719264269Ssbruno		s++;
720264269Ssbruno	}
721264269Ssbruno	*d = '\0';
722264269Ssbruno	mtx_unlock(&interp_list_mtx);
723264269Ssbruno
724264269Ssbruno	if (!error)
725264269Ssbruno		imgp->interpreter_name = imgp->args->begin_argv;
726264269Ssbruno
727264269Ssbruno
728264269Ssbrunodone:
729264269Ssbruno	if (sname)
730264269Ssbruno		sbuf_delete(sname);
731264269Ssbruno	return (error);
732264269Ssbruno}
733264269Ssbruno
734264269Ssbrunostatic void
735264269Ssbrunoimgact_binmisc_init(void *arg)
736264269Ssbruno{
737264269Ssbruno
738264269Ssbruno	mtx_init(&interp_list_mtx, KMOD_NAME, NULL, MTX_DEF);
739264269Ssbruno}
740264269Ssbruno
741264269Ssbrunostatic void
742264269Ssbrunoimgact_binmisc_fini(void *arg)
743264269Ssbruno{
744264269Ssbruno	imgact_binmisc_entry_t *ibe, *ibe_tmp;
745264269Ssbruno
746264269Ssbruno	/* Free all the interpreters. */
747264269Ssbruno	mtx_lock(&interp_list_mtx);
748264269Ssbruno	SLIST_FOREACH_SAFE(ibe, &interpreter_list, link, ibe_tmp) {
749264269Ssbruno		SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry,
750264269Ssbruno		    link);
751264269Ssbruno		imgact_binmisc_destroy_entry(ibe);
752264269Ssbruno	}
753264269Ssbruno	mtx_unlock(&interp_list_mtx);
754264269Ssbruno
755264269Ssbruno	mtx_destroy(&interp_list_mtx);
756264269Ssbruno}
757264269Ssbruno
758264269SsbrunoSYSINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_init, 0);
759264269SsbrunoSYSUNINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_fini, 0);
760264269Ssbruno
761264269Ssbruno/*
762264269Ssbruno * Tell kern_execve.c about it, with a little help from the linker.
763264269Ssbruno */
764264269Ssbrunostatic struct execsw imgact_binmisc_execsw = { imgact_binmisc_exec, KMOD_NAME };
765264269SsbrunoEXEC_SET(imgact_binmisc, imgact_binmisc_execsw);
766