config.c revision 214649
1214117Sjamie/*- 2214117Sjamie * Copyright (c) 2010 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: projects/jailconf/usr.sbin/jail/config.c 214649 2010-11-01 21:37:28Z jamie $"); 29214117Sjamie 30214117Sjamie#include <sys/types.h> 31214117Sjamie#include <sys/socket.h> 32214117Sjamie#include <sys/sysctl.h> 33214117Sjamie 34214117Sjamie#include <arpa/inet.h> 35214117Sjamie#include <netinet/in.h> 36214117Sjamie 37214117Sjamie#include <err.h> 38214117Sjamie#include <netdb.h> 39214117Sjamie#include <stdio.h> 40214117Sjamie#include <stdlib.h> 41214117Sjamie#include <string.h> 42214117Sjamie 43214117Sjamie#include "jailp.h" 44214117Sjamie 45214117Sjamiestruct ipspec { 46214117Sjamie const char *name; 47214117Sjamie unsigned flags; 48214117Sjamie}; 49214117Sjamie 50214117Sjamieextern FILE *yyin; 51214117Sjamieextern int yynerrs; 52214117Sjamie 53214117Sjamiestruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails); 54214117Sjamie 55214117Sjamiestatic void free_param(struct cfparams *pp, struct cfparam *p); 56214117Sjamiestatic void free_param_strings(struct cfparam *p); 57214117Sjamie 58214117Sjamiestatic const struct ipspec intparams[] = { 59214423Sjamie [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL}, 60214423Sjamie [IP_COMMAND] = {"command", PF_INTERNAL}, 61214423Sjamie [IP_DEPEND] = {"depend", PF_INTERNAL}, 62214423Sjamie [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL}, 63214423Sjamie [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL}, 64214423Sjamie [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT}, 65214423Sjamie [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL}, 66214423Sjamie [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL}, 67214423Sjamie [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL}, 68214423Sjamie [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL}, 69214423Sjamie [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL}, 70214423Sjamie [IP_EXEC_START] = {"exec.start", PF_INTERNAL}, 71214423Sjamie [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL}, 72214423Sjamie [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user", 73214423Sjamie PF_INTERNAL | PF_BOOL}, 74214423Sjamie [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL}, 75214423Sjamie [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT}, 76214423Sjamie [IP_INTERFACE] = {"interface", PF_INTERNAL}, 77214423Sjamie [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL}, 78214423Sjamie [IP_MOUNT] = {"mount", PF_INTERNAL}, 79214423Sjamie [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, 80214423Sjamie [IP_MOUNT_DEVFS_RULESET]= {"mount.devfs.ruleset", PF_INTERNAL}, 81214423Sjamie [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, 82214423Sjamie [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, 83214423Sjamie [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, 84214423Sjamie [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV}, 85214117Sjamie#ifdef INET6 86214423Sjamie [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV}, 87214117Sjamie#endif 88214423Sjamie [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, 89214423Sjamie [KP_ALLOW_MOUNT] = {"allow.mount", 0}, 90214423Sjamie [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, 91214423Sjamie [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, 92214423Sjamie [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, 93214423Sjamie [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, 94214423Sjamie [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, 95214423Sjamie [KP_HOST_HOSTNAME] = {"host.hostname", 0}, 96214423Sjamie [KP_IP4_ADDR] = {"ip4.addr", 0}, 97214423Sjamie#ifdef INET6 98214423Sjamie [KP_IP6_ADDR] = {"ip6.addr", 0}, 99214423Sjamie#endif 100214423Sjamie [KP_JID] = {"jid", 0}, 101214423Sjamie [KP_NAME] = {"name", 0}, 102214423Sjamie [KP_PATH] = {"path", 0}, 103214423Sjamie [KP_PERSIST] = {"persist", 0}, 104214423Sjamie [KP_SECURELEVEL] = {"securelevel", 0}, 105214423Sjamie [KP_VNET] = {"vnet", 0}, 106214117Sjamie}; 107214117Sjamie 108214117Sjamie/* 109214117Sjamie * Parse the jail configuration file. 110214117Sjamie */ 111214117Sjamievoid 112214117Sjamieload_config(void) 113214117Sjamie{ 114214117Sjamie struct cfjails wild; 115214117Sjamie struct cfparams opp; 116214117Sjamie struct cfjail *j, *tj, *wj; 117214117Sjamie struct cfparam *p, *vp, *tp; 118214117Sjamie struct cfstring *s, *vs, *ns; 119214117Sjamie struct cfvar *v; 120214117Sjamie char *ep; 121214117Sjamie size_t varoff; 122214117Sjamie int did_self, jseq, pgen; 123214117Sjamie 124214117Sjamie if (!strcmp(cfname, "-")) { 125214117Sjamie cfname = "STDIN"; 126214117Sjamie yyin = stdin; 127214117Sjamie } else { 128214117Sjamie yyin = fopen(cfname, "r"); 129214117Sjamie if (!yyin) 130214117Sjamie err(1, "%s", cfname); 131214117Sjamie } 132214117Sjamie if (yyparse() || yynerrs) 133214117Sjamie exit(1); 134214117Sjamie 135214117Sjamie /* Separate the wildcard jails out from the actual jails. */ 136214117Sjamie jseq = 0; 137214117Sjamie TAILQ_INIT(&wild); 138214117Sjamie TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 139214117Sjamie j->seq = ++jseq; 140214117Sjamie if (wild_jail_name(j->name)) 141214117Sjamie requeue(j, &wild); 142214117Sjamie } 143214117Sjamie 144214117Sjamie TAILQ_FOREACH(j, &cfjails, tq) { 145214117Sjamie /* Set aside the jail's parameters. */ 146214117Sjamie TAILQ_INIT(&opp); 147214117Sjamie TAILQ_CONCAT(&opp, &j->params, tq); 148214117Sjamie /* 149214117Sjamie * The jail name implies its "name" or "jid" parameter, 150214117Sjamie * though they may also be explicitly set later on. 151214117Sjamie */ 152214117Sjamie add_param(j, NULL, 153214423Sjamie strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, 154214117Sjamie j->name); 155214117Sjamie /* 156214117Sjamie * Collect parameters for the jail, global parameters/variables, 157214117Sjamie * and any matching wildcard jails. 158214117Sjamie */ 159214117Sjamie did_self = 0; 160214117Sjamie TAILQ_FOREACH(wj, &wild, tq) { 161214117Sjamie if (j->seq < wj->seq && !did_self) { 162214117Sjamie TAILQ_FOREACH(p, &opp, tq) 163214423Sjamie add_param(j, p, 0, NULL); 164214117Sjamie did_self = 1; 165214117Sjamie } 166214117Sjamie if (wild_jail_match(j->name, wj->name)) 167214117Sjamie TAILQ_FOREACH(p, &wj->params, tq) 168214423Sjamie add_param(j, p, 0, NULL); 169214117Sjamie } 170214117Sjamie if (!did_self) 171214117Sjamie TAILQ_FOREACH(p, &opp, tq) 172214423Sjamie add_param(j, p, 0, NULL); 173214117Sjamie 174214117Sjamie /* Resolve any variable substitutions. */ 175214117Sjamie pgen = 0; 176214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 177214117Sjamie p->gen = ++pgen; 178214117Sjamie find_vars: 179214117Sjamie STAILQ_FOREACH(s, &p->val, tq) { 180214117Sjamie varoff = 0; 181214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 182214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 183214117Sjamie if (!strcmp(vp->name, v->name)) 184214117Sjamie break; 185214117Sjamie if (!vp) { 186214117Sjamie jail_warnx(j, 187214117Sjamie "%s: variable \"%s\" not found", 188214117Sjamie p->name, v->name); 189214117Sjamie bad_var: 190214117Sjamie j->flags |= JF_FAILED; 191214117Sjamie TAILQ_FOREACH(vp, &j->params, tq) 192214117Sjamie if (vp->gen == pgen) 193214117Sjamie vp->flags |= PF_BAD; 194214117Sjamie goto free_var; 195214117Sjamie } 196214117Sjamie if (vp->flags & PF_BAD) 197214117Sjamie goto bad_var; 198214117Sjamie if (vp->gen == pgen) { 199214117Sjamie jail_warnx(j, "%s: variable loop", 200214117Sjamie v->name); 201214117Sjamie goto bad_var; 202214117Sjamie } 203214117Sjamie STAILQ_FOREACH(vs, &vp->val, tq) 204214117Sjamie if (!STAILQ_EMPTY(&vs->vars)) { 205214117Sjamie vp->gen = pgen; 206214117Sjamie TAILQ_REMOVE(&j->params, vp, 207214117Sjamie tq); 208214117Sjamie TAILQ_INSERT_BEFORE(p, vp, tq); 209214117Sjamie p = vp; 210214117Sjamie goto find_vars; 211214117Sjamie } 212214117Sjamie vs = STAILQ_FIRST(&vp->val); 213214117Sjamie if (STAILQ_NEXT(vs, tq) != NULL && 214214117Sjamie (s->s[0] != '\0' || 215214117Sjamie STAILQ_NEXT(v, tq))) { 216214117Sjamie jail_warnx(j, "%s: array cannot be " 217214117Sjamie "substituted inline", 218214117Sjamie p->name); 219214117Sjamie goto bad_var; 220214117Sjamie } 221214117Sjamie s->s = erealloc(s->s, s->len + vs->len + 1); 222214117Sjamie memmove(s->s + v->pos + varoff + vs->len, 223214117Sjamie s->s + v->pos + varoff, 224214117Sjamie s->len - (v->pos + varoff) + 1); 225214117Sjamie memcpy(s->s + v->pos + varoff, vs->s, vs->len); 226214117Sjamie varoff += vs->len; 227214117Sjamie s->len += vs->len; 228214117Sjamie while ((vs = STAILQ_NEXT(vs, tq))) { 229214117Sjamie ns = emalloc(sizeof(struct cfstring)); 230214117Sjamie ns->s = estrdup(vs->s); 231214117Sjamie ns->len = vs->len; 232214117Sjamie STAILQ_INIT(&ns->vars); 233214117Sjamie STAILQ_INSERT_AFTER(&p->val, s, ns, tq); 234214117Sjamie s = ns; 235214117Sjamie } 236214117Sjamie free_var: 237214117Sjamie free(v->name); 238214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 239214117Sjamie free(v); 240214117Sjamie } 241214117Sjamie } 242214117Sjamie } 243214117Sjamie 244214117Sjamie /* Free the jail's original parameter list and any variables. */ 245214117Sjamie while ((p = TAILQ_FIRST(&opp))) 246214117Sjamie free_param(&opp, p); 247214117Sjamie TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) 248214117Sjamie if (p->flags & PF_VAR) 249214117Sjamie free_param(&j->params, p); 250214117Sjamie } 251214117Sjamie while ((wj = TAILQ_FIRST(&wild))) { 252214117Sjamie free(wj->name); 253214117Sjamie while ((p = TAILQ_FIRST(&wj->params))) 254214117Sjamie free_param(&wj->params, p); 255214117Sjamie TAILQ_REMOVE(&wild, wj, tq); 256214117Sjamie } 257214117Sjamie} 258214117Sjamie 259214117Sjamie/* 260214117Sjamie * Create a new jail record. 261214117Sjamie */ 262214117Sjamiestruct cfjail * 263214117Sjamieadd_jail(void) 264214117Sjamie{ 265214117Sjamie struct cfjail *j; 266214117Sjamie 267214117Sjamie j = emalloc(sizeof(struct cfjail)); 268214117Sjamie memset(j, 0, sizeof(struct cfjail)); 269214117Sjamie TAILQ_INIT(&j->params); 270214117Sjamie STAILQ_INIT(&j->dep[DEP_FROM]); 271214117Sjamie STAILQ_INIT(&j->dep[DEP_TO]); 272214117Sjamie j->queue = &cfjails; 273214117Sjamie TAILQ_INSERT_TAIL(&cfjails, j, tq); 274214117Sjamie return j; 275214117Sjamie} 276214117Sjamie 277214117Sjamie/* 278214117Sjamie * Add a parameter to a jail. 279214117Sjamie */ 280214117Sjamievoid 281214423Sjamieadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, 282214117Sjamie const char *value) 283214117Sjamie{ 284214117Sjamie struct cfstrings nss; 285214117Sjamie struct cfparam *dp, *np; 286214117Sjamie struct cfstring *s, *ns; 287214117Sjamie struct cfvar *v, *nv; 288214423Sjamie struct ipspec *ips; 289214423Sjamie const char *name; 290214423Sjamie char *cs, *tname; 291214117Sjamie unsigned flags; 292214117Sjamie 293214117Sjamie if (j == NULL) { 294214117Sjamie /* Create a single anonymous jail if one doesn't yet exist. */ 295214117Sjamie j = TAILQ_LAST(&cfjails, cfjails); 296214117Sjamie if (j == NULL) 297214117Sjamie j = add_jail(); 298214117Sjamie } 299214117Sjamie STAILQ_INIT(&nss); 300214117Sjamie if (p != NULL) { 301214117Sjamie name = p->name; 302214117Sjamie flags = p->flags; 303214117Sjamie /* 304214117Sjamie * Make a copy of the parameter's string list, 305214117Sjamie * which may be freed if it's overridden later. 306214117Sjamie */ 307214117Sjamie STAILQ_FOREACH(s, &p->val, tq) { 308214117Sjamie ns = emalloc(sizeof(struct cfstring)); 309214117Sjamie ns->s = estrdup(s->s); 310214117Sjamie ns->len = s->len; 311214117Sjamie STAILQ_INIT(&ns->vars); 312214117Sjamie STAILQ_FOREACH(v, &s->vars, tq) { 313214117Sjamie nv = emalloc(sizeof(struct cfvar)); 314214117Sjamie nv->name = strdup(v->name); 315214117Sjamie nv->pos = v->pos; 316214117Sjamie STAILQ_INSERT_TAIL(&ns->vars, nv, tq); 317214117Sjamie } 318214117Sjamie STAILQ_INSERT_TAIL(&nss, ns, tq); 319214117Sjamie } 320214117Sjamie } else { 321214117Sjamie flags = PF_APPEND; 322214423Sjamie if (ipnum != 0) { 323214423Sjamie name = intparams[ipnum].name; 324214423Sjamie flags |= intparams[ipnum].flags; 325214423Sjamie } else if ((cs = strchr(value, '='))) { 326214423Sjamie tname = alloca(cs - value + 1); 327214423Sjamie strlcpy(tname, value, cs - value + 1); 328214423Sjamie name = tname; 329214423Sjamie value = cs + 1; 330214423Sjamie } else { 331214423Sjamie name = value; 332214423Sjamie value = NULL; 333214423Sjamie } 334214117Sjamie if (value != NULL) { 335214117Sjamie ns = emalloc(sizeof(struct cfstring)); 336214117Sjamie ns->s = estrdup(value); 337214117Sjamie ns->len = strlen(value); 338214117Sjamie STAILQ_INIT(&ns->vars); 339214117Sjamie STAILQ_INSERT_TAIL(&nss, ns, tq); 340214117Sjamie } 341214117Sjamie } 342214117Sjamie 343214117Sjamie /* See if this parameter has already been added. */ 344214423Sjamie if (ipnum != 0) 345214423Sjamie dp = j->intparams[ipnum]; 346214423Sjamie else 347214423Sjamie TAILQ_FOREACH(dp, &j->params, tq) 348214423Sjamie if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) 349214423Sjamie break; 350214423Sjamie if (dp != NULL) { 351214423Sjamie /* Found it - append or replace. */ 352214423Sjamie if (strcmp(dp->name, name)) { 353214423Sjamie free(dp->name); 354214423Sjamie dp->name = estrdup(name); 355214117Sjamie } 356214423Sjamie if (!(flags & PF_APPEND) || STAILQ_EMPTY(&nss)) 357214423Sjamie free_param_strings(dp); 358214423Sjamie STAILQ_CONCAT(&dp->val, &nss); 359214423Sjamie dp->flags |= flags; 360214423Sjamie } else { 361214117Sjamie /* Not found - add it. */ 362214117Sjamie np = emalloc(sizeof(struct cfparam)); 363214117Sjamie np->name = estrdup(name); 364214117Sjamie STAILQ_INIT(&np->val); 365214117Sjamie STAILQ_CONCAT(&np->val, &nss); 366214117Sjamie np->flags = flags; 367214117Sjamie np->gen = 0; 368214117Sjamie TAILQ_INSERT_TAIL(&j->params, np, tq); 369214423Sjamie if (ipnum != 0) 370214423Sjamie j->intparams[ipnum] = np; 371214423Sjamie else 372214423Sjamie for (ipnum = 1; ipnum < IP_NPARAM; ipnum++) 373214423Sjamie if (!(intparams[ipnum].flags & PF_CONV) && 374214423Sjamie equalopts(name, intparams[ipnum].name)) { 375214423Sjamie j->intparams[ipnum] = np; 376214423Sjamie np->flags |= intparams[ipnum].flags; 377214423Sjamie break; 378214423Sjamie } 379214117Sjamie } 380214117Sjamie} 381214117Sjamie 382214117Sjamie/* 383214117Sjamie * Return if a boolean parameter exists and is true. 384214117Sjamie */ 385214117Sjamieint 386214117Sjamiebool_param(const struct cfparam *p) 387214117Sjamie{ 388214117Sjamie const char *cs; 389214117Sjamie 390214117Sjamie if (p == NULL) 391214117Sjamie return 0; 392214117Sjamie cs = strrchr(p->name, '.'); 393214117Sjamie return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ 394214117Sjamie (STAILQ_EMPTY(&p->val) || 395214117Sjamie !strcasecmp(STAILQ_LAST(&p->val, cfstring, tq)->s, "true") || 396214117Sjamie (strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10))); 397214117Sjamie} 398214117Sjamie 399214117Sjamie/* 400214117Sjamie * Set an integer if a parameter if it exists. 401214117Sjamie */ 402214117Sjamieint 403214117Sjamieint_param(const struct cfparam *p, int *ip) 404214117Sjamie{ 405214117Sjamie if (p == NULL || STAILQ_EMPTY(&p->val)) 406214117Sjamie return 0; 407214117Sjamie *ip = strtol(STAILQ_LAST(&p->val, cfstring, tq)->s, NULL, 10); 408214117Sjamie return 1; 409214117Sjamie} 410214117Sjamie 411214117Sjamie/* 412214117Sjamie * Return the string value of a scalar parameter if it exists. 413214117Sjamie */ 414214117Sjamieconst char * 415214117Sjamiestring_param(const struct cfparam *p) 416214117Sjamie{ 417214117Sjamie return (p && !STAILQ_EMPTY(&p->val) 418214117Sjamie ? STAILQ_LAST(&p->val, cfstring, tq)->s : NULL); 419214117Sjamie} 420214117Sjamie 421214117Sjamie/* 422214649Sjamie * Check syntax and values of internal parameters. Set some internal 423214649Sjamie * parameters based on the values of others. 424214117Sjamie */ 425214117Sjamieint 426214649Sjamiecheck_intparams(struct cfjail *j) 427214117Sjamie{ 428214117Sjamie struct in_addr addr4; 429214649Sjamie struct addrinfo hints; 430214649Sjamie struct addrinfo *ai0, *ai; 431214649Sjamie struct cfparam *p; 432214117Sjamie struct cfstring *s, *ns; 433214649Sjamie const char *hostname, *val; 434214117Sjamie char *cs, *ep; 435214117Sjamie size_t size; 436214649Sjamie int error, gicode, ip4ok, defif, prefix; 437214117Sjamie int mib[4]; 438214117Sjamie char avalue4[INET_ADDRSTRLEN]; 439214117Sjamie#ifdef INET6 440214117Sjamie struct in6_addr addr6; 441214117Sjamie int ip6ok, isip6; 442214117Sjamie char avalue6[INET6_ADDRSTRLEN]; 443214117Sjamie#endif 444214117Sjamie 445214117Sjamie error = 0; 446214649Sjamie /* Check format of boolan and integer values. */ 447214649Sjamie TAILQ_FOREACH(p, &j->params, tq) { 448214649Sjamie if (!STAILQ_EMPTY(&p->val) && 449214649Sjamie (p->flags & (PF_BOOL | PF_INT))) { 450214649Sjamie val = STAILQ_LAST(&p->val, cfstring, tq)->s; 451214649Sjamie if (p->flags & PF_BOOL) { 452214649Sjamie if (strcasecmp(val, "false") && 453214649Sjamie strcasecmp(val, "true") && 454214649Sjamie ((void)strtol(val, &ep, 10), *ep)) { 455214649Sjamie jail_warnx(j, 456214649Sjamie "%s: unknown boolean value \"%s\"", 457214649Sjamie p->name, val); 458214649Sjamie error = -1; 459214649Sjamie } 460214649Sjamie } else { 461214649Sjamie (void)strtol(val, &ep, 10); 462214649Sjamie if (ep == val || *ep) { 463214649Sjamie jail_warnx(j, 464214649Sjamie "%s: non-integer value \"%s\"", 465214649Sjamie p->name, val); 466214649Sjamie error = -1; 467214649Sjamie } 468214649Sjamie } 469214649Sjamie } 470214649Sjamie } 471214649Sjamie 472214117Sjamie /* 473214117Sjamie * The ip_hostname parameter looks up the hostname, and adds parameters 474214117Sjamie * for any IP addresses it finds. 475214117Sjamie */ 476214649Sjamie if (((j->flags & JF_OP_MASK) != JF_STOP || 477214649Sjamie j->intparams[IP_INTERFACE] != NULL) && 478214649Sjamie bool_param(j->intparams[IP_IP_HOSTNAME]) && 479214423Sjamie (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { 480214117Sjamie j->intparams[IP_IP_HOSTNAME] = NULL; 481214117Sjamie /* 482214117Sjamie * Silently ignore unsupported address families from 483214117Sjamie * DNS lookups. 484214117Sjamie */ 485214117Sjamie size = 4; 486214117Sjamie ip4ok = sysctlnametomib("security.jail.param.ip4", mib, &size) 487214117Sjamie == 0; 488214117Sjamie#ifdef INET6 489214117Sjamie size = 4; 490214117Sjamie ip6ok = sysctlnametomib("security.jail.param.ip6", mib, &size) 491214117Sjamie == 0; 492214117Sjamie#endif 493214117Sjamie if (ip4ok 494214117Sjamie#ifdef INET6 495214117Sjamie || ip6ok 496214117Sjamie#endif 497214117Sjamie ) { 498214117Sjamie /* Look up the hostname (or get the address) */ 499214117Sjamie memset(&hints, 0, sizeof(hints)); 500214117Sjamie hints.ai_socktype = SOCK_STREAM; 501214117Sjamie hints.ai_family = 502214117Sjamie#ifdef INET6 503214117Sjamie ip6ok ? (ip4ok ? PF_UNSPEC : PF_INET6) : 504214117Sjamie#endif 505214117Sjamie PF_INET; 506214649Sjamie gicode = getaddrinfo(hostname, NULL, &hints, &ai0); 507214649Sjamie if (gicode != 0) { 508214117Sjamie jail_warnx(j, "host.hostname %s: %s", hostname, 509214649Sjamie gai_strerror(gicode)); 510214117Sjamie error = -1; 511214117Sjamie } else { 512214117Sjamie /* 513214117Sjamie * Convert the addresses to ASCII so jailparam 514214117Sjamie * can convert them back. Errors are not 515214117Sjamie * expected here. 516214117Sjamie */ 517214117Sjamie for (ai = ai0; ai; ai = ai->ai_next) 518214117Sjamie switch (ai->ai_family) { 519214117Sjamie case AF_INET: 520214117Sjamie memcpy(&addr4, 521214117Sjamie &((struct sockaddr_in *) 522214117Sjamie (void *)ai->ai_addr)-> 523214117Sjamie sin_addr, sizeof(addr4)); 524214117Sjamie if (inet_ntop(AF_INET, 525214117Sjamie &addr4, avalue4, 526214117Sjamie INET_ADDRSTRLEN) == NULL) 527214117Sjamie err(1, "inet_ntop"); 528214423Sjamie add_param(j, NULL, KP_IP4_ADDR, 529214117Sjamie avalue4); 530214117Sjamie break; 531214117Sjamie#ifdef INET6 532214117Sjamie case AF_INET6: 533214117Sjamie memcpy(&addr6, 534214117Sjamie &((struct sockaddr_in6 *) 535214117Sjamie (void *)ai->ai_addr)-> 536214117Sjamie sin6_addr, sizeof(addr6)); 537214117Sjamie if (inet_ntop(AF_INET6, 538214117Sjamie &addr6, avalue6, 539214117Sjamie INET6_ADDRSTRLEN) == NULL) 540214117Sjamie err(1, "inet_ntop"); 541214423Sjamie add_param(j, NULL, KP_IP6_ADDR, 542214117Sjamie avalue6); 543214117Sjamie break; 544214117Sjamie#endif 545214117Sjamie } 546214117Sjamie freeaddrinfo(ai0); 547214117Sjamie } 548214117Sjamie } 549214117Sjamie } 550214649Sjamie 551214117Sjamie /* 552214117Sjamie * IP addresses may include an interface to set that address on, 553214117Sjamie * and a netmask/suffix for that address. 554214117Sjamie */ 555214117Sjamie defif = string_param(j->intparams[IP_INTERFACE]) != NULL; 556214117Sjamie#ifdef INET6 557214117Sjamie for (isip6 = 0; isip6 <= 1; isip6++) 558214117Sjamie#else 559214117Sjamie#define isip6 0 560214117Sjamie do 561214117Sjamie#endif 562214117Sjamie { 563214117Sjamie if (j->intparams[KP_IP4_ADDR + isip6] == NULL) 564214117Sjamie continue; 565214117Sjamie STAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR + isip6]->val, tq) { 566214117Sjamie cs = strchr(s->s, '|'); 567214423Sjamie if (cs || defif) 568214423Sjamie add_param(j, NULL, IP__IP4_IFADDR + isip6, 569214423Sjamie s->s); 570214423Sjamie if (cs) { 571214423Sjamie strcpy(s->s, cs + 1); 572214423Sjamie s->len -= cs + 1 - s->s; 573214117Sjamie } 574214117Sjamie if ((cs = strchr(s->s, '/'))) { 575214117Sjamie prefix = strtol(cs + 1, &ep, 10); 576214433Sjamie if ( 577214433Sjamie#ifdef INET6 578214433Sjamie !isip6 && 579214433Sjamie#endif 580214433Sjamie *ep == '.' 581214117Sjamie ? inet_pton(AF_INET, cs + 1, &addr4) != 1 582214433Sjamie : *ep || prefix < 0 || prefix > ( 583214433Sjamie#ifdef INET6 584214433Sjamie isip6 ? 128 : 585214433Sjamie#endif 586214433Sjamie 32)) { 587214433Sjamie jail_warnx(j, 588214433Sjamie#ifdef INET6 589214433Sjamie isip6 590214433Sjamie ? "ip6.addr: bad prefixlen \"%s\"" : 591214433Sjamie#endif 592214433Sjamie "ip4.addr: bad netmask \"%s\"", 593214117Sjamie cs); 594214117Sjamie error = -1; 595214117Sjamie } 596214117Sjamie *cs = '\0'; 597214117Sjamie s->len = cs - s->s + 1; 598214117Sjamie } 599214117Sjamie } 600214117Sjamie } 601214117Sjamie#ifndef INET6 602214117Sjamie while (0); 603214117Sjamie#endif 604214117Sjamie return error; 605214117Sjamie} 606214117Sjamie 607214117Sjamie/* 608214117Sjamie * Import parameters into libjail's binary jailparam format. 609214117Sjamie */ 610214117Sjamieint 611214117Sjamieimport_params(struct cfjail *j) 612214117Sjamie{ 613214117Sjamie struct cfparam *p; 614214117Sjamie struct cfstring *s, *ts; 615214117Sjamie struct jailparam *jp; 616214117Sjamie char *value, *cs; 617214117Sjamie size_t vallen; 618214117Sjamie int error; 619214117Sjamie 620214117Sjamie error = 0; 621214117Sjamie j->njp = 0; 622214117Sjamie TAILQ_FOREACH(p, &j->params, tq) 623214117Sjamie if (!(p->flags & PF_INTERNAL)) 624214117Sjamie j->njp++; 625214117Sjamie j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); 626214117Sjamie TAILQ_FOREACH(p, &j->params, tq) { 627214117Sjamie if (p->flags & PF_INTERNAL) 628214117Sjamie continue; 629214117Sjamie if (jailparam_init(jp, p->name) < 0) { 630214117Sjamie error = -1; 631214117Sjamie jail_warnx(j, "%s", jail_errmsg); 632214117Sjamie continue; 633214117Sjamie } 634214117Sjamie if (STAILQ_EMPTY(&p->val)) 635214117Sjamie value = NULL; 636214117Sjamie else if (!jp->jp_elemlen || 637214117Sjamie !STAILQ_NEXT(STAILQ_FIRST(&p->val), tq)) { 638214117Sjamie /* 639214117Sjamie * Scalar parameters silently discard multiple (array) 640214117Sjamie * values, keeping only the last value added. This 641214117Sjamie * lets values added from the command line append to 642214117Sjamie * arrays wthout pre-checking the type. 643214117Sjamie */ 644214117Sjamie value = STAILQ_LAST(&p->val, cfstring, tq)->s; 645214117Sjamie } else { 646214117Sjamie /* 647214117Sjamie * Convert arrays into comma-separated strings, which 648214117Sjamie * jailparam_import will then convert back into arrays. 649214117Sjamie */ 650214117Sjamie vallen = 0; 651214117Sjamie STAILQ_FOREACH(s, &p->val, tq) 652214117Sjamie vallen += s->len + 1; 653214117Sjamie value = alloca(vallen); 654214117Sjamie cs = value; 655214117Sjamie STAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 656214117Sjamie strcpy(cs, s->s); 657214117Sjamie if (ts != NULL) { 658214117Sjamie cs += s->len + 1; 659214117Sjamie cs[-1] = ','; 660214117Sjamie } 661214117Sjamie } 662214117Sjamie } 663214117Sjamie if (jailparam_import(jp, value) < 0) { 664214117Sjamie error = -1; 665214117Sjamie jail_warnx(j, "%s", jail_errmsg); 666214117Sjamie } 667214117Sjamie jp++; 668214117Sjamie } 669214117Sjamie if (error) { 670214117Sjamie jailparam_free(j->jp, j->njp); 671214117Sjamie free(j->jp); 672214117Sjamie j->jp = NULL; 673214117Sjamie failed(j); 674214117Sjamie } 675214117Sjamie return error; 676214117Sjamie} 677214117Sjamie 678214117Sjamie/* 679214117Sjamie * Check if options are equal (with or without the "no" prefix). 680214117Sjamie */ 681214117Sjamieint 682214117Sjamieequalopts(const char *opt1, const char *opt2) 683214117Sjamie{ 684214117Sjamie char *p; 685214117Sjamie 686214117Sjamie /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 687214117Sjamie if (strcmp(opt1, opt2) == 0) 688214117Sjamie return (1); 689214117Sjamie /* "noopt" vs. "opt" */ 690214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 691214117Sjamie return (1); 692214117Sjamie /* "opt" vs. "noopt" */ 693214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 694214117Sjamie return (1); 695214117Sjamie while ((p = strchr(opt1, '.')) != NULL && 696214117Sjamie !strncmp(opt1, opt2, ++p - opt1)) { 697214117Sjamie opt2 += p - opt1; 698214117Sjamie opt1 = p; 699214117Sjamie /* "foo.noopt" vs. "foo.opt" */ 700214117Sjamie if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 701214117Sjamie return (1); 702214117Sjamie /* "foo.opt" vs. "foo.noopt" */ 703214117Sjamie if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 704214117Sjamie return (1); 705214117Sjamie } 706214117Sjamie return (0); 707214117Sjamie} 708214117Sjamie 709214117Sjamie/* 710214117Sjamie * See if a jail name matches a wildcard. 711214117Sjamie */ 712214117Sjamieint 713214117Sjamiewild_jail_match(const char *jname, const char *wname) 714214117Sjamie{ 715214117Sjamie const char *jc, *jd, *wc, *wd; 716214117Sjamie 717214117Sjamie /* 718214117Sjamie * A non-final "*" component in the wild name matches a single jail 719214117Sjamie * component, and a final "*" matches one or more jail components. 720214117Sjamie */ 721214117Sjamie for (jc = jname, wc = wname; 722214117Sjamie (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); 723214117Sjamie jc = jd + 1, wc = wd + 1) 724214117Sjamie if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) 725214117Sjamie return 0; 726214117Sjamie return (!strcmp(jc, wc) || !strcmp(wc, "*")); 727214117Sjamie} 728214117Sjamie 729214117Sjamie/* 730214117Sjamie * Return if a jail name is a wildcard. 731214117Sjamie */ 732214117Sjamieint 733214117Sjamiewild_jail_name(const char *wname) 734214117Sjamie{ 735214117Sjamie const char *wc; 736214117Sjamie 737214117Sjamie for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) 738214117Sjamie if ((wc == wname || wc[-1] == '.') && 739214117Sjamie (wc[1] == '\0' || wc[1] == '.')) 740214117Sjamie return 1; 741214117Sjamie return 0; 742214117Sjamie} 743214117Sjamie 744214117Sjamie/* 745214117Sjamie * Free a parameter record and all its strings and variables. 746214117Sjamie */ 747214117Sjamiestatic void 748214117Sjamiefree_param(struct cfparams *pp, struct cfparam *p) 749214117Sjamie{ 750214117Sjamie free(p->name); 751214117Sjamie free_param_strings(p); 752214117Sjamie TAILQ_REMOVE(pp, p, tq); 753214117Sjamie free(p); 754214117Sjamie} 755214117Sjamie 756214117Sjamiestatic void 757214117Sjamiefree_param_strings(struct cfparam *p) 758214117Sjamie{ 759214117Sjamie struct cfstring *s; 760214117Sjamie struct cfvar *v; 761214117Sjamie 762214117Sjamie while ((s = STAILQ_FIRST(&p->val))) { 763214117Sjamie free(s->s); 764214117Sjamie while ((v = STAILQ_FIRST(&s->vars))) { 765214117Sjamie free(v->name); 766214117Sjamie STAILQ_REMOVE_HEAD(&s->vars, tq); 767214117Sjamie free(v); 768214117Sjamie } 769214117Sjamie STAILQ_REMOVE_HEAD(&p->val, tq); 770214117Sjamie free(s); 771214117Sjamie } 772214117Sjamie} 773