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