1214117Sjamie/*- 2223190Sjamie * Copyright (c) 2011 James Gritton 3214117Sjamie * All rights reserved. 4214117Sjamie * 5214117Sjamie * Redistribution and use in source and binary forms, with or without 6214117Sjamie * modification, are permitted provided that the following conditions 7214117Sjamie * are met: 8214117Sjamie * 1. Redistributions of source code must retain the above copyright 9214117Sjamie * notice, this list of conditions and the following disclaimer. 10214117Sjamie * 2. Redistributions in binary form must reproduce the above copyright 11214117Sjamie * notice, this list of conditions and the following disclaimer in the 12214117Sjamie * documentation and/or other materials provided with the distribution. 13214117Sjamie * 14214117Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15214117Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16214117Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17214117Sjamie * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18214117Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19214117Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20214117Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21214117Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214117Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23214117Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24214117Sjamie * SUCH DAMAGE. 25214117Sjamie */ 26214117Sjamie 27214117Sjamie#include <sys/cdefs.h> 28214117Sjamie__FBSDID("$FreeBSD: releng/10.2/usr.sbin/jail/config.c 285827 2015-07-23 20:01:56Z hrs $"); 29214117Sjamie 30214117Sjamie#include <sys/types.h> 31214783Sjamie#include <sys/errno.h> 32214117Sjamie#include <sys/socket.h> 33214117Sjamie#include <sys/sysctl.h> 34214117Sjamie 35214117Sjamie#include <arpa/inet.h> 36214117Sjamie#include <netinet/in.h> 37214117Sjamie 38214117Sjamie#include <err.h> 39214117Sjamie#include <netdb.h> 40214117Sjamie#include <stdio.h> 41214117Sjamie#include <stdlib.h> 42214117Sjamie#include <string.h> 43223351Sjamie#include <unistd.h> 44214117Sjamie 45214117Sjamie#include "jailp.h" 46214117Sjamie 47214117Sjamiestruct ipspec { 48214117Sjamie const char *name; 49214117Sjamie unsigned flags; 50214117Sjamie}; 51214117Sjamie 52214117Sjamieextern FILE *yyin; 53214117Sjamieextern int yynerrs; 54214117Sjamie 55235789Sbaptextern int yyparse(void); 56235789Sbapt 57214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails); 58214117Sjamie 59214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p); 60214117Sjamiestatic void free_param_strings(struct cfparam *p); 61214117Sjamie 62214117Sjamiestatic const struct ipspec intparams[] = { 63214423Sjamie [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL}, 64214423Sjamie [IP_COMMAND] = {"command", PF_INTERNAL}, 65214423Sjamie [IP_DEPEND] = {"depend", PF_INTERNAL}, 66214423Sjamie [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL}, 67214423Sjamie [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL}, 68214423Sjamie [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT}, 69214423Sjamie [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL}, 70214423Sjamie [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL}, 71214423Sjamie [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL}, 72214423Sjamie [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL}, 73214423Sjamie [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL}, 74214423Sjamie [IP_EXEC_START] = {"exec.start", PF_INTERNAL}, 75214423Sjamie [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL}, 76214423Sjamie [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user", 77214423Sjamie PF_INTERNAL | PF_BOOL}, 78214423Sjamie [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL}, 79214423Sjamie [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT}, 80223351Sjamie#if defined(INET) || defined(INET6) 81214423Sjamie [IP_INTERFACE] = {"interface", PF_INTERNAL}, 82214423Sjamie [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL}, 83223351Sjamie#endif 84248854Sjamie [IP_MOUNT] = {"mount", PF_INTERNAL | PF_REV}, 85214423Sjamie [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, 86256387Shrs [IP_MOUNT_FDESCFS] = {"mount.fdescfs", PF_INTERNAL | PF_BOOL}, 87278484Sjamie [IP_MOUNT_PROCFS] = {"mount.procfs", PF_INTERNAL | PF_BOOL}, 88214423Sjamie [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, 89214423Sjamie [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, 90214423Sjamie [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, 91223351Sjamie#ifdef INET 92248854Sjamie [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV | PF_REV}, 93223351Sjamie#endif 94214117Sjamie#ifdef INET6 95248854Sjamie [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV | PF_REV}, 96214117Sjamie#endif 97248854Sjamie [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV | PF_REV}, 98223189Sjamie [IP__OP] = {NULL, PF_CONV}, 99214423Sjamie [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, 100214423Sjamie [KP_ALLOW_MOUNT] = {"allow.mount", 0}, 101214423Sjamie [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, 102214423Sjamie [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, 103214423Sjamie [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, 104214423Sjamie [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, 105232242Sjamie [KP_DEVFS_RULESET] = {"devfs_ruleset", 0}, 106214423Sjamie [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, 107214423Sjamie [KP_HOST_HOSTNAME] = {"host.hostname", 0}, 108223351Sjamie#ifdef INET 109214423Sjamie [KP_IP4_ADDR] = {"ip4.addr", 0}, 110223351Sjamie#endif 111214423Sjamie#ifdef INET6 112214423Sjamie [KP_IP6_ADDR] = {"ip6.addr", 0}, 113214423Sjamie#endif 114285827Shrs [KP_JID] = {"jid", PF_IMMUTABLE}, 115285827Shrs [KP_NAME] = {"name", PF_IMMUTABLE}, 116214423Sjamie [KP_PATH] = {"path", 0}, 117214423Sjamie [KP_PERSIST] = {"persist", 0}, 118214423Sjamie [KP_SECURELEVEL] = {"securelevel", 0}, 119214423Sjamie [KP_VNET] = {"vnet", 0}, 120214117Sjamie}; 121214117Sjamie 122214117Sjamie/* 123214117Sjamie * Parse the jail configuration file. 124214117Sjamie */ 125214117Sjamievoid 126214117Sjamieload_config(void) 127214117Sjamie{ 128214117Sjamie struct cfjails wild; 129214117Sjamie struct cfparams opp; 130214117Sjamie struct cfjail *j, *tj, *wj; 131214117Sjamie struct cfparam *p, *vp, *tp; 132214117Sjamie struct cfstring *s, *vs, *ns; 133285827Shrs struct cfvar *v, *vv; 134214117Sjamie char *ep; 135214117Sjamie int did_self, jseq, pgen; 136214117Sjamie 137214117Sjamie if (!strcmp(cfname, "-")) { 138214117Sjamie cfname = "STDIN"; 139214117Sjamie yyin = stdin; 140214117Sjamie } else { 141214117Sjamie yyin = fopen(cfname, "r"); 142214117Sjamie if (!yyin) 143214117Sjamie err(1, "%s", cfname); 144214117Sjamie } 145214117Sjamie if (yyparse() || yynerrs) 146214117Sjamie exit(1); 147214117Sjamie 148214117Sjamie /* Separate the wildcard jails out from the actual jails. */ 149214117Sjamie jseq = 0; 150214117Sjamie TAILQ_INIT(&wild); 151214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 152214117Sjamie j->seq = ++jseq; 153214117Sjamie if (wild_jail_name(j->name)) 154214117Sjamie requeue(j, &wild); 155214117Sjamie } 156214117Sjamie 157214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) { 158214117Sjamie /* Set aside the jail's parameters. */ 159214117Sjamie TAILQ_INIT(&opp); 160214117Sjamie TAILQ_CONCAT(&opp, &j->params, tq); 161214117Sjamie /* 162214117Sjamie * The jail name implies its "name" or "jid" parameter, 163214117Sjamie * though they may also be explicitly set later on. 164214117Sjamie */ 165214117Sjamie add_param(j, NULL, 166214423Sjamie strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, 167214117Sjamie j->name); 168214117Sjamie /* 169214117Sjamie * Collect parameters for the jail, global parameters/variables, 170214117Sjamie * and any matching wildcard jails. 171214117Sjamie */ 172214117Sjamie did_self = 0; 173214117Sjamie TAILQ_FOREACH(wj, &wild, tq) { 174214117Sjamie if (j->seq < wj->seq && !did_self) { 175214117Sjamie TAILQ_FOREACH(p, &opp, tq) 176214423Sjamie add_param(j, p, 0, NULL); 177214117Sjamie did_self = 1; 178214117Sjamie } 179214117Sjamie if (wild_jail_match(j->name, wj->name)) 180214117Sjamie TAILQ_FOREACH(p, &wj->params, tq) 181214423Sjamie add_param(j, p, 0, NULL); 182214117Sjamie } 183214117Sjamie if (!did_self) 184214117Sjamie TAILQ_FOREACH(p, &opp, tq) 185214423Sjamie add_param(j, p, 0, NULL); 186214117Sjamie 187214117Sjamie /* Resolve any variable substitutions. */ 188214117Sjamie pgen = 0; 189214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 190214117Sjamie p->gen = ++pgen; 191214117Sjamie find_vars: 192223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 193214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 194214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 195214117Sjamie if (!strcmp(vp->name, v->name)) 196214117Sjamie break; 197214117Sjamie if (!vp) { 198214117Sjamie jail_warnx(j, 199214117Sjamie "%s: variable \"%s\" not found", 200214117Sjamie p->name, v->name); 201214117Sjamie bad_var: 202214117Sjamie j->flags |= JF_FAILED; 203214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 204214117Sjamie if (vp->gen == pgen) 205214117Sjamie vp->flags |= PF_BAD; 206214117Sjamie goto free_var; 207214117Sjamie } 208214117Sjamie if (vp->flags & PF_BAD) 209214117Sjamie goto bad_var; 210214117Sjamie if (vp->gen == pgen) { 211214117Sjamie jail_warnx(j, "%s: variable loop", 212214117Sjamie v->name); 213214117Sjamie goto bad_var; 214214117Sjamie } 215223188Sjamie TAILQ_FOREACH(vs, &vp->val, tq) 216214117Sjamie if (!STAILQ_EMPTY(&vs->vars)) { 217214117Sjamie vp->gen = pgen; 218214117Sjamie TAILQ_REMOVE(&j->params, vp, 219214117Sjamie tq); 220214117Sjamie TAILQ_INSERT_BEFORE(p, vp, tq); 221214117Sjamie p = vp; 222214117Sjamie goto find_vars; 223214117Sjamie } 224223188Sjamie vs = TAILQ_FIRST(&vp->val); 225223188Sjamie if (TAILQ_NEXT(vs, tq) != NULL && 226214117Sjamie (s->s[0] != '\0' || 227214117Sjamie STAILQ_NEXT(v, tq))) { 228214117Sjamie jail_warnx(j, "%s: array cannot be " 229214117Sjamie "substituted inline", 230214117Sjamie p->name); 231214117Sjamie goto bad_var; 232214117Sjamie } 233214117Sjamie s->s = erealloc(s->s, s->len + vs->len + 1); 234285827Shrs memmove(s->s + v->pos + vs->len, 235285827Shrs s->s + v->pos, 236285827Shrs s->len - v->pos + 1); 237285827Shrs memcpy(s->s + v->pos, vs->s, vs->len); 238285827Shrs vv = v; 239285827Shrs while ((vv = STAILQ_NEXT(vv, tq))) 240285827Shrs vv->pos += vs->len; 241214117Sjamie s->len += vs->len; 242223188Sjamie while ((vs = TAILQ_NEXT(vs, tq))) { 243214117Sjamie ns = emalloc(sizeof(struct cfstring)); 244214117Sjamie ns->s = estrdup(vs->s); 245214117Sjamie ns->len = vs->len; 246214117Sjamie STAILQ_INIT(&ns->vars); 247223188Sjamie TAILQ_INSERT_AFTER(&p->val, s, ns, tq); 248214117Sjamie s = ns; 249214117Sjamie } 250214117Sjamie free_var: 251214117Sjamie free(v->name); 252214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 253214117Sjamie free(v); 254214117Sjamie } 255214117Sjamie } 256214117Sjamie } 257214117Sjamie 258214117Sjamie /* Free the jail's original parameter list and any variables. */ 259214117Sjamie while ((p = TAILQ_FIRST(&opp))) 260214117Sjamie free_param(&opp, p); 261214117Sjamie TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) 262214117Sjamie if (p->flags & PF_VAR) 263214117Sjamie free_param(&j->params, p); 264214117Sjamie } 265214117Sjamie while ((wj = TAILQ_FIRST(&wild))) { 266214117Sjamie free(wj->name); 267214117Sjamie while ((p = TAILQ_FIRST(&wj->params))) 268214117Sjamie free_param(&wj->params, p); 269214117Sjamie TAILQ_REMOVE(&wild, wj, tq); 270214117Sjamie } 271214117Sjamie} 272214117Sjamie 273214117Sjamie/* 274214117Sjamie * Create a new jail record. 275214117Sjamie */ 276214117Sjamiestruct cfjail * 277214117Sjamieadd_jail(void) 278214117Sjamie{ 279214117Sjamie struct cfjail *j; 280214117Sjamie 281214117Sjamie j = emalloc(sizeof(struct cfjail)); 282214117Sjamie memset(j, 0, sizeof(struct cfjail)); 283214117Sjamie TAILQ_INIT(&j->params); 284214117Sjamie STAILQ_INIT(&j->dep[DEP_FROM]); 285214117Sjamie STAILQ_INIT(&j->dep[DEP_TO]); 286214117Sjamie j->queue = &cfjails; 287214117Sjamie TAILQ_INSERT_TAIL(&cfjails, j, tq); 288214117Sjamie return j; 289214117Sjamie} 290214117Sjamie 291214117Sjamie/* 292214117Sjamie * Add a parameter to a jail. 293214117Sjamie */ 294214117Sjamievoid 295214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, 296214117Sjamie const char *value) 297214117Sjamie{ 298214117Sjamie struct cfstrings nss; 299214117Sjamie struct cfparam *dp, *np; 300214117Sjamie struct cfstring *s, *ns; 301214117Sjamie struct cfvar *v, *nv; 302214423Sjamie const char *name; 303214423Sjamie char *cs, *tname; 304214117Sjamie unsigned flags; 305214117Sjamie 306214117Sjamie if (j == NULL) { 307214117Sjamie /* Create a single anonymous jail if one doesn't yet exist. */ 308214117Sjamie j = TAILQ_LAST(&cfjails, cfjails); 309214117Sjamie if (j == NULL) 310214117Sjamie j = add_jail(); 311214117Sjamie } 312223188Sjamie TAILQ_INIT(&nss); 313214117Sjamie if (p != NULL) { 314214117Sjamie name = p->name; 315214117Sjamie flags = p->flags; 316214117Sjamie /* 317214117Sjamie * Make a copy of the parameter's string list, 318214117Sjamie * which may be freed if it's overridden later. 319214117Sjamie */ 320223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 321214117Sjamie ns = emalloc(sizeof(struct cfstring)); 322214117Sjamie ns->s = estrdup(s->s); 323214117Sjamie ns->len = s->len; 324214117Sjamie STAILQ_INIT(&ns->vars); 325214117Sjamie STAILQ_FOREACH(v, &s->vars, tq) { 326214117Sjamie nv = emalloc(sizeof(struct cfvar)); 327214117Sjamie nv->name = strdup(v->name); 328214117Sjamie nv->pos = v->pos; 329214117Sjamie STAILQ_INSERT_TAIL(&ns->vars, nv, tq); 330214117Sjamie } 331223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 332214117Sjamie } 333214117Sjamie } else { 334214117Sjamie flags = PF_APPEND; 335234988Sjamie if (ipnum != IP__NULL) { 336214423Sjamie name = intparams[ipnum].name; 337214423Sjamie flags |= intparams[ipnum].flags; 338214423Sjamie } else if ((cs = strchr(value, '='))) { 339214423Sjamie tname = alloca(cs - value + 1); 340214423Sjamie strlcpy(tname, value, cs - value + 1); 341214423Sjamie name = tname; 342214423Sjamie value = cs + 1; 343214423Sjamie } else { 344214423Sjamie name = value; 345214423Sjamie value = NULL; 346214423Sjamie } 347214117Sjamie if (value != NULL) { 348214117Sjamie ns = emalloc(sizeof(struct cfstring)); 349214117Sjamie ns->s = estrdup(value); 350214117Sjamie ns->len = strlen(value); 351214117Sjamie STAILQ_INIT(&ns->vars); 352223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 353214117Sjamie } 354214117Sjamie } 355214117Sjamie 356214117Sjamie /* See if this parameter has already been added. */ 357234988Sjamie if (ipnum != IP__NULL) 358214423Sjamie dp = j->intparams[ipnum]; 359214423Sjamie else 360214423Sjamie TAILQ_FOREACH(dp, &j->params, tq) 361214423Sjamie if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) 362214423Sjamie break; 363214423Sjamie if (dp != NULL) { 364214423Sjamie /* Found it - append or replace. */ 365285827Shrs if (dp->flags & PF_IMMUTABLE) { 366285827Shrs jail_warnx(j, "cannot redefine variable \"%s\".", 367285827Shrs dp->name); 368285827Shrs return; 369285827Shrs } 370214423Sjamie if (strcmp(dp->name, name)) { 371214423Sjamie free(dp->name); 372214423Sjamie dp->name = estrdup(name); 373214117Sjamie } 374223188Sjamie if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss)) 375214423Sjamie free_param_strings(dp); 376223188Sjamie TAILQ_CONCAT(&dp->val, &nss, tq); 377214423Sjamie dp->flags |= flags; 378214423Sjamie } else { 379214117Sjamie /* Not found - add it. */ 380214117Sjamie np = emalloc(sizeof(struct cfparam)); 381214117Sjamie np->name = estrdup(name); 382223188Sjamie TAILQ_INIT(&np->val); 383223188Sjamie TAILQ_CONCAT(&np->val, &nss, tq); 384214117Sjamie np->flags = flags; 385214117Sjamie np->gen = 0; 386214117Sjamie TAILQ_INSERT_TAIL(&j->params, np, tq); 387234988Sjamie if (ipnum != IP__NULL) 388214423Sjamie j->intparams[ipnum] = np; 389214423Sjamie else 390234988Sjamie for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++) 391214423Sjamie if (!(intparams[ipnum].flags & PF_CONV) && 392214423Sjamie equalopts(name, intparams[ipnum].name)) { 393214423Sjamie j->intparams[ipnum] = np; 394214423Sjamie np->flags |= intparams[ipnum].flags; 395214423Sjamie break; 396214423Sjamie } 397214117Sjamie } 398214117Sjamie} 399214117Sjamie 400214117Sjamie/* 401214117Sjamie * Return if a boolean parameter exists and is true. 402214117Sjamie */ 403214117Sjamieint 404214117Sjamiebool_param(const struct cfparam *p) 405214117Sjamie{ 406214117Sjamie const char *cs; 407214117Sjamie 408214117Sjamie if (p == NULL) 409214117Sjamie return 0; 410214117Sjamie cs = strrchr(p->name, '.'); 411214117Sjamie return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ 412223188Sjamie (TAILQ_EMPTY(&p->val) || 413223188Sjamie !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") || 414223188Sjamie (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10))); 415214117Sjamie} 416214117Sjamie 417214117Sjamie/* 418214117Sjamie * Set an integer if a parameter if it exists. 419214117Sjamie */ 420214117Sjamieint 421214117Sjamieint_param(const struct cfparam *p, int *ip) 422214117Sjamie{ 423223188Sjamie if (p == NULL || TAILQ_EMPTY(&p->val)) 424214117Sjamie return 0; 425223188Sjamie *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10); 426214117Sjamie return 1; 427214117Sjamie} 428214117Sjamie 429214117Sjamie/* 430214117Sjamie * Return the string value of a scalar parameter if it exists. 431214117Sjamie */ 432214117Sjamieconst char * 433214117Sjamiestring_param(const struct cfparam *p) 434214117Sjamie{ 435223188Sjamie return (p && !TAILQ_EMPTY(&p->val) 436223188Sjamie ? TAILQ_LAST(&p->val, cfstrings)->s : NULL); 437214117Sjamie} 438214117Sjamie 439214117Sjamie/* 440214649Sjamie * Check syntax and values of internal parameters. Set some internal 441214649Sjamie * parameters based on the values of others. 442214117Sjamie */ 443214117Sjamieint 444214649Sjamiecheck_intparams(struct cfjail *j) 445214117Sjamie{ 446214649Sjamie struct cfparam *p; 447223327Sjamie struct cfstring *s; 448214783Sjamie FILE *f; 449223351Sjamie const char *val; 450214783Sjamie char *cs, *ep, *ln; 451223351Sjamie size_t lnlen; 452223351Sjamie int error; 453223351Sjamie#if defined(INET) || defined(INET6) 454223351Sjamie struct addrinfo hints; 455223351Sjamie struct addrinfo *ai0, *ai; 456223351Sjamie const char *hostname; 457223351Sjamie int gicode, defif, prefix; 458223351Sjamie#endif 459223351Sjamie#ifdef INET 460223351Sjamie struct in_addr addr4; 461223351Sjamie int ip4ok; 462214117Sjamie char avalue4[INET_ADDRSTRLEN]; 463223351Sjamie#endif 464214117Sjamie#ifdef INET6 465214117Sjamie struct in6_addr addr6; 466223351Sjamie int ip6ok; 467214117Sjamie char avalue6[INET6_ADDRSTRLEN]; 468214117Sjamie#endif 469214117Sjamie 470214117Sjamie error = 0; 471214649Sjamie /* Check format of boolan and integer values. */ 472214649Sjamie TAILQ_FOREACH(p, &j->params, tq) { 473223188Sjamie if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) { 474223188Sjamie val = TAILQ_LAST(&p->val, cfstrings)->s; 475214649Sjamie if (p->flags & PF_BOOL) { 476214649Sjamie if (strcasecmp(val, "false") && 477214649Sjamie strcasecmp(val, "true") && 478214649Sjamie ((void)strtol(val, &ep, 10), *ep)) { 479214649Sjamie jail_warnx(j, 480214649Sjamie "%s: unknown boolean value \"%s\"", 481214649Sjamie p->name, val); 482214649Sjamie error = -1; 483214649Sjamie } 484214649Sjamie } else { 485214649Sjamie (void)strtol(val, &ep, 10); 486214649Sjamie if (ep == val || *ep) { 487214649Sjamie jail_warnx(j, 488214649Sjamie "%s: non-integer value \"%s\"", 489214649Sjamie p->name, val); 490214649Sjamie error = -1; 491214649Sjamie } 492214649Sjamie } 493214649Sjamie } 494214649Sjamie } 495214649Sjamie 496223351Sjamie#if defined(INET) || defined(INET6) 497214117Sjamie /* 498214117Sjamie * The ip_hostname parameter looks up the hostname, and adds parameters 499214117Sjamie * for any IP addresses it finds. 500214117Sjamie */ 501214649Sjamie if (((j->flags & JF_OP_MASK) != JF_STOP || 502214649Sjamie j->intparams[IP_INTERFACE] != NULL) && 503214649Sjamie bool_param(j->intparams[IP_IP_HOSTNAME]) && 504214423Sjamie (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { 505214117Sjamie j->intparams[IP_IP_HOSTNAME] = NULL; 506214117Sjamie /* 507214117Sjamie * Silently ignore unsupported address families from 508214117Sjamie * DNS lookups. 509214117Sjamie */ 510223351Sjamie#ifdef INET 511223351Sjamie ip4ok = feature_present("inet"); 512214117Sjamie#endif 513214117Sjamie#ifdef INET6 514223351Sjamie ip6ok = feature_present("inet6"); 515214117Sjamie#endif 516223351Sjamie if ( 517223351Sjamie#if defined(INET) && defined(INET6) 518223351Sjamie ip4ok || ip6ok 519223351Sjamie#elif defined(INET) 520223351Sjamie ip4ok 521223351Sjamie#elif defined(INET6) 522223351Sjamie ip6ok 523223351Sjamie#endif 524223351Sjamie ) { 525214117Sjamie /* Look up the hostname (or get the address) */ 526214117Sjamie memset(&hints, 0, sizeof(hints)); 527214117Sjamie hints.ai_socktype = SOCK_STREAM; 528214117Sjamie hints.ai_family = 529223351Sjamie#if defined(INET) && defined(INET6) 530223351Sjamie ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6; 531223351Sjamie#elif defined(INET) 532223351Sjamie PF_INET; 533223351Sjamie#elif defined(INET6) 534223351Sjamie PF_INET6; 535214117Sjamie#endif 536214649Sjamie gicode = getaddrinfo(hostname, NULL, &hints, &ai0); 537214649Sjamie if (gicode != 0) { 538214117Sjamie jail_warnx(j, "host.hostname %s: %s", hostname, 539214649Sjamie gai_strerror(gicode)); 540214117Sjamie error = -1; 541214117Sjamie } else { 542214117Sjamie /* 543214117Sjamie * Convert the addresses to ASCII so jailparam 544214117Sjamie * can convert them back. Errors are not 545214117Sjamie * expected here. 546214117Sjamie */ 547214117Sjamie for (ai = ai0; ai; ai = ai->ai_next) 548214117Sjamie switch (ai->ai_family) { 549223351Sjamie#ifdef INET 550214117Sjamie case AF_INET: 551214117Sjamie memcpy(&addr4, 552214117Sjamie &((struct sockaddr_in *) 553214117Sjamie (void *)ai->ai_addr)-> 554214117Sjamie sin_addr, sizeof(addr4)); 555214117Sjamie if (inet_ntop(AF_INET, 556214117Sjamie &addr4, avalue4, 557214117Sjamie INET_ADDRSTRLEN) == NULL) 558214117Sjamie err(1, "inet_ntop"); 559214423Sjamie add_param(j, NULL, KP_IP4_ADDR, 560214117Sjamie avalue4); 561214117Sjamie break; 562223351Sjamie#endif 563214117Sjamie#ifdef INET6 564214117Sjamie case AF_INET6: 565214117Sjamie memcpy(&addr6, 566214117Sjamie &((struct sockaddr_in6 *) 567214117Sjamie (void *)ai->ai_addr)-> 568214117Sjamie sin6_addr, sizeof(addr6)); 569214117Sjamie if (inet_ntop(AF_INET6, 570214117Sjamie &addr6, avalue6, 571214117Sjamie INET6_ADDRSTRLEN) == NULL) 572214117Sjamie err(1, "inet_ntop"); 573214423Sjamie add_param(j, NULL, KP_IP6_ADDR, 574214117Sjamie avalue6); 575214117Sjamie break; 576214117Sjamie#endif 577214117Sjamie } 578214117Sjamie freeaddrinfo(ai0); 579214117Sjamie } 580214117Sjamie } 581214117Sjamie } 582214649Sjamie 583214117Sjamie /* 584214117Sjamie * IP addresses may include an interface to set that address on, 585269805Ssmh * a netmask/suffix for that address and options for ifconfig. 586269805Ssmh * These are copied to an internal command parameter and then stripped 587269805Ssmh * so they won't be passed on to jailparam_set. 588214117Sjamie */ 589214117Sjamie defif = string_param(j->intparams[IP_INTERFACE]) != NULL; 590223351Sjamie#ifdef INET 591223351Sjamie if (j->intparams[KP_IP4_ADDR] != NULL) { 592223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) { 593214117Sjamie cs = strchr(s->s, '|'); 594214423Sjamie if (cs || defif) 595223351Sjamie add_param(j, NULL, IP__IP4_IFADDR, s->s); 596214423Sjamie if (cs) { 597214423Sjamie strcpy(s->s, cs + 1); 598214423Sjamie s->len -= cs + 1 - s->s; 599214117Sjamie } 600214117Sjamie if ((cs = strchr(s->s, '/'))) { 601214117Sjamie prefix = strtol(cs + 1, &ep, 10); 602223351Sjamie if (*ep == '.' 603223351Sjamie ? inet_pton(AF_INET, cs + 1, &addr4) != 1 604223351Sjamie : *ep || prefix < 0 || prefix > 32) { 605223351Sjamie jail_warnx(j, 606223351Sjamie "ip4.addr: bad netmask \"%s\"", cs); 607223351Sjamie error = -1; 608223351Sjamie } 609239621Sjamie *cs = '\0'; 610239601Sjamie s->len = cs - s->s; 611223351Sjamie } 612269805Ssmh if ((cs = strchr(s->s, ' ')) != NULL) { 613269805Ssmh *cs = '\0'; 614269805Ssmh s->len = cs - s->s; 615269805Ssmh } 616223351Sjamie } 617223351Sjamie } 618214433Sjamie#endif 619214433Sjamie#ifdef INET6 620223351Sjamie if (j->intparams[KP_IP6_ADDR] != NULL) { 621223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) { 622223351Sjamie cs = strchr(s->s, '|'); 623223351Sjamie if (cs || defif) 624223351Sjamie add_param(j, NULL, IP__IP6_IFADDR, s->s); 625223351Sjamie if (cs) { 626223351Sjamie strcpy(s->s, cs + 1); 627223351Sjamie s->len -= cs + 1 - s->s; 628223351Sjamie } 629223351Sjamie if ((cs = strchr(s->s, '/'))) { 630223351Sjamie prefix = strtol(cs + 1, &ep, 10); 631223351Sjamie if (*ep || prefix < 0 || prefix > 128) { 632214433Sjamie jail_warnx(j, 633223351Sjamie "ip6.addr: bad prefixlen \"%s\"", 634214117Sjamie cs); 635214117Sjamie error = -1; 636214117Sjamie } 637239621Sjamie *cs = '\0'; 638239601Sjamie s->len = cs - s->s; 639214117Sjamie } 640269805Ssmh if ((cs = strchr(s->s, ' ')) != NULL) { 641269805Ssmh *cs = '\0'; 642269805Ssmh s->len = cs - s->s; 643269805Ssmh } 644214117Sjamie } 645214117Sjamie } 646214117Sjamie#endif 647223351Sjamie#endif 648214783Sjamie 649214783Sjamie /* 650214783Sjamie * Read mount.fstab file(s), and treat each line as its own mount 651214783Sjamie * parameter. 652214783Sjamie */ 653214783Sjamie if (j->intparams[IP_MOUNT_FSTAB] != NULL) { 654223188Sjamie TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) { 655214783Sjamie if (s->len == 0) 656214783Sjamie continue; 657214783Sjamie f = fopen(s->s, "r"); 658214783Sjamie if (f == NULL) { 659214783Sjamie jail_warnx(j, "mount.fstab: %s: %s", 660214783Sjamie s->s, strerror(errno)); 661214783Sjamie error = -1; 662214783Sjamie continue; 663214783Sjamie } 664214783Sjamie while ((ln = fgetln(f, &lnlen))) { 665214783Sjamie if ((cs = memchr(ln, '#', lnlen - 1))) 666214783Sjamie lnlen = cs - ln + 1; 667214783Sjamie if (ln[lnlen - 1] == '\n' || 668214783Sjamie ln[lnlen - 1] == '#') 669214783Sjamie ln[lnlen - 1] = '\0'; 670214783Sjamie else { 671214783Sjamie cs = alloca(lnlen + 1); 672214783Sjamie strlcpy(cs, ln, lnlen + 1); 673214783Sjamie ln = cs; 674214783Sjamie } 675214783Sjamie add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln); 676214783Sjamie } 677214783Sjamie fclose(f); 678214783Sjamie } 679214783Sjamie } 680214783Sjamie if (error) 681214783Sjamie failed(j); 682214117Sjamie return error; 683214117Sjamie} 684214117Sjamie 685214117Sjamie/* 686214117Sjamie * Import parameters into libjail's binary jailparam format. 687214117Sjamie */ 688214117Sjamieint 689214117Sjamieimport_params(struct cfjail *j) 690214117Sjamie{ 691214117Sjamie struct cfparam *p; 692214117Sjamie struct cfstring *s, *ts; 693214117Sjamie struct jailparam *jp; 694214117Sjamie char *value, *cs; 695214117Sjamie size_t vallen; 696214117Sjamie int error; 697214117Sjamie 698214117Sjamie error = 0; 699214117Sjamie j->njp = 0; 700214117Sjamie TAILQ_FOREACH(p, &j->params, tq) 701214117Sjamie if (!(p->flags & PF_INTERNAL)) 702214117Sjamie j->njp++; 703214117Sjamie j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); 704214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 705214117Sjamie if (p->flags & PF_INTERNAL) 706214117Sjamie continue; 707214117Sjamie if (jailparam_init(jp, p->name) < 0) { 708214117Sjamie error = -1; 709214117Sjamie jail_warnx(j, "%s", jail_errmsg); 710241196Sjamie jp++; 711214117Sjamie continue; 712214117Sjamie } 713223188Sjamie if (TAILQ_EMPTY(&p->val)) 714214117Sjamie value = NULL; 715214117Sjamie else if (!jp->jp_elemlen || 716223188Sjamie !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) { 717214117Sjamie /* 718214117Sjamie * Scalar parameters silently discard multiple (array) 719214117Sjamie * values, keeping only the last value added. This 720214117Sjamie * lets values added from the command line append to 721214117Sjamie * arrays wthout pre-checking the type. 722214117Sjamie */ 723223188Sjamie value = TAILQ_LAST(&p->val, cfstrings)->s; 724214117Sjamie } else { 725214117Sjamie /* 726214117Sjamie * Convert arrays into comma-separated strings, which 727214117Sjamie * jailparam_import will then convert back into arrays. 728214117Sjamie */ 729214117Sjamie vallen = 0; 730223188Sjamie TAILQ_FOREACH(s, &p->val, tq) 731214117Sjamie vallen += s->len + 1; 732214117Sjamie value = alloca(vallen); 733214117Sjamie cs = value; 734223188Sjamie TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 735239601Sjamie memcpy(cs, s->s, s->len); 736239621Sjamie cs += s->len + 1; 737239621Sjamie cs[-1] = ','; 738214117Sjamie } 739239621Sjamie value[vallen - 1] = '\0'; 740214117Sjamie } 741214117Sjamie if (jailparam_import(jp, value) < 0) { 742214117Sjamie error = -1; 743214117Sjamie jail_warnx(j, "%s", jail_errmsg); 744214117Sjamie } 745214117Sjamie jp++; 746214117Sjamie } 747214117Sjamie if (error) { 748214117Sjamie jailparam_free(j->jp, j->njp); 749214117Sjamie free(j->jp); 750214117Sjamie j->jp = NULL; 751214117Sjamie failed(j); 752214117Sjamie } 753214117Sjamie return error; 754214117Sjamie} 755214117Sjamie 756214117Sjamie/* 757214117Sjamie * Check if options are equal (with or without the "no" prefix). 758214117Sjamie */ 759214117Sjamieint 760214117Sjamieequalopts(const char *opt1, const char *opt2) 761214117Sjamie{ 762214117Sjamie char *p; 763214117Sjamie 764214117Sjamie /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 765214117Sjamie if (strcmp(opt1, opt2) == 0) 766214117Sjamie return (1); 767214117Sjamie /* "noopt" vs. "opt" */ 768214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 769214117Sjamie return (1); 770214117Sjamie /* "opt" vs. "noopt" */ 771214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 772214117Sjamie return (1); 773214117Sjamie while ((p = strchr(opt1, '.')) != NULL && 774214117Sjamie !strncmp(opt1, opt2, ++p - opt1)) { 775214117Sjamie opt2 += p - opt1; 776214117Sjamie opt1 = p; 777214117Sjamie /* "foo.noopt" vs. "foo.opt" */ 778214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 779214117Sjamie return (1); 780214117Sjamie /* "foo.opt" vs. "foo.noopt" */ 781214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 782214117Sjamie return (1); 783214117Sjamie } 784214117Sjamie return (0); 785214117Sjamie} 786214117Sjamie 787214117Sjamie/* 788214117Sjamie * See if a jail name matches a wildcard. 789214117Sjamie */ 790214117Sjamieint 791214117Sjamiewild_jail_match(const char *jname, const char *wname) 792214117Sjamie{ 793214117Sjamie const char *jc, *jd, *wc, *wd; 794214117Sjamie 795214117Sjamie /* 796214117Sjamie * A non-final "*" component in the wild name matches a single jail 797214117Sjamie * component, and a final "*" matches one or more jail components. 798214117Sjamie */ 799214117Sjamie for (jc = jname, wc = wname; 800214117Sjamie (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); 801214117Sjamie jc = jd + 1, wc = wd + 1) 802214117Sjamie if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) 803214117Sjamie return 0; 804214117Sjamie return (!strcmp(jc, wc) || !strcmp(wc, "*")); 805214117Sjamie} 806214117Sjamie 807214117Sjamie/* 808214117Sjamie * Return if a jail name is a wildcard. 809214117Sjamie */ 810214117Sjamieint 811214117Sjamiewild_jail_name(const char *wname) 812214117Sjamie{ 813214117Sjamie const char *wc; 814214117Sjamie 815214117Sjamie for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) 816214117Sjamie if ((wc == wname || wc[-1] == '.') && 817214117Sjamie (wc[1] == '\0' || wc[1] == '.')) 818214117Sjamie return 1; 819214117Sjamie return 0; 820214117Sjamie} 821214117Sjamie 822214117Sjamie/* 823214117Sjamie * Free a parameter record and all its strings and variables. 824214117Sjamie */ 825214117Sjamiestatic void 826214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p) 827214117Sjamie{ 828214117Sjamie free(p->name); 829214117Sjamie free_param_strings(p); 830214117Sjamie TAILQ_REMOVE(pp, p, tq); 831214117Sjamie free(p); 832214117Sjamie} 833214117Sjamie 834214117Sjamiestatic void 835214117Sjamiefree_param_strings(struct cfparam *p) 836214117Sjamie{ 837214117Sjamie struct cfstring *s; 838214117Sjamie struct cfvar *v; 839214117Sjamie 840223188Sjamie while ((s = TAILQ_FIRST(&p->val))) { 841214117Sjamie free(s->s); 842214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 843214117Sjamie free(v->name); 844214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 845214117Sjamie free(v); 846214117Sjamie } 847223188Sjamie TAILQ_REMOVE(&p->val, s, tq); 848214117Sjamie free(s); 849214117Sjamie } 850214117Sjamie} 851