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