1214117Sjamie/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4223190Sjamie * Copyright (c) 2011 James Gritton 5214117Sjamie * All rights reserved. 6214117Sjamie * 7214117Sjamie * Redistribution and use in source and binary forms, with or without 8214117Sjamie * modification, are permitted provided that the following conditions 9214117Sjamie * are met: 10214117Sjamie * 1. Redistributions of source code must retain the above copyright 11214117Sjamie * notice, this list of conditions and the following disclaimer. 12214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright 13214117Sjamie * notice, this list of conditions and the following disclaimer in the 14214117Sjamie * documentation and/or other materials provided with the distribution. 15214117Sjamie * 16214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19214117Sjamie * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26214117Sjamie * SUCH DAMAGE. 27214117Sjamie */ 28214117Sjamie 29214117Sjamie#include <sys/cdefs.h> 30214117Sjamie__FBSDID("$FreeBSD: stable/11/usr.sbin/jail/config.c 370034 2021-06-24 17:55:37Z jamie $"); 31214117Sjamie 32214117Sjamie#include <sys/types.h> 33214783Sjamie#include <sys/errno.h> 34214117Sjamie#include <sys/socket.h> 35214117Sjamie#include <sys/sysctl.h> 36214117Sjamie 37214117Sjamie#include <arpa/inet.h> 38214117Sjamie#include <netinet/in.h> 39214117Sjamie 40214117Sjamie#include <err.h> 41214117Sjamie#include <netdb.h> 42214117Sjamie#include <stdio.h> 43214117Sjamie#include <stdlib.h> 44214117Sjamie#include <string.h> 45223351Sjamie#include <unistd.h> 46214117Sjamie 47214117Sjamie#include "jailp.h" 48214117Sjamie 49214117Sjamiestruct ipspec { 50214117Sjamie const char *name; 51214117Sjamie unsigned flags; 52214117Sjamie}; 53214117Sjamie 54214117Sjamieextern FILE *yyin; 55214117Sjamieextern int yynerrs; 56214117Sjamie 57235789Sbaptextern int yyparse(void); 58235789Sbapt 59214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails); 60214117Sjamie 61214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p); 62214117Sjamiestatic void free_param_strings(struct cfparam *p); 63214117Sjamie 64214117Sjamiestatic const struct ipspec intparams[] = { 65214423Sjamie [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL}, 66214423Sjamie [IP_COMMAND] = {"command", PF_INTERNAL}, 67214423Sjamie [IP_DEPEND] = {"depend", PF_INTERNAL}, 68214423Sjamie [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL}, 69214423Sjamie [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL}, 70214423Sjamie [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT}, 71214423Sjamie [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL}, 72214423Sjamie [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL}, 73214423Sjamie [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL}, 74214423Sjamie [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL}, 75214423Sjamie [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL}, 76214423Sjamie [IP_EXEC_START] = {"exec.start", PF_INTERNAL}, 77214423Sjamie [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL}, 78214423Sjamie [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user", 79214423Sjamie PF_INTERNAL | PF_BOOL}, 80214423Sjamie [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL}, 81214423Sjamie [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT}, 82223351Sjamie#if defined(INET) || defined(INET6) 83214423Sjamie [IP_INTERFACE] = {"interface", PF_INTERNAL}, 84214423Sjamie [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL}, 85223351Sjamie#endif 86248854Sjamie [IP_MOUNT] = {"mount", PF_INTERNAL | PF_REV}, 87214423Sjamie [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, 88256385Shrs [IP_MOUNT_FDESCFS] = {"mount.fdescfs", PF_INTERNAL | PF_BOOL}, 89278323Sjamie [IP_MOUNT_PROCFS] = {"mount.procfs", PF_INTERNAL | PF_BOOL}, 90214423Sjamie [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, 91214423Sjamie [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, 92214423Sjamie [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, 93223351Sjamie#ifdef INET 94248854Sjamie [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV | PF_REV}, 95223351Sjamie#endif 96214117Sjamie#ifdef INET6 97248854Sjamie [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV | PF_REV}, 98214117Sjamie#endif 99248854Sjamie [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV | PF_REV}, 100223189Sjamie [IP__OP] = {NULL, PF_CONV}, 101214423Sjamie [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, 102214423Sjamie [KP_ALLOW_MOUNT] = {"allow.mount", 0}, 103214423Sjamie [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, 104214423Sjamie [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, 105214423Sjamie [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, 106214423Sjamie [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, 107232242Sjamie [KP_DEVFS_RULESET] = {"devfs_ruleset", 0}, 108214423Sjamie [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, 109214423Sjamie [KP_HOST_HOSTNAME] = {"host.hostname", 0}, 110223351Sjamie#ifdef INET 111214423Sjamie [KP_IP4_ADDR] = {"ip4.addr", 0}, 112223351Sjamie#endif 113214423Sjamie#ifdef INET6 114214423Sjamie [KP_IP6_ADDR] = {"ip6.addr", 0}, 115214423Sjamie#endif 116285279Shrs [KP_JID] = {"jid", PF_IMMUTABLE}, 117285279Shrs [KP_NAME] = {"name", PF_IMMUTABLE}, 118214423Sjamie [KP_PATH] = {"path", 0}, 119214423Sjamie [KP_PERSIST] = {"persist", 0}, 120214423Sjamie [KP_SECURELEVEL] = {"securelevel", 0}, 121214423Sjamie [KP_VNET] = {"vnet", 0}, 122214117Sjamie}; 123214117Sjamie 124214117Sjamie/* 125214117Sjamie * Parse the jail configuration file. 126214117Sjamie */ 127214117Sjamievoid 128214117Sjamieload_config(void) 129214117Sjamie{ 130214117Sjamie struct cfjails wild; 131214117Sjamie struct cfparams opp; 132214117Sjamie struct cfjail *j, *tj, *wj; 133214117Sjamie struct cfparam *p, *vp, *tp; 134214117Sjamie struct cfstring *s, *vs, *ns; 135285261Shrs struct cfvar *v, *vv; 136214117Sjamie char *ep; 137214117Sjamie int did_self, jseq, pgen; 138214117Sjamie 139214117Sjamie if (!strcmp(cfname, "-")) { 140214117Sjamie cfname = "STDIN"; 141214117Sjamie yyin = stdin; 142214117Sjamie } else { 143214117Sjamie yyin = fopen(cfname, "r"); 144214117Sjamie if (!yyin) 145214117Sjamie err(1, "%s", cfname); 146214117Sjamie } 147214117Sjamie if (yyparse() || yynerrs) 148214117Sjamie exit(1); 149214117Sjamie 150214117Sjamie /* Separate the wildcard jails out from the actual jails. */ 151214117Sjamie jseq = 0; 152214117Sjamie TAILQ_INIT(&wild); 153214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 154214117Sjamie j->seq = ++jseq; 155214117Sjamie if (wild_jail_name(j->name)) 156214117Sjamie requeue(j, &wild); 157214117Sjamie } 158214117Sjamie 159214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) { 160214117Sjamie /* Set aside the jail's parameters. */ 161214117Sjamie TAILQ_INIT(&opp); 162214117Sjamie TAILQ_CONCAT(&opp, &j->params, tq); 163214117Sjamie /* 164214117Sjamie * The jail name implies its "name" or "jid" parameter, 165214117Sjamie * though they may also be explicitly set later on. 166214117Sjamie */ 167214117Sjamie add_param(j, NULL, 168214423Sjamie strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, 169214117Sjamie j->name); 170214117Sjamie /* 171214117Sjamie * Collect parameters for the jail, global parameters/variables, 172214117Sjamie * and any matching wildcard jails. 173214117Sjamie */ 174214117Sjamie did_self = 0; 175214117Sjamie TAILQ_FOREACH(wj, &wild, tq) { 176214117Sjamie if (j->seq < wj->seq && !did_self) { 177214117Sjamie TAILQ_FOREACH(p, &opp, tq) 178214423Sjamie add_param(j, p, 0, NULL); 179214117Sjamie did_self = 1; 180214117Sjamie } 181214117Sjamie if (wild_jail_match(j->name, wj->name)) 182214117Sjamie TAILQ_FOREACH(p, &wj->params, tq) 183214423Sjamie add_param(j, p, 0, NULL); 184214117Sjamie } 185214117Sjamie if (!did_self) 186214117Sjamie TAILQ_FOREACH(p, &opp, tq) 187214423Sjamie add_param(j, p, 0, NULL); 188214117Sjamie 189214117Sjamie /* Resolve any variable substitutions. */ 190214117Sjamie pgen = 0; 191214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 192214117Sjamie p->gen = ++pgen; 193214117Sjamie find_vars: 194223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 195214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 196214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 197214117Sjamie if (!strcmp(vp->name, v->name)) 198214117Sjamie break; 199370034Sjamie if (!vp || TAILQ_EMPTY(&vp->val)) { 200214117Sjamie jail_warnx(j, 201214117Sjamie "%s: variable \"%s\" not found", 202214117Sjamie p->name, v->name); 203214117Sjamie bad_var: 204214117Sjamie j->flags |= JF_FAILED; 205214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 206214117Sjamie if (vp->gen == pgen) 207214117Sjamie vp->flags |= PF_BAD; 208214117Sjamie goto free_var; 209214117Sjamie } 210214117Sjamie if (vp->flags & PF_BAD) 211214117Sjamie goto bad_var; 212214117Sjamie if (vp->gen == pgen) { 213214117Sjamie jail_warnx(j, "%s: variable loop", 214214117Sjamie v->name); 215214117Sjamie goto bad_var; 216214117Sjamie } 217223188Sjamie TAILQ_FOREACH(vs, &vp->val, tq) 218214117Sjamie if (!STAILQ_EMPTY(&vs->vars)) { 219214117Sjamie vp->gen = pgen; 220214117Sjamie TAILQ_REMOVE(&j->params, vp, 221214117Sjamie tq); 222214117Sjamie TAILQ_INSERT_BEFORE(p, vp, tq); 223214117Sjamie p = vp; 224214117Sjamie goto find_vars; 225214117Sjamie } 226223188Sjamie vs = TAILQ_FIRST(&vp->val); 227223188Sjamie if (TAILQ_NEXT(vs, tq) != NULL && 228214117Sjamie (s->s[0] != '\0' || 229214117Sjamie STAILQ_NEXT(v, tq))) { 230214117Sjamie jail_warnx(j, "%s: array cannot be " 231214117Sjamie "substituted inline", 232214117Sjamie p->name); 233214117Sjamie goto bad_var; 234214117Sjamie } 235214117Sjamie s->s = erealloc(s->s, s->len + vs->len + 1); 236285261Shrs memmove(s->s + v->pos + vs->len, 237285261Shrs s->s + v->pos, 238285261Shrs s->len - v->pos + 1); 239285261Shrs memcpy(s->s + v->pos, vs->s, vs->len); 240285261Shrs vv = v; 241285261Shrs while ((vv = STAILQ_NEXT(vv, tq))) 242285261Shrs vv->pos += vs->len; 243214117Sjamie s->len += vs->len; 244223188Sjamie while ((vs = TAILQ_NEXT(vs, tq))) { 245214117Sjamie ns = emalloc(sizeof(struct cfstring)); 246214117Sjamie ns->s = estrdup(vs->s); 247214117Sjamie ns->len = vs->len; 248214117Sjamie STAILQ_INIT(&ns->vars); 249223188Sjamie TAILQ_INSERT_AFTER(&p->val, s, ns, tq); 250214117Sjamie s = ns; 251214117Sjamie } 252214117Sjamie free_var: 253214117Sjamie free(v->name); 254214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 255214117Sjamie free(v); 256214117Sjamie } 257214117Sjamie } 258214117Sjamie } 259214117Sjamie 260214117Sjamie /* Free the jail's original parameter list and any variables. */ 261214117Sjamie while ((p = TAILQ_FIRST(&opp))) 262214117Sjamie free_param(&opp, p); 263214117Sjamie TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) 264214117Sjamie if (p->flags & PF_VAR) 265214117Sjamie free_param(&j->params, p); 266214117Sjamie } 267214117Sjamie while ((wj = TAILQ_FIRST(&wild))) { 268214117Sjamie free(wj->name); 269214117Sjamie while ((p = TAILQ_FIRST(&wj->params))) 270214117Sjamie free_param(&wj->params, p); 271214117Sjamie TAILQ_REMOVE(&wild, wj, tq); 272214117Sjamie } 273214117Sjamie} 274214117Sjamie 275214117Sjamie/* 276214117Sjamie * Create a new jail record. 277214117Sjamie */ 278214117Sjamiestruct cfjail * 279214117Sjamieadd_jail(void) 280214117Sjamie{ 281214117Sjamie struct cfjail *j; 282214117Sjamie 283214117Sjamie j = emalloc(sizeof(struct cfjail)); 284214117Sjamie memset(j, 0, sizeof(struct cfjail)); 285214117Sjamie TAILQ_INIT(&j->params); 286214117Sjamie STAILQ_INIT(&j->dep[DEP_FROM]); 287214117Sjamie STAILQ_INIT(&j->dep[DEP_TO]); 288214117Sjamie j->queue = &cfjails; 289214117Sjamie TAILQ_INSERT_TAIL(&cfjails, j, tq); 290214117Sjamie return j; 291214117Sjamie} 292214117Sjamie 293214117Sjamie/* 294214117Sjamie * Add a parameter to a jail. 295214117Sjamie */ 296214117Sjamievoid 297214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, 298214117Sjamie const char *value) 299214117Sjamie{ 300214117Sjamie struct cfstrings nss; 301214117Sjamie struct cfparam *dp, *np; 302214117Sjamie struct cfstring *s, *ns; 303214117Sjamie struct cfvar *v, *nv; 304214423Sjamie const char *name; 305214423Sjamie char *cs, *tname; 306214117Sjamie unsigned flags; 307214117Sjamie 308214117Sjamie if (j == NULL) { 309214117Sjamie /* Create a single anonymous jail if one doesn't yet exist. */ 310214117Sjamie j = TAILQ_LAST(&cfjails, cfjails); 311214117Sjamie if (j == NULL) 312214117Sjamie j = add_jail(); 313214117Sjamie } 314223188Sjamie TAILQ_INIT(&nss); 315214117Sjamie if (p != NULL) { 316214117Sjamie name = p->name; 317214117Sjamie flags = p->flags; 318214117Sjamie /* 319214117Sjamie * Make a copy of the parameter's string list, 320214117Sjamie * which may be freed if it's overridden later. 321214117Sjamie */ 322223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 323214117Sjamie ns = emalloc(sizeof(struct cfstring)); 324214117Sjamie ns->s = estrdup(s->s); 325214117Sjamie ns->len = s->len; 326214117Sjamie STAILQ_INIT(&ns->vars); 327214117Sjamie STAILQ_FOREACH(v, &s->vars, tq) { 328214117Sjamie nv = emalloc(sizeof(struct cfvar)); 329214117Sjamie nv->name = strdup(v->name); 330214117Sjamie nv->pos = v->pos; 331214117Sjamie STAILQ_INSERT_TAIL(&ns->vars, nv, tq); 332214117Sjamie } 333223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 334214117Sjamie } 335214117Sjamie } else { 336214117Sjamie flags = PF_APPEND; 337234988Sjamie if (ipnum != IP__NULL) { 338214423Sjamie name = intparams[ipnum].name; 339214423Sjamie flags |= intparams[ipnum].flags; 340214423Sjamie } else if ((cs = strchr(value, '='))) { 341214423Sjamie tname = alloca(cs - value + 1); 342214423Sjamie strlcpy(tname, value, cs - value + 1); 343214423Sjamie name = tname; 344214423Sjamie value = cs + 1; 345214423Sjamie } else { 346214423Sjamie name = value; 347214423Sjamie value = NULL; 348214423Sjamie } 349214117Sjamie if (value != NULL) { 350214117Sjamie ns = emalloc(sizeof(struct cfstring)); 351214117Sjamie ns->s = estrdup(value); 352214117Sjamie ns->len = strlen(value); 353214117Sjamie STAILQ_INIT(&ns->vars); 354223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 355214117Sjamie } 356214117Sjamie } 357214117Sjamie 358214117Sjamie /* See if this parameter has already been added. */ 359234988Sjamie if (ipnum != IP__NULL) 360214423Sjamie dp = j->intparams[ipnum]; 361214423Sjamie else 362214423Sjamie TAILQ_FOREACH(dp, &j->params, tq) 363214423Sjamie if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) 364214423Sjamie break; 365214423Sjamie if (dp != NULL) { 366214423Sjamie /* Found it - append or replace. */ 367285279Shrs if (dp->flags & PF_IMMUTABLE) { 368285279Shrs jail_warnx(j, "cannot redefine variable \"%s\".", 369285279Shrs dp->name); 370285279Shrs return; 371285279Shrs } 372214423Sjamie if (strcmp(dp->name, name)) { 373214423Sjamie free(dp->name); 374214423Sjamie dp->name = estrdup(name); 375214117Sjamie } 376223188Sjamie if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss)) 377214423Sjamie free_param_strings(dp); 378223188Sjamie TAILQ_CONCAT(&dp->val, &nss, tq); 379214423Sjamie dp->flags |= flags; 380214423Sjamie } else { 381214117Sjamie /* Not found - add it. */ 382214117Sjamie np = emalloc(sizeof(struct cfparam)); 383214117Sjamie np->name = estrdup(name); 384223188Sjamie TAILQ_INIT(&np->val); 385223188Sjamie TAILQ_CONCAT(&np->val, &nss, tq); 386214117Sjamie np->flags = flags; 387214117Sjamie np->gen = 0; 388214117Sjamie TAILQ_INSERT_TAIL(&j->params, np, tq); 389234988Sjamie if (ipnum != IP__NULL) 390214423Sjamie j->intparams[ipnum] = np; 391214423Sjamie else 392234988Sjamie for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++) 393214423Sjamie if (!(intparams[ipnum].flags & PF_CONV) && 394214423Sjamie equalopts(name, intparams[ipnum].name)) { 395214423Sjamie j->intparams[ipnum] = np; 396214423Sjamie np->flags |= intparams[ipnum].flags; 397214423Sjamie break; 398214423Sjamie } 399214117Sjamie } 400214117Sjamie} 401214117Sjamie 402214117Sjamie/* 403214117Sjamie * Return if a boolean parameter exists and is true. 404214117Sjamie */ 405214117Sjamieint 406214117Sjamiebool_param(const struct cfparam *p) 407214117Sjamie{ 408214117Sjamie const char *cs; 409214117Sjamie 410214117Sjamie if (p == NULL) 411214117Sjamie return 0; 412214117Sjamie cs = strrchr(p->name, '.'); 413214117Sjamie return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ 414223188Sjamie (TAILQ_EMPTY(&p->val) || 415223188Sjamie !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") || 416223188Sjamie (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10))); 417214117Sjamie} 418214117Sjamie 419214117Sjamie/* 420214117Sjamie * Set an integer if a parameter if it exists. 421214117Sjamie */ 422214117Sjamieint 423214117Sjamieint_param(const struct cfparam *p, int *ip) 424214117Sjamie{ 425223188Sjamie if (p == NULL || TAILQ_EMPTY(&p->val)) 426214117Sjamie return 0; 427223188Sjamie *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10); 428214117Sjamie return 1; 429214117Sjamie} 430214117Sjamie 431214117Sjamie/* 432214117Sjamie * Return the string value of a scalar parameter if it exists. 433214117Sjamie */ 434214117Sjamieconst char * 435214117Sjamiestring_param(const struct cfparam *p) 436214117Sjamie{ 437223188Sjamie return (p && !TAILQ_EMPTY(&p->val) 438223188Sjamie ? TAILQ_LAST(&p->val, cfstrings)->s : NULL); 439214117Sjamie} 440214117Sjamie 441214117Sjamie/* 442214649Sjamie * Check syntax and values of internal parameters. Set some internal 443214649Sjamie * parameters based on the values of others. 444214117Sjamie */ 445214117Sjamieint 446214649Sjamiecheck_intparams(struct cfjail *j) 447214117Sjamie{ 448214649Sjamie struct cfparam *p; 449223327Sjamie struct cfstring *s; 450214783Sjamie FILE *f; 451223351Sjamie const char *val; 452214783Sjamie char *cs, *ep, *ln; 453223351Sjamie size_t lnlen; 454223351Sjamie int error; 455223351Sjamie#if defined(INET) || defined(INET6) 456223351Sjamie struct addrinfo hints; 457223351Sjamie struct addrinfo *ai0, *ai; 458223351Sjamie const char *hostname; 459294196Sjamie int gicode, defif; 460223351Sjamie#endif 461223351Sjamie#ifdef INET 462223351Sjamie struct in_addr addr4; 463223351Sjamie int ip4ok; 464214117Sjamie char avalue4[INET_ADDRSTRLEN]; 465223351Sjamie#endif 466214117Sjamie#ifdef INET6 467214117Sjamie struct in6_addr addr6; 468223351Sjamie int ip6ok; 469214117Sjamie char avalue6[INET6_ADDRSTRLEN]; 470214117Sjamie#endif 471214117Sjamie 472214117Sjamie error = 0; 473214649Sjamie /* Check format of boolan and integer values. */ 474214649Sjamie TAILQ_FOREACH(p, &j->params, tq) { 475223188Sjamie if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) { 476223188Sjamie val = TAILQ_LAST(&p->val, cfstrings)->s; 477214649Sjamie if (p->flags & PF_BOOL) { 478214649Sjamie if (strcasecmp(val, "false") && 479214649Sjamie strcasecmp(val, "true") && 480214649Sjamie ((void)strtol(val, &ep, 10), *ep)) { 481214649Sjamie jail_warnx(j, 482214649Sjamie "%s: unknown boolean value \"%s\"", 483214649Sjamie p->name, val); 484214649Sjamie error = -1; 485214649Sjamie } 486214649Sjamie } else { 487214649Sjamie (void)strtol(val, &ep, 10); 488214649Sjamie if (ep == val || *ep) { 489214649Sjamie jail_warnx(j, 490214649Sjamie "%s: non-integer value \"%s\"", 491214649Sjamie p->name, val); 492214649Sjamie error = -1; 493214649Sjamie } 494214649Sjamie } 495214649Sjamie } 496214649Sjamie } 497214649Sjamie 498223351Sjamie#if defined(INET) || defined(INET6) 499214117Sjamie /* 500214117Sjamie * The ip_hostname parameter looks up the hostname, and adds parameters 501214117Sjamie * for any IP addresses it finds. 502214117Sjamie */ 503214649Sjamie if (((j->flags & JF_OP_MASK) != JF_STOP || 504214649Sjamie j->intparams[IP_INTERFACE] != NULL) && 505214649Sjamie bool_param(j->intparams[IP_IP_HOSTNAME]) && 506214423Sjamie (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { 507214117Sjamie j->intparams[IP_IP_HOSTNAME] = NULL; 508214117Sjamie /* 509214117Sjamie * Silently ignore unsupported address families from 510214117Sjamie * DNS lookups. 511214117Sjamie */ 512223351Sjamie#ifdef INET 513223351Sjamie ip4ok = feature_present("inet"); 514214117Sjamie#endif 515214117Sjamie#ifdef INET6 516223351Sjamie ip6ok = feature_present("inet6"); 517214117Sjamie#endif 518223351Sjamie if ( 519223351Sjamie#if defined(INET) && defined(INET6) 520223351Sjamie ip4ok || ip6ok 521223351Sjamie#elif defined(INET) 522223351Sjamie ip4ok 523223351Sjamie#elif defined(INET6) 524223351Sjamie ip6ok 525223351Sjamie#endif 526223351Sjamie ) { 527214117Sjamie /* Look up the hostname (or get the address) */ 528214117Sjamie memset(&hints, 0, sizeof(hints)); 529214117Sjamie hints.ai_socktype = SOCK_STREAM; 530214117Sjamie hints.ai_family = 531223351Sjamie#if defined(INET) && defined(INET6) 532223351Sjamie ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6; 533223351Sjamie#elif defined(INET) 534223351Sjamie PF_INET; 535223351Sjamie#elif defined(INET6) 536223351Sjamie PF_INET6; 537214117Sjamie#endif 538214649Sjamie gicode = getaddrinfo(hostname, NULL, &hints, &ai0); 539214649Sjamie if (gicode != 0) { 540214117Sjamie jail_warnx(j, "host.hostname %s: %s", hostname, 541214649Sjamie gai_strerror(gicode)); 542214117Sjamie error = -1; 543214117Sjamie } else { 544214117Sjamie /* 545214117Sjamie * Convert the addresses to ASCII so jailparam 546214117Sjamie * can convert them back. Errors are not 547214117Sjamie * expected here. 548214117Sjamie */ 549214117Sjamie for (ai = ai0; ai; ai = ai->ai_next) 550214117Sjamie switch (ai->ai_family) { 551223351Sjamie#ifdef INET 552214117Sjamie case AF_INET: 553214117Sjamie memcpy(&addr4, 554214117Sjamie &((struct sockaddr_in *) 555214117Sjamie (void *)ai->ai_addr)-> 556214117Sjamie sin_addr, sizeof(addr4)); 557214117Sjamie if (inet_ntop(AF_INET, 558214117Sjamie &addr4, avalue4, 559214117Sjamie INET_ADDRSTRLEN) == NULL) 560214117Sjamie err(1, "inet_ntop"); 561214423Sjamie add_param(j, NULL, KP_IP4_ADDR, 562214117Sjamie avalue4); 563214117Sjamie break; 564223351Sjamie#endif 565214117Sjamie#ifdef INET6 566214117Sjamie case AF_INET6: 567214117Sjamie memcpy(&addr6, 568214117Sjamie &((struct sockaddr_in6 *) 569214117Sjamie (void *)ai->ai_addr)-> 570214117Sjamie sin6_addr, sizeof(addr6)); 571214117Sjamie if (inet_ntop(AF_INET6, 572214117Sjamie &addr6, avalue6, 573214117Sjamie INET6_ADDRSTRLEN) == NULL) 574214117Sjamie err(1, "inet_ntop"); 575214423Sjamie add_param(j, NULL, KP_IP6_ADDR, 576214117Sjamie avalue6); 577214117Sjamie break; 578214117Sjamie#endif 579214117Sjamie } 580214117Sjamie freeaddrinfo(ai0); 581214117Sjamie } 582214117Sjamie } 583214117Sjamie } 584214649Sjamie 585214117Sjamie /* 586214117Sjamie * IP addresses may include an interface to set that address on, 587269522Ssmh * a netmask/suffix for that address and options for ifconfig. 588269522Ssmh * These are copied to an internal command parameter and then stripped 589269522Ssmh * so they won't be passed on to jailparam_set. 590214117Sjamie */ 591214117Sjamie defif = string_param(j->intparams[IP_INTERFACE]) != NULL; 592223351Sjamie#ifdef INET 593223351Sjamie if (j->intparams[KP_IP4_ADDR] != NULL) { 594223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) { 595214117Sjamie cs = strchr(s->s, '|'); 596214423Sjamie if (cs || defif) 597223351Sjamie add_param(j, NULL, IP__IP4_IFADDR, s->s); 598214423Sjamie if (cs) { 599214423Sjamie strcpy(s->s, cs + 1); 600214423Sjamie s->len -= cs + 1 - s->s; 601214117Sjamie } 602294196Sjamie if ((cs = strchr(s->s, '/')) != NULL) { 603239621Sjamie *cs = '\0'; 604239601Sjamie s->len = cs - s->s; 605223351Sjamie } 606269522Ssmh if ((cs = strchr(s->s, ' ')) != NULL) { 607269522Ssmh *cs = '\0'; 608269522Ssmh s->len = cs - s->s; 609269522Ssmh } 610223351Sjamie } 611223351Sjamie } 612214433Sjamie#endif 613214433Sjamie#ifdef INET6 614223351Sjamie if (j->intparams[KP_IP6_ADDR] != NULL) { 615223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) { 616223351Sjamie cs = strchr(s->s, '|'); 617223351Sjamie if (cs || defif) 618223351Sjamie add_param(j, NULL, IP__IP6_IFADDR, s->s); 619223351Sjamie if (cs) { 620223351Sjamie strcpy(s->s, cs + 1); 621223351Sjamie s->len -= cs + 1 - s->s; 622223351Sjamie } 623294196Sjamie if ((cs = strchr(s->s, '/')) != NULL) { 624239621Sjamie *cs = '\0'; 625239601Sjamie s->len = cs - s->s; 626214117Sjamie } 627269522Ssmh if ((cs = strchr(s->s, ' ')) != NULL) { 628269522Ssmh *cs = '\0'; 629269522Ssmh s->len = cs - s->s; 630269522Ssmh } 631214117Sjamie } 632214117Sjamie } 633214117Sjamie#endif 634223351Sjamie#endif 635214783Sjamie 636214783Sjamie /* 637214783Sjamie * Read mount.fstab file(s), and treat each line as its own mount 638214783Sjamie * parameter. 639214783Sjamie */ 640214783Sjamie if (j->intparams[IP_MOUNT_FSTAB] != NULL) { 641223188Sjamie TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) { 642214783Sjamie if (s->len == 0) 643214783Sjamie continue; 644214783Sjamie f = fopen(s->s, "r"); 645214783Sjamie if (f == NULL) { 646214783Sjamie jail_warnx(j, "mount.fstab: %s: %s", 647214783Sjamie s->s, strerror(errno)); 648214783Sjamie error = -1; 649214783Sjamie continue; 650214783Sjamie } 651214783Sjamie while ((ln = fgetln(f, &lnlen))) { 652214783Sjamie if ((cs = memchr(ln, '#', lnlen - 1))) 653214783Sjamie lnlen = cs - ln + 1; 654214783Sjamie if (ln[lnlen - 1] == '\n' || 655214783Sjamie ln[lnlen - 1] == '#') 656214783Sjamie ln[lnlen - 1] = '\0'; 657214783Sjamie else { 658214783Sjamie cs = alloca(lnlen + 1); 659214783Sjamie strlcpy(cs, ln, lnlen + 1); 660214783Sjamie ln = cs; 661214783Sjamie } 662214783Sjamie add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln); 663214783Sjamie } 664214783Sjamie fclose(f); 665214783Sjamie } 666214783Sjamie } 667214783Sjamie if (error) 668214783Sjamie failed(j); 669214117Sjamie return error; 670214117Sjamie} 671214117Sjamie 672214117Sjamie/* 673214117Sjamie * Import parameters into libjail's binary jailparam format. 674214117Sjamie */ 675214117Sjamieint 676214117Sjamieimport_params(struct cfjail *j) 677214117Sjamie{ 678214117Sjamie struct cfparam *p; 679214117Sjamie struct cfstring *s, *ts; 680214117Sjamie struct jailparam *jp; 681214117Sjamie char *value, *cs; 682214117Sjamie size_t vallen; 683214117Sjamie int error; 684214117Sjamie 685214117Sjamie error = 0; 686214117Sjamie j->njp = 0; 687214117Sjamie TAILQ_FOREACH(p, &j->params, tq) 688214117Sjamie if (!(p->flags & PF_INTERNAL)) 689214117Sjamie j->njp++; 690214117Sjamie j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); 691214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 692214117Sjamie if (p->flags & PF_INTERNAL) 693214117Sjamie continue; 694214117Sjamie if (jailparam_init(jp, p->name) < 0) { 695214117Sjamie error = -1; 696214117Sjamie jail_warnx(j, "%s", jail_errmsg); 697241196Sjamie jp++; 698214117Sjamie continue; 699214117Sjamie } 700223188Sjamie if (TAILQ_EMPTY(&p->val)) 701214117Sjamie value = NULL; 702214117Sjamie else if (!jp->jp_elemlen || 703223188Sjamie !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) { 704214117Sjamie /* 705214117Sjamie * Scalar parameters silently discard multiple (array) 706214117Sjamie * values, keeping only the last value added. This 707214117Sjamie * lets values added from the command line append to 708214117Sjamie * arrays wthout pre-checking the type. 709214117Sjamie */ 710223188Sjamie value = TAILQ_LAST(&p->val, cfstrings)->s; 711214117Sjamie } else { 712214117Sjamie /* 713214117Sjamie * Convert arrays into comma-separated strings, which 714214117Sjamie * jailparam_import will then convert back into arrays. 715214117Sjamie */ 716214117Sjamie vallen = 0; 717223188Sjamie TAILQ_FOREACH(s, &p->val, tq) 718214117Sjamie vallen += s->len + 1; 719214117Sjamie value = alloca(vallen); 720214117Sjamie cs = value; 721223188Sjamie TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 722239601Sjamie memcpy(cs, s->s, s->len); 723239621Sjamie cs += s->len + 1; 724239621Sjamie cs[-1] = ','; 725214117Sjamie } 726239621Sjamie value[vallen - 1] = '\0'; 727214117Sjamie } 728214117Sjamie if (jailparam_import(jp, value) < 0) { 729214117Sjamie error = -1; 730214117Sjamie jail_warnx(j, "%s", jail_errmsg); 731214117Sjamie } 732214117Sjamie jp++; 733214117Sjamie } 734214117Sjamie if (error) { 735214117Sjamie jailparam_free(j->jp, j->njp); 736214117Sjamie free(j->jp); 737214117Sjamie j->jp = NULL; 738214117Sjamie failed(j); 739214117Sjamie } 740214117Sjamie return error; 741214117Sjamie} 742214117Sjamie 743214117Sjamie/* 744214117Sjamie * Check if options are equal (with or without the "no" prefix). 745214117Sjamie */ 746214117Sjamieint 747214117Sjamieequalopts(const char *opt1, const char *opt2) 748214117Sjamie{ 749214117Sjamie char *p; 750214117Sjamie 751214117Sjamie /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 752214117Sjamie if (strcmp(opt1, opt2) == 0) 753214117Sjamie return (1); 754214117Sjamie /* "noopt" vs. "opt" */ 755214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 756214117Sjamie return (1); 757214117Sjamie /* "opt" vs. "noopt" */ 758214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 759214117Sjamie return (1); 760214117Sjamie while ((p = strchr(opt1, '.')) != NULL && 761214117Sjamie !strncmp(opt1, opt2, ++p - opt1)) { 762214117Sjamie opt2 += p - opt1; 763214117Sjamie opt1 = p; 764214117Sjamie /* "foo.noopt" vs. "foo.opt" */ 765214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 766214117Sjamie return (1); 767214117Sjamie /* "foo.opt" vs. "foo.noopt" */ 768214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 769214117Sjamie return (1); 770214117Sjamie } 771214117Sjamie return (0); 772214117Sjamie} 773214117Sjamie 774214117Sjamie/* 775214117Sjamie * See if a jail name matches a wildcard. 776214117Sjamie */ 777214117Sjamieint 778214117Sjamiewild_jail_match(const char *jname, const char *wname) 779214117Sjamie{ 780214117Sjamie const char *jc, *jd, *wc, *wd; 781214117Sjamie 782214117Sjamie /* 783214117Sjamie * A non-final "*" component in the wild name matches a single jail 784214117Sjamie * component, and a final "*" matches one or more jail components. 785214117Sjamie */ 786214117Sjamie for (jc = jname, wc = wname; 787214117Sjamie (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); 788214117Sjamie jc = jd + 1, wc = wd + 1) 789214117Sjamie if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) 790214117Sjamie return 0; 791214117Sjamie return (!strcmp(jc, wc) || !strcmp(wc, "*")); 792214117Sjamie} 793214117Sjamie 794214117Sjamie/* 795214117Sjamie * Return if a jail name is a wildcard. 796214117Sjamie */ 797214117Sjamieint 798214117Sjamiewild_jail_name(const char *wname) 799214117Sjamie{ 800214117Sjamie const char *wc; 801214117Sjamie 802214117Sjamie for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) 803214117Sjamie if ((wc == wname || wc[-1] == '.') && 804214117Sjamie (wc[1] == '\0' || wc[1] == '.')) 805214117Sjamie return 1; 806214117Sjamie return 0; 807214117Sjamie} 808214117Sjamie 809214117Sjamie/* 810214117Sjamie * Free a parameter record and all its strings and variables. 811214117Sjamie */ 812214117Sjamiestatic void 813214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p) 814214117Sjamie{ 815214117Sjamie free(p->name); 816214117Sjamie free_param_strings(p); 817214117Sjamie TAILQ_REMOVE(pp, p, tq); 818214117Sjamie free(p); 819214117Sjamie} 820214117Sjamie 821214117Sjamiestatic void 822214117Sjamiefree_param_strings(struct cfparam *p) 823214117Sjamie{ 824214117Sjamie struct cfstring *s; 825214117Sjamie struct cfvar *v; 826214117Sjamie 827223188Sjamie while ((s = TAILQ_FIRST(&p->val))) { 828214117Sjamie free(s->s); 829214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 830214117Sjamie free(v->name); 831214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 832214117Sjamie free(v); 833214117Sjamie } 834223188Sjamie TAILQ_REMOVE(&p->val, s, tq); 835214117Sjamie free(s); 836214117Sjamie } 837214117Sjamie} 838