config.c revision 235789
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: head/usr.sbin/jail/config.c 235789 2012-05-22 16:33:10Z bapt $"); 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 84214423Sjamie [IP_MOUNT] = {"mount", PF_INTERNAL}, 85214423Sjamie [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, 86214423Sjamie [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, 87214423Sjamie [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, 88214423Sjamie [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, 89223351Sjamie#ifdef INET 90214423Sjamie [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV}, 91223351Sjamie#endif 92214117Sjamie#ifdef INET6 93214423Sjamie [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV}, 94214117Sjamie#endif 95214783Sjamie [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV}, 96223189Sjamie [IP__OP] = {NULL, PF_CONV}, 97214423Sjamie [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, 98214423Sjamie [KP_ALLOW_MOUNT] = {"allow.mount", 0}, 99214423Sjamie [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, 100214423Sjamie [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, 101214423Sjamie [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, 102214423Sjamie [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, 103232242Sjamie [KP_DEVFS_RULESET] = {"devfs_ruleset", 0}, 104214423Sjamie [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, 105214423Sjamie [KP_HOST_HOSTNAME] = {"host.hostname", 0}, 106223351Sjamie#ifdef INET 107214423Sjamie [KP_IP4_ADDR] = {"ip4.addr", 0}, 108223351Sjamie#endif 109214423Sjamie#ifdef INET6 110214423Sjamie [KP_IP6_ADDR] = {"ip6.addr", 0}, 111214423Sjamie#endif 112214423Sjamie [KP_JID] = {"jid", 0}, 113214423Sjamie [KP_NAME] = {"name", 0}, 114214423Sjamie [KP_PATH] = {"path", 0}, 115214423Sjamie [KP_PERSIST] = {"persist", 0}, 116214423Sjamie [KP_SECURELEVEL] = {"securelevel", 0}, 117214423Sjamie [KP_VNET] = {"vnet", 0}, 118214117Sjamie}; 119214117Sjamie 120214117Sjamie/* 121214117Sjamie * Parse the jail configuration file. 122214117Sjamie */ 123214117Sjamievoid 124214117Sjamieload_config(void) 125214117Sjamie{ 126214117Sjamie struct cfjails wild; 127214117Sjamie struct cfparams opp; 128214117Sjamie struct cfjail *j, *tj, *wj; 129214117Sjamie struct cfparam *p, *vp, *tp; 130214117Sjamie struct cfstring *s, *vs, *ns; 131214117Sjamie struct cfvar *v; 132214117Sjamie char *ep; 133214117Sjamie size_t varoff; 134214117Sjamie int did_self, jseq, pgen; 135214117Sjamie 136214117Sjamie if (!strcmp(cfname, "-")) { 137214117Sjamie cfname = "STDIN"; 138214117Sjamie yyin = stdin; 139214117Sjamie } else { 140214117Sjamie yyin = fopen(cfname, "r"); 141214117Sjamie if (!yyin) 142214117Sjamie err(1, "%s", cfname); 143214117Sjamie } 144214117Sjamie if (yyparse() || yynerrs) 145214117Sjamie exit(1); 146214117Sjamie 147214117Sjamie /* Separate the wildcard jails out from the actual jails. */ 148214117Sjamie jseq = 0; 149214117Sjamie TAILQ_INIT(&wild); 150214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 151214117Sjamie j->seq = ++jseq; 152214117Sjamie if (wild_jail_name(j->name)) 153214117Sjamie requeue(j, &wild); 154214117Sjamie } 155214117Sjamie 156214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) { 157214117Sjamie /* Set aside the jail's parameters. */ 158214117Sjamie TAILQ_INIT(&opp); 159214117Sjamie TAILQ_CONCAT(&opp, &j->params, tq); 160214117Sjamie /* 161214117Sjamie * The jail name implies its "name" or "jid" parameter, 162214117Sjamie * though they may also be explicitly set later on. 163214117Sjamie */ 164214117Sjamie add_param(j, NULL, 165214423Sjamie strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, 166214117Sjamie j->name); 167214117Sjamie /* 168214117Sjamie * Collect parameters for the jail, global parameters/variables, 169214117Sjamie * and any matching wildcard jails. 170214117Sjamie */ 171214117Sjamie did_self = 0; 172214117Sjamie TAILQ_FOREACH(wj, &wild, tq) { 173214117Sjamie if (j->seq < wj->seq && !did_self) { 174214117Sjamie TAILQ_FOREACH(p, &opp, tq) 175214423Sjamie add_param(j, p, 0, NULL); 176214117Sjamie did_self = 1; 177214117Sjamie } 178214117Sjamie if (wild_jail_match(j->name, wj->name)) 179214117Sjamie TAILQ_FOREACH(p, &wj->params, tq) 180214423Sjamie add_param(j, p, 0, NULL); 181214117Sjamie } 182214117Sjamie if (!did_self) 183214117Sjamie TAILQ_FOREACH(p, &opp, tq) 184214423Sjamie add_param(j, p, 0, NULL); 185214117Sjamie 186214117Sjamie /* Resolve any variable substitutions. */ 187214117Sjamie pgen = 0; 188214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 189214117Sjamie p->gen = ++pgen; 190214117Sjamie find_vars: 191223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 192214117Sjamie varoff = 0; 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); 234214117Sjamie memmove(s->s + v->pos + varoff + vs->len, 235214117Sjamie s->s + v->pos + varoff, 236214117Sjamie s->len - (v->pos + varoff) + 1); 237214117Sjamie memcpy(s->s + v->pos + varoff, vs->s, vs->len); 238214117Sjamie varoff += vs->len; 239214117Sjamie s->len += vs->len; 240223188Sjamie while ((vs = TAILQ_NEXT(vs, tq))) { 241214117Sjamie ns = emalloc(sizeof(struct cfstring)); 242214117Sjamie ns->s = estrdup(vs->s); 243214117Sjamie ns->len = vs->len; 244214117Sjamie STAILQ_INIT(&ns->vars); 245223188Sjamie TAILQ_INSERT_AFTER(&p->val, s, ns, tq); 246214117Sjamie s = ns; 247214117Sjamie } 248214117Sjamie free_var: 249214117Sjamie free(v->name); 250214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 251214117Sjamie free(v); 252214117Sjamie } 253214117Sjamie } 254214117Sjamie } 255214117Sjamie 256214117Sjamie /* Free the jail's original parameter list and any variables. */ 257214117Sjamie while ((p = TAILQ_FIRST(&opp))) 258214117Sjamie free_param(&opp, p); 259214117Sjamie TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) 260214117Sjamie if (p->flags & PF_VAR) 261214117Sjamie free_param(&j->params, p); 262214117Sjamie } 263214117Sjamie while ((wj = TAILQ_FIRST(&wild))) { 264214117Sjamie free(wj->name); 265214117Sjamie while ((p = TAILQ_FIRST(&wj->params))) 266214117Sjamie free_param(&wj->params, p); 267214117Sjamie TAILQ_REMOVE(&wild, wj, tq); 268214117Sjamie } 269214117Sjamie} 270214117Sjamie 271214117Sjamie/* 272214117Sjamie * Create a new jail record. 273214117Sjamie */ 274214117Sjamiestruct cfjail * 275214117Sjamieadd_jail(void) 276214117Sjamie{ 277214117Sjamie struct cfjail *j; 278214117Sjamie 279214117Sjamie j = emalloc(sizeof(struct cfjail)); 280214117Sjamie memset(j, 0, sizeof(struct cfjail)); 281214117Sjamie TAILQ_INIT(&j->params); 282214117Sjamie STAILQ_INIT(&j->dep[DEP_FROM]); 283214117Sjamie STAILQ_INIT(&j->dep[DEP_TO]); 284214117Sjamie j->queue = &cfjails; 285214117Sjamie TAILQ_INSERT_TAIL(&cfjails, j, tq); 286214117Sjamie return j; 287214117Sjamie} 288214117Sjamie 289214117Sjamie/* 290214117Sjamie * Add a parameter to a jail. 291214117Sjamie */ 292214117Sjamievoid 293214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, 294214117Sjamie const char *value) 295214117Sjamie{ 296214117Sjamie struct cfstrings nss; 297214117Sjamie struct cfparam *dp, *np; 298214117Sjamie struct cfstring *s, *ns; 299214117Sjamie struct cfvar *v, *nv; 300214423Sjamie const char *name; 301214423Sjamie char *cs, *tname; 302214117Sjamie unsigned flags; 303214117Sjamie 304214117Sjamie if (j == NULL) { 305214117Sjamie /* Create a single anonymous jail if one doesn't yet exist. */ 306214117Sjamie j = TAILQ_LAST(&cfjails, cfjails); 307214117Sjamie if (j == NULL) 308214117Sjamie j = add_jail(); 309214117Sjamie } 310223188Sjamie TAILQ_INIT(&nss); 311214117Sjamie if (p != NULL) { 312214117Sjamie name = p->name; 313214117Sjamie flags = p->flags; 314214117Sjamie /* 315214117Sjamie * Make a copy of the parameter's string list, 316214117Sjamie * which may be freed if it's overridden later. 317214117Sjamie */ 318223188Sjamie TAILQ_FOREACH(s, &p->val, tq) { 319214117Sjamie ns = emalloc(sizeof(struct cfstring)); 320214117Sjamie ns->s = estrdup(s->s); 321214117Sjamie ns->len = s->len; 322214117Sjamie STAILQ_INIT(&ns->vars); 323214117Sjamie STAILQ_FOREACH(v, &s->vars, tq) { 324214117Sjamie nv = emalloc(sizeof(struct cfvar)); 325214117Sjamie nv->name = strdup(v->name); 326214117Sjamie nv->pos = v->pos; 327214117Sjamie STAILQ_INSERT_TAIL(&ns->vars, nv, tq); 328214117Sjamie } 329223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 330214117Sjamie } 331214117Sjamie } else { 332214117Sjamie flags = PF_APPEND; 333234988Sjamie if (ipnum != IP__NULL) { 334214423Sjamie name = intparams[ipnum].name; 335214423Sjamie flags |= intparams[ipnum].flags; 336214423Sjamie } else if ((cs = strchr(value, '='))) { 337214423Sjamie tname = alloca(cs - value + 1); 338214423Sjamie strlcpy(tname, value, cs - value + 1); 339214423Sjamie name = tname; 340214423Sjamie value = cs + 1; 341214423Sjamie } else { 342214423Sjamie name = value; 343214423Sjamie value = NULL; 344214423Sjamie } 345214117Sjamie if (value != NULL) { 346214117Sjamie ns = emalloc(sizeof(struct cfstring)); 347214117Sjamie ns->s = estrdup(value); 348214117Sjamie ns->len = strlen(value); 349214117Sjamie STAILQ_INIT(&ns->vars); 350223188Sjamie TAILQ_INSERT_TAIL(&nss, ns, tq); 351214117Sjamie } 352214117Sjamie } 353214117Sjamie 354214117Sjamie /* See if this parameter has already been added. */ 355234988Sjamie if (ipnum != IP__NULL) 356214423Sjamie dp = j->intparams[ipnum]; 357214423Sjamie else 358214423Sjamie TAILQ_FOREACH(dp, &j->params, tq) 359214423Sjamie if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) 360214423Sjamie break; 361214423Sjamie if (dp != NULL) { 362214423Sjamie /* Found it - append or replace. */ 363214423Sjamie if (strcmp(dp->name, name)) { 364214423Sjamie free(dp->name); 365214423Sjamie dp->name = estrdup(name); 366214117Sjamie } 367223188Sjamie if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss)) 368214423Sjamie free_param_strings(dp); 369223188Sjamie TAILQ_CONCAT(&dp->val, &nss, tq); 370214423Sjamie dp->flags |= flags; 371214423Sjamie } else { 372214117Sjamie /* Not found - add it. */ 373214117Sjamie np = emalloc(sizeof(struct cfparam)); 374214117Sjamie np->name = estrdup(name); 375223188Sjamie TAILQ_INIT(&np->val); 376223188Sjamie TAILQ_CONCAT(&np->val, &nss, tq); 377214117Sjamie np->flags = flags; 378214117Sjamie np->gen = 0; 379214117Sjamie TAILQ_INSERT_TAIL(&j->params, np, tq); 380234988Sjamie if (ipnum != IP__NULL) 381214423Sjamie j->intparams[ipnum] = np; 382214423Sjamie else 383234988Sjamie for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++) 384214423Sjamie if (!(intparams[ipnum].flags & PF_CONV) && 385214423Sjamie equalopts(name, intparams[ipnum].name)) { 386214423Sjamie j->intparams[ipnum] = np; 387214423Sjamie np->flags |= intparams[ipnum].flags; 388214423Sjamie break; 389214423Sjamie } 390214117Sjamie } 391214117Sjamie} 392214117Sjamie 393214117Sjamie/* 394214117Sjamie * Return if a boolean parameter exists and is true. 395214117Sjamie */ 396214117Sjamieint 397214117Sjamiebool_param(const struct cfparam *p) 398214117Sjamie{ 399214117Sjamie const char *cs; 400214117Sjamie 401214117Sjamie if (p == NULL) 402214117Sjamie return 0; 403214117Sjamie cs = strrchr(p->name, '.'); 404214117Sjamie return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ 405223188Sjamie (TAILQ_EMPTY(&p->val) || 406223188Sjamie !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") || 407223188Sjamie (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10))); 408214117Sjamie} 409214117Sjamie 410214117Sjamie/* 411214117Sjamie * Set an integer if a parameter if it exists. 412214117Sjamie */ 413214117Sjamieint 414214117Sjamieint_param(const struct cfparam *p, int *ip) 415214117Sjamie{ 416223188Sjamie if (p == NULL || TAILQ_EMPTY(&p->val)) 417214117Sjamie return 0; 418223188Sjamie *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10); 419214117Sjamie return 1; 420214117Sjamie} 421214117Sjamie 422214117Sjamie/* 423214117Sjamie * Return the string value of a scalar parameter if it exists. 424214117Sjamie */ 425214117Sjamieconst char * 426214117Sjamiestring_param(const struct cfparam *p) 427214117Sjamie{ 428223188Sjamie return (p && !TAILQ_EMPTY(&p->val) 429223188Sjamie ? TAILQ_LAST(&p->val, cfstrings)->s : NULL); 430214117Sjamie} 431214117Sjamie 432214117Sjamie/* 433214649Sjamie * Check syntax and values of internal parameters. Set some internal 434214649Sjamie * parameters based on the values of others. 435214117Sjamie */ 436214117Sjamieint 437214649Sjamiecheck_intparams(struct cfjail *j) 438214117Sjamie{ 439214649Sjamie struct cfparam *p; 440223327Sjamie struct cfstring *s; 441214783Sjamie FILE *f; 442223351Sjamie const char *val; 443214783Sjamie char *cs, *ep, *ln; 444223351Sjamie size_t lnlen; 445223351Sjamie int error; 446223351Sjamie#if defined(INET) || defined(INET6) 447223351Sjamie struct addrinfo hints; 448223351Sjamie struct addrinfo *ai0, *ai; 449223351Sjamie const char *hostname; 450223351Sjamie int gicode, defif, prefix; 451223351Sjamie#endif 452223351Sjamie#ifdef INET 453223351Sjamie struct in_addr addr4; 454223351Sjamie int ip4ok; 455214117Sjamie char avalue4[INET_ADDRSTRLEN]; 456223351Sjamie#endif 457214117Sjamie#ifdef INET6 458214117Sjamie struct in6_addr addr6; 459223351Sjamie int ip6ok; 460214117Sjamie char avalue6[INET6_ADDRSTRLEN]; 461214117Sjamie#endif 462214117Sjamie 463214117Sjamie error = 0; 464214649Sjamie /* Check format of boolan and integer values. */ 465214649Sjamie TAILQ_FOREACH(p, &j->params, tq) { 466223188Sjamie if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) { 467223188Sjamie val = TAILQ_LAST(&p->val, cfstrings)->s; 468214649Sjamie if (p->flags & PF_BOOL) { 469214649Sjamie if (strcasecmp(val, "false") && 470214649Sjamie strcasecmp(val, "true") && 471214649Sjamie ((void)strtol(val, &ep, 10), *ep)) { 472214649Sjamie jail_warnx(j, 473214649Sjamie "%s: unknown boolean value \"%s\"", 474214649Sjamie p->name, val); 475214649Sjamie error = -1; 476214649Sjamie } 477214649Sjamie } else { 478214649Sjamie (void)strtol(val, &ep, 10); 479214649Sjamie if (ep == val || *ep) { 480214649Sjamie jail_warnx(j, 481214649Sjamie "%s: non-integer value \"%s\"", 482214649Sjamie p->name, val); 483214649Sjamie error = -1; 484214649Sjamie } 485214649Sjamie } 486214649Sjamie } 487214649Sjamie } 488214649Sjamie 489223351Sjamie#if defined(INET) || defined(INET6) 490214117Sjamie /* 491214117Sjamie * The ip_hostname parameter looks up the hostname, and adds parameters 492214117Sjamie * for any IP addresses it finds. 493214117Sjamie */ 494214649Sjamie if (((j->flags & JF_OP_MASK) != JF_STOP || 495214649Sjamie j->intparams[IP_INTERFACE] != NULL) && 496214649Sjamie bool_param(j->intparams[IP_IP_HOSTNAME]) && 497214423Sjamie (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { 498214117Sjamie j->intparams[IP_IP_HOSTNAME] = NULL; 499214117Sjamie /* 500214117Sjamie * Silently ignore unsupported address families from 501214117Sjamie * DNS lookups. 502214117Sjamie */ 503223351Sjamie#ifdef INET 504223351Sjamie ip4ok = feature_present("inet"); 505214117Sjamie#endif 506214117Sjamie#ifdef INET6 507223351Sjamie ip6ok = feature_present("inet6"); 508214117Sjamie#endif 509223351Sjamie if ( 510223351Sjamie#if defined(INET) && defined(INET6) 511223351Sjamie ip4ok || ip6ok 512223351Sjamie#elif defined(INET) 513223351Sjamie ip4ok 514223351Sjamie#elif defined(INET6) 515223351Sjamie ip6ok 516223351Sjamie#endif 517223351Sjamie ) { 518214117Sjamie /* Look up the hostname (or get the address) */ 519214117Sjamie memset(&hints, 0, sizeof(hints)); 520214117Sjamie hints.ai_socktype = SOCK_STREAM; 521214117Sjamie hints.ai_family = 522223351Sjamie#if defined(INET) && defined(INET6) 523223351Sjamie ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6; 524223351Sjamie#elif defined(INET) 525223351Sjamie PF_INET; 526223351Sjamie#elif defined(INET6) 527223351Sjamie PF_INET6; 528214117Sjamie#endif 529214649Sjamie gicode = getaddrinfo(hostname, NULL, &hints, &ai0); 530214649Sjamie if (gicode != 0) { 531214117Sjamie jail_warnx(j, "host.hostname %s: %s", hostname, 532214649Sjamie gai_strerror(gicode)); 533214117Sjamie error = -1; 534214117Sjamie } else { 535214117Sjamie /* 536214117Sjamie * Convert the addresses to ASCII so jailparam 537214117Sjamie * can convert them back. Errors are not 538214117Sjamie * expected here. 539214117Sjamie */ 540214117Sjamie for (ai = ai0; ai; ai = ai->ai_next) 541214117Sjamie switch (ai->ai_family) { 542223351Sjamie#ifdef INET 543214117Sjamie case AF_INET: 544214117Sjamie memcpy(&addr4, 545214117Sjamie &((struct sockaddr_in *) 546214117Sjamie (void *)ai->ai_addr)-> 547214117Sjamie sin_addr, sizeof(addr4)); 548214117Sjamie if (inet_ntop(AF_INET, 549214117Sjamie &addr4, avalue4, 550214117Sjamie INET_ADDRSTRLEN) == NULL) 551214117Sjamie err(1, "inet_ntop"); 552214423Sjamie add_param(j, NULL, KP_IP4_ADDR, 553214117Sjamie avalue4); 554214117Sjamie break; 555223351Sjamie#endif 556214117Sjamie#ifdef INET6 557214117Sjamie case AF_INET6: 558214117Sjamie memcpy(&addr6, 559214117Sjamie &((struct sockaddr_in6 *) 560214117Sjamie (void *)ai->ai_addr)-> 561214117Sjamie sin6_addr, sizeof(addr6)); 562214117Sjamie if (inet_ntop(AF_INET6, 563214117Sjamie &addr6, avalue6, 564214117Sjamie INET6_ADDRSTRLEN) == NULL) 565214117Sjamie err(1, "inet_ntop"); 566214423Sjamie add_param(j, NULL, KP_IP6_ADDR, 567214117Sjamie avalue6); 568214117Sjamie break; 569214117Sjamie#endif 570214117Sjamie } 571214117Sjamie freeaddrinfo(ai0); 572214117Sjamie } 573214117Sjamie } 574214117Sjamie } 575214649Sjamie 576214117Sjamie /* 577214117Sjamie * IP addresses may include an interface to set that address on, 578214117Sjamie * and a netmask/suffix for that address. 579214117Sjamie */ 580214117Sjamie defif = string_param(j->intparams[IP_INTERFACE]) != NULL; 581223351Sjamie#ifdef INET 582223351Sjamie if (j->intparams[KP_IP4_ADDR] != NULL) { 583223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) { 584214117Sjamie cs = strchr(s->s, '|'); 585214423Sjamie if (cs || defif) 586223351Sjamie add_param(j, NULL, IP__IP4_IFADDR, s->s); 587214423Sjamie if (cs) { 588214423Sjamie strcpy(s->s, cs + 1); 589214423Sjamie s->len -= cs + 1 - s->s; 590214117Sjamie } 591214117Sjamie if ((cs = strchr(s->s, '/'))) { 592214117Sjamie prefix = strtol(cs + 1, &ep, 10); 593223351Sjamie if (*ep == '.' 594223351Sjamie ? inet_pton(AF_INET, cs + 1, &addr4) != 1 595223351Sjamie : *ep || prefix < 0 || prefix > 32) { 596223351Sjamie jail_warnx(j, 597223351Sjamie "ip4.addr: bad netmask \"%s\"", cs); 598223351Sjamie error = -1; 599223351Sjamie } 600223351Sjamie *cs = '\0'; 601223351Sjamie s->len = cs - s->s + 1; 602223351Sjamie } 603223351Sjamie } 604223351Sjamie } 605214433Sjamie#endif 606214433Sjamie#ifdef INET6 607223351Sjamie if (j->intparams[KP_IP6_ADDR] != NULL) { 608223351Sjamie TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) { 609223351Sjamie cs = strchr(s->s, '|'); 610223351Sjamie if (cs || defif) 611223351Sjamie add_param(j, NULL, IP__IP6_IFADDR, s->s); 612223351Sjamie if (cs) { 613223351Sjamie strcpy(s->s, cs + 1); 614223351Sjamie s->len -= cs + 1 - s->s; 615223351Sjamie } 616223351Sjamie if ((cs = strchr(s->s, '/'))) { 617223351Sjamie prefix = strtol(cs + 1, &ep, 10); 618223351Sjamie if (*ep || prefix < 0 || prefix > 128) { 619214433Sjamie jail_warnx(j, 620223351Sjamie "ip6.addr: bad prefixlen \"%s\"", 621214117Sjamie cs); 622214117Sjamie error = -1; 623214117Sjamie } 624214117Sjamie *cs = '\0'; 625214117Sjamie s->len = cs - s->s + 1; 626214117Sjamie } 627214117Sjamie } 628214117Sjamie } 629214117Sjamie#endif 630223351Sjamie#endif 631214783Sjamie 632214783Sjamie /* 633214783Sjamie * Read mount.fstab file(s), and treat each line as its own mount 634214783Sjamie * parameter. 635214783Sjamie */ 636214783Sjamie if (j->intparams[IP_MOUNT_FSTAB] != NULL) { 637223188Sjamie TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) { 638214783Sjamie if (s->len == 0) 639214783Sjamie continue; 640214783Sjamie f = fopen(s->s, "r"); 641214783Sjamie if (f == NULL) { 642214783Sjamie jail_warnx(j, "mount.fstab: %s: %s", 643214783Sjamie s->s, strerror(errno)); 644214783Sjamie error = -1; 645214783Sjamie continue; 646214783Sjamie } 647214783Sjamie while ((ln = fgetln(f, &lnlen))) { 648214783Sjamie if ((cs = memchr(ln, '#', lnlen - 1))) 649214783Sjamie lnlen = cs - ln + 1; 650214783Sjamie if (ln[lnlen - 1] == '\n' || 651214783Sjamie ln[lnlen - 1] == '#') 652214783Sjamie ln[lnlen - 1] = '\0'; 653214783Sjamie else { 654214783Sjamie cs = alloca(lnlen + 1); 655214783Sjamie strlcpy(cs, ln, lnlen + 1); 656214783Sjamie ln = cs; 657214783Sjamie } 658214783Sjamie add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln); 659214783Sjamie } 660214783Sjamie fclose(f); 661214783Sjamie } 662214783Sjamie } 663214783Sjamie if (error) 664214783Sjamie failed(j); 665214117Sjamie return error; 666214117Sjamie} 667214117Sjamie 668214117Sjamie/* 669214117Sjamie * Import parameters into libjail's binary jailparam format. 670214117Sjamie */ 671214117Sjamieint 672214117Sjamieimport_params(struct cfjail *j) 673214117Sjamie{ 674214117Sjamie struct cfparam *p; 675214117Sjamie struct cfstring *s, *ts; 676214117Sjamie struct jailparam *jp; 677214117Sjamie char *value, *cs; 678214117Sjamie size_t vallen; 679214117Sjamie int error; 680214117Sjamie 681214117Sjamie error = 0; 682214117Sjamie j->njp = 0; 683214117Sjamie TAILQ_FOREACH(p, &j->params, tq) 684214117Sjamie if (!(p->flags & PF_INTERNAL)) 685214117Sjamie j->njp++; 686214117Sjamie j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); 687214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 688214117Sjamie if (p->flags & PF_INTERNAL) 689214117Sjamie continue; 690214117Sjamie if (jailparam_init(jp, p->name) < 0) { 691214117Sjamie error = -1; 692214117Sjamie jail_warnx(j, "%s", jail_errmsg); 693214117Sjamie continue; 694214117Sjamie } 695223188Sjamie if (TAILQ_EMPTY(&p->val)) 696214117Sjamie value = NULL; 697214117Sjamie else if (!jp->jp_elemlen || 698223188Sjamie !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) { 699214117Sjamie /* 700214117Sjamie * Scalar parameters silently discard multiple (array) 701214117Sjamie * values, keeping only the last value added. This 702214117Sjamie * lets values added from the command line append to 703214117Sjamie * arrays wthout pre-checking the type. 704214117Sjamie */ 705223188Sjamie value = TAILQ_LAST(&p->val, cfstrings)->s; 706214117Sjamie } else { 707214117Sjamie /* 708214117Sjamie * Convert arrays into comma-separated strings, which 709214117Sjamie * jailparam_import will then convert back into arrays. 710214117Sjamie */ 711214117Sjamie vallen = 0; 712223188Sjamie TAILQ_FOREACH(s, &p->val, tq) 713214117Sjamie vallen += s->len + 1; 714214117Sjamie value = alloca(vallen); 715214117Sjamie cs = value; 716223188Sjamie TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 717214117Sjamie strcpy(cs, s->s); 718214117Sjamie if (ts != NULL) { 719214117Sjamie cs += s->len + 1; 720214117Sjamie cs[-1] = ','; 721214117Sjamie } 722214117Sjamie } 723214117Sjamie } 724214117Sjamie if (jailparam_import(jp, value) < 0) { 725214117Sjamie error = -1; 726214117Sjamie jail_warnx(j, "%s", jail_errmsg); 727214117Sjamie } 728214117Sjamie jp++; 729214117Sjamie } 730214117Sjamie if (error) { 731214117Sjamie jailparam_free(j->jp, j->njp); 732214117Sjamie free(j->jp); 733214117Sjamie j->jp = NULL; 734214117Sjamie failed(j); 735214117Sjamie } 736214117Sjamie return error; 737214117Sjamie} 738214117Sjamie 739214117Sjamie/* 740214117Sjamie * Check if options are equal (with or without the "no" prefix). 741214117Sjamie */ 742214117Sjamieint 743214117Sjamieequalopts(const char *opt1, const char *opt2) 744214117Sjamie{ 745214117Sjamie char *p; 746214117Sjamie 747214117Sjamie /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 748214117Sjamie if (strcmp(opt1, opt2) == 0) 749214117Sjamie return (1); 750214117Sjamie /* "noopt" vs. "opt" */ 751214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 752214117Sjamie return (1); 753214117Sjamie /* "opt" vs. "noopt" */ 754214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 755214117Sjamie return (1); 756214117Sjamie while ((p = strchr(opt1, '.')) != NULL && 757214117Sjamie !strncmp(opt1, opt2, ++p - opt1)) { 758214117Sjamie opt2 += p - opt1; 759214117Sjamie opt1 = p; 760214117Sjamie /* "foo.noopt" vs. "foo.opt" */ 761214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 762214117Sjamie return (1); 763214117Sjamie /* "foo.opt" vs. "foo.noopt" */ 764214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 765214117Sjamie return (1); 766214117Sjamie } 767214117Sjamie return (0); 768214117Sjamie} 769214117Sjamie 770214117Sjamie/* 771214117Sjamie * See if a jail name matches a wildcard. 772214117Sjamie */ 773214117Sjamieint 774214117Sjamiewild_jail_match(const char *jname, const char *wname) 775214117Sjamie{ 776214117Sjamie const char *jc, *jd, *wc, *wd; 777214117Sjamie 778214117Sjamie /* 779214117Sjamie * A non-final "*" component in the wild name matches a single jail 780214117Sjamie * component, and a final "*" matches one or more jail components. 781214117Sjamie */ 782214117Sjamie for (jc = jname, wc = wname; 783214117Sjamie (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); 784214117Sjamie jc = jd + 1, wc = wd + 1) 785214117Sjamie if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) 786214117Sjamie return 0; 787214117Sjamie return (!strcmp(jc, wc) || !strcmp(wc, "*")); 788214117Sjamie} 789214117Sjamie 790214117Sjamie/* 791214117Sjamie * Return if a jail name is a wildcard. 792214117Sjamie */ 793214117Sjamieint 794214117Sjamiewild_jail_name(const char *wname) 795214117Sjamie{ 796214117Sjamie const char *wc; 797214117Sjamie 798214117Sjamie for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) 799214117Sjamie if ((wc == wname || wc[-1] == '.') && 800214117Sjamie (wc[1] == '\0' || wc[1] == '.')) 801214117Sjamie return 1; 802214117Sjamie return 0; 803214117Sjamie} 804214117Sjamie 805214117Sjamie/* 806214117Sjamie * Free a parameter record and all its strings and variables. 807214117Sjamie */ 808214117Sjamiestatic void 809214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p) 810214117Sjamie{ 811214117Sjamie free(p->name); 812214117Sjamie free_param_strings(p); 813214117Sjamie TAILQ_REMOVE(pp, p, tq); 814214117Sjamie free(p); 815214117Sjamie} 816214117Sjamie 817214117Sjamiestatic void 818214117Sjamiefree_param_strings(struct cfparam *p) 819214117Sjamie{ 820214117Sjamie struct cfstring *s; 821214117Sjamie struct cfvar *v; 822214117Sjamie 823223188Sjamie while ((s = TAILQ_FIRST(&p->val))) { 824214117Sjamie free(s->s); 825214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 826214117Sjamie free(v->name); 827214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 828214117Sjamie free(v); 829214117Sjamie } 830223188Sjamie TAILQ_REMOVE(&p->val, s, tq); 831214117Sjamie free(s); 832214117Sjamie } 833214117Sjamie} 834