config.c revision 278484
155714Skris/*- 255714Skris * Copyright (c) 2011 James Gritton 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8280304Sjkim * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280304Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280304Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455714Skris * SUCH DAMAGE. 2555714Skris */ 2655714Skris 2755714Skris#include <sys/cdefs.h> 2855714Skris__FBSDID("$FreeBSD: stable/10/usr.sbin/jail/config.c 278484 2015-02-10 01:05:51Z jamie $"); 2955714Skris 3055714Skris#include <sys/types.h> 3155714Skris#include <sys/errno.h> 3255714Skris#include <sys/socket.h> 3355714Skris#include <sys/sysctl.h> 3455714Skris 3555714Skris#include <arpa/inet.h> 3655714Skris#include <netinet/in.h> 37280304Sjkim 3855714Skris#include <err.h> 3955714Skris#include <netdb.h> 40280304Sjkim#include <stdio.h> 4155714Skris#include <stdlib.h> 4255714Skris#include <string.h> 4355714Skris#include <unistd.h> 4455714Skris 4555714Skris#include "jailp.h" 4655714Skris 4755714Skrisstruct ipspec { 4855714Skris const char *name; 4955714Skris unsigned flags; 5055714Skris}; 5155714Skris 52280304Sjkimextern FILE *yyin; 5355714Skrisextern int yynerrs; 5455714Skris 5555714Skrisextern int yyparse(void); 5655714Skris 5755714Skrisstruct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails); 5855714Skris 5955714Skrisstatic void free_param(struct cfparams *pp, struct cfparam *p); 6055714Skrisstatic void free_param_strings(struct cfparam *p); 6155714Skris 6255714Skrisstatic const struct ipspec intparams[] = { 6359191Skris [IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL}, 6455714Skris [IP_COMMAND] = {"command", PF_INTERNAL}, 6555714Skris [IP_DEPEND] = {"depend", PF_INTERNAL}, 6655714Skris [IP_EXEC_CLEAN] = {"exec.clean", PF_INTERNAL | PF_BOOL}, 6755714Skris [IP_EXEC_CONSOLELOG] = {"exec.consolelog", PF_INTERNAL}, 6855714Skris [IP_EXEC_FIB] = {"exec.fib", PF_INTERNAL | PF_INT}, 6955714Skris [IP_EXEC_JAIL_USER] = {"exec.jail_user", PF_INTERNAL}, 7059191Skris [IP_EXEC_POSTSTART] = {"exec.poststart", PF_INTERNAL}, 7155714Skris [IP_EXEC_POSTSTOP] = {"exec.poststop", PF_INTERNAL}, 7255714Skris [IP_EXEC_PRESTART] = {"exec.prestart", PF_INTERNAL}, 73238405Sjkim [IP_EXEC_PRESTOP] = {"exec.prestop", PF_INTERNAL}, 74238405Sjkim [IP_EXEC_START] = {"exec.start", PF_INTERNAL}, 75238405Sjkim [IP_EXEC_STOP] = {"exec.stop", PF_INTERNAL}, 76238405Sjkim [IP_EXEC_SYSTEM_JAIL_USER]= {"exec.system_jail_user", 77280304Sjkim PF_INTERNAL | PF_BOOL}, 78238405Sjkim [IP_EXEC_SYSTEM_USER] = {"exec.system_user", PF_INTERNAL}, 79238405Sjkim [IP_EXEC_TIMEOUT] = {"exec.timeout", PF_INTERNAL | PF_INT}, 80238405Sjkim#if defined(INET) || defined(INET6) 81280304Sjkim [IP_INTERFACE] = {"interface", PF_INTERNAL}, 82238405Sjkim [IP_IP_HOSTNAME] = {"ip_hostname", PF_INTERNAL | PF_BOOL}, 83238405Sjkim#endif 84238405Sjkim [IP_MOUNT] = {"mount", PF_INTERNAL | PF_REV}, 85280304Sjkim [IP_MOUNT_DEVFS] = {"mount.devfs", PF_INTERNAL | PF_BOOL}, 86238405Sjkim [IP_MOUNT_FDESCFS] = {"mount.fdescfs", PF_INTERNAL | PF_BOOL}, 87238405Sjkim [IP_MOUNT_PROCFS] = {"mount.procfs", PF_INTERNAL | PF_BOOL}, 88238405Sjkim [IP_MOUNT_FSTAB] = {"mount.fstab", PF_INTERNAL}, 89280304Sjkim [IP_STOP_TIMEOUT] = {"stop.timeout", PF_INTERNAL | PF_INT}, 90238405Sjkim [IP_VNET_INTERFACE] = {"vnet.interface", PF_INTERNAL}, 91238405Sjkim#ifdef INET 92238405Sjkim [IP__IP4_IFADDR] = {"ip4.addr", PF_INTERNAL | PF_CONV | PF_REV}, 93238405Sjkim#endif 94238405Sjkim#ifdef INET6 95238405Sjkim [IP__IP6_IFADDR] = {"ip6.addr", PF_INTERNAL | PF_CONV | PF_REV}, 96238405Sjkim#endif 97280304Sjkim [IP__MOUNT_FROM_FSTAB] = {"mount.fstab", PF_INTERNAL | PF_CONV | PF_REV}, 98238405Sjkim [IP__OP] = {NULL, PF_CONV}, 99238405Sjkim [KP_ALLOW_CHFLAGS] = {"allow.chflags", 0}, 100238405Sjkim [KP_ALLOW_MOUNT] = {"allow.mount", 0}, 101280304Sjkim [KP_ALLOW_RAW_SOCKETS] = {"allow.raw_sockets", 0}, 102238405Sjkim [KP_ALLOW_SET_HOSTNAME]= {"allow.set_hostname", 0}, 103238405Sjkim [KP_ALLOW_SOCKET_AF] = {"allow.socket_af", 0}, 104238405Sjkim [KP_ALLOW_SYSVIPC] = {"allow.sysvipc", 0}, 105280304Sjkim [KP_DEVFS_RULESET] = {"devfs_ruleset", 0}, 106238405Sjkim [KP_ENFORCE_STATFS] = {"enforce_statfs", 0}, 107238405Sjkim [KP_HOST_HOSTNAME] = {"host.hostname", 0}, 108238405Sjkim#ifdef INET 109280304Sjkim [KP_IP4_ADDR] = {"ip4.addr", 0}, 110238405Sjkim#endif 111280304Sjkim#ifdef INET6 11268651Skris [KP_IP6_ADDR] = {"ip6.addr", 0}, 11368651Skris#endif 114160814Ssimon [KP_JID] = {"jid", 0}, 115238405Sjkim [KP_NAME] = {"name", 0}, 11659191Skris [KP_PATH] = {"path", 0}, 117109998Smarkm [KP_PERSIST] = {"persist", 0}, 118109998Smarkm [KP_SECURELEVEL] = {"securelevel", 0}, 119160814Ssimon [KP_VNET] = {"vnet", 0}, 120238405Sjkim}; 121238405Sjkim 122280304Sjkim/* 123238405Sjkim * Parse the jail configuration file. 124280304Sjkim */ 125280304Sjkimvoid 126280304Sjkimload_config(void) 127280304Sjkim{ 128280304Sjkim struct cfjails wild; 129280304Sjkim struct cfparams opp; 130238405Sjkim struct cfjail *j, *tj, *wj; 131280304Sjkim struct cfparam *p, *vp, *tp; 132238405Sjkim struct cfstring *s, *vs, *ns; 133238405Sjkim struct cfvar *v; 134280304Sjkim char *ep; 135280304Sjkim size_t varoff; 136238405Sjkim int did_self, jseq, pgen; 13755714Skris 138280304Sjkim if (!strcmp(cfname, "-")) { 13955714Skris cfname = "STDIN"; 14055714Skris yyin = stdin; 141280304Sjkim } else { 142280304Sjkim yyin = fopen(cfname, "r"); 143280304Sjkim if (!yyin) 14455714Skris err(1, "%s", cfname); 14555714Skris } 14655714Skris if (yyparse() || yynerrs) 147280304Sjkim exit(1); 148280304Sjkim 149280304Sjkim /* Separate the wildcard jails out from the actual jails. */ 15055714Skris jseq = 0; 15155714Skris TAILQ_INIT(&wild); 15255714Skris TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) { 153280304Sjkim j->seq = ++jseq; 154284285Sjkim if (wild_jail_name(j->name)) 155280304Sjkim requeue(j, &wild); 156280304Sjkim } 157280304Sjkim 158284285Sjkim TAILQ_FOREACH(j, &cfjails, tq) { 159280304Sjkim /* Set aside the jail's parameters. */ 160280304Sjkim TAILQ_INIT(&opp); 161280304Sjkim TAILQ_CONCAT(&opp, &j->params, tq); 162280304Sjkim /* 163306196Sjkim * The jail name implies its "name" or "jid" parameter, 164280304Sjkim * though they may also be explicitly set later on. 165280304Sjkim */ 166285330Sjkim add_param(j, NULL, 167285330Sjkim strtol(j->name, &ep, 10) && !*ep ? KP_JID : KP_NAME, 168285330Sjkim j->name); 169285330Sjkim /* 170285330Sjkim * Collect parameters for the jail, global parameters/variables, 171285330Sjkim * and any matching wildcard jails. 172306196Sjkim */ 173285330Sjkim did_self = 0; 174285330Sjkim TAILQ_FOREACH(wj, &wild, tq) { 17555714Skris if (j->seq < wj->seq && !did_self) { 176280304Sjkim TAILQ_FOREACH(p, &opp, tq) 17755714Skris add_param(j, p, 0, NULL); 178280304Sjkim did_self = 1; 179280304Sjkim } 180280304Sjkim if (wild_jail_match(j->name, wj->name)) 181280304Sjkim TAILQ_FOREACH(p, &wj->params, tq) 182285330Sjkim add_param(j, p, 0, NULL); 183285330Sjkim } 184285330Sjkim if (!did_self) 185306196Sjkim TAILQ_FOREACH(p, &opp, tq) 186306196Sjkim add_param(j, p, 0, NULL); 187285330Sjkim 188280304Sjkim /* Resolve any variable substitutions. */ 189285330Sjkim pgen = 0; 190285330Sjkim TAILQ_FOREACH(p, &j->params, tq) { 19155714Skris p->gen = ++pgen; 192280304Sjkim find_vars: 193280304Sjkim TAILQ_FOREACH(s, &p->val, tq) { 194280304Sjkim varoff = 0; 195280304Sjkim while ((v = STAILQ_FIRST(&s->vars))) { 196306196Sjkim TAILQ_FOREACH(vp, &j->params, tq) 197306196Sjkim if (!strcmp(vp->name, v->name)) 198280304Sjkim break; 199280304Sjkim if (!vp) { 20055714Skris jail_warnx(j, 201280304Sjkim "%s: variable \"%s\" not found", 202280304Sjkim p->name, v->name); 203280304Sjkim bad_var: 20455714Skris j->flags |= JF_FAILED; 205280304Sjkim TAILQ_FOREACH(vp, &j->params, tq) 206280304Sjkim if (vp->gen == pgen) 207280304Sjkim vp->flags |= PF_BAD; 208280304Sjkim goto free_var; 209280304Sjkim } 210280304Sjkim if (vp->flags & PF_BAD) 211280304Sjkim goto bad_var; 21255714Skris if (vp->gen == pgen) { 213280304Sjkim jail_warnx(j, "%s: variable loop", 214280304Sjkim v->name); 215280304Sjkim goto bad_var; 21655714Skris } 217280304Sjkim TAILQ_FOREACH(vs, &vp->val, tq) 218280304Sjkim if (!STAILQ_EMPTY(&vs->vars)) { 219280304Sjkim vp->gen = pgen; 220280304Sjkim TAILQ_REMOVE(&j->params, vp, 221280304Sjkim tq); 222280304Sjkim TAILQ_INSERT_BEFORE(p, vp, tq); 223306196Sjkim p = vp; 224306196Sjkim goto find_vars; 225280304Sjkim } 226280304Sjkim vs = TAILQ_FIRST(&vp->val); 227280304Sjkim if (TAILQ_NEXT(vs, tq) != NULL && 228280304Sjkim (s->s[0] != '\0' || 229280304Sjkim STAILQ_NEXT(v, tq))) { 230280304Sjkim jail_warnx(j, "%s: array cannot be " 231280304Sjkim "substituted inline", 232280304Sjkim p->name); 233280304Sjkim goto bad_var; 234280304Sjkim } 235280304Sjkim s->s = erealloc(s->s, s->len + vs->len + 1); 236280304Sjkim memmove(s->s + v->pos + varoff + vs->len, 237280304Sjkim s->s + v->pos + varoff, 238280304Sjkim s->len - (v->pos + varoff) + 1); 239280304Sjkim memcpy(s->s + v->pos + varoff, vs->s, vs->len); 24055714Skris varoff += vs->len; 241284285Sjkim s->len += vs->len; 242284285Sjkim while ((vs = TAILQ_NEXT(vs, tq))) { 243280304Sjkim ns = emalloc(sizeof(struct cfstring)); 244280304Sjkim ns->s = estrdup(vs->s); 245280304Sjkim ns->len = vs->len; 246280304Sjkim STAILQ_INIT(&ns->vars); 247280304Sjkim TAILQ_INSERT_AFTER(&p->val, s, ns, tq); 24855714Skris s = ns; 249284285Sjkim } 250284285Sjkim free_var: 251284285Sjkim free(v->name); 252284285Sjkim STAILQ_REMOVE_HEAD(&s->vars, tq); 253284285Sjkim free(v); 254284285Sjkim } 255284285Sjkim } 256284285Sjkim } 257284285Sjkim 258284285Sjkim /* Free the jail's original parameter list and any variables. */ 259284285Sjkim while ((p = TAILQ_FIRST(&opp))) 260284285Sjkim free_param(&opp, p); 261284285Sjkim TAILQ_FOREACH_SAFE(p, &j->params, tq, tp) 262284285Sjkim if (p->flags & PF_VAR) 263284285Sjkim free_param(&j->params, p); 264284285Sjkim } 265284285Sjkim while ((wj = TAILQ_FIRST(&wild))) { 266284285Sjkim free(wj->name); 267284285Sjkim while ((p = TAILQ_FIRST(&wj->params))) 268284285Sjkim free_param(&wj->params, p); 269284285Sjkim TAILQ_REMOVE(&wild, wj, tq); 270284285Sjkim } 271284285Sjkim} 272284285Sjkim 273284285Sjkim/* 274284285Sjkim * Create a new jail record. 275284285Sjkim */ 276284285Sjkimstruct cfjail * 277284285Sjkimadd_jail(void) 278284285Sjkim{ 279284285Sjkim struct cfjail *j; 280284285Sjkim 281284285Sjkim j = emalloc(sizeof(struct cfjail)); 282284285Sjkim memset(j, 0, sizeof(struct cfjail)); 283284285Sjkim TAILQ_INIT(&j->params); 284280304Sjkim STAILQ_INIT(&j->dep[DEP_FROM]); 285280304Sjkim STAILQ_INIT(&j->dep[DEP_TO]); 286284285Sjkim j->queue = &cfjails; 287280304Sjkim TAILQ_INSERT_TAIL(&cfjails, j, tq); 288284285Sjkim return j; 289284285Sjkim} 290284285Sjkim 291284285Sjkim/* 292284285Sjkim * Add a parameter to a jail. 293280304Sjkim */ 294280304Sjkimvoid 295284285Sjkimadd_param(struct cfjail *j, const struct cfparam *p, enum intparam ipnum, 296284285Sjkim const char *value) 297284285Sjkim{ 298284285Sjkim struct cfstrings nss; 299284285Sjkim struct cfparam *dp, *np; 300284285Sjkim struct cfstring *s, *ns; 301284285Sjkim struct cfvar *v, *nv; 302284285Sjkim const char *name; 303284285Sjkim char *cs, *tname; 304306196Sjkim unsigned flags; 305306196Sjkim 306306196Sjkim if (j == NULL) { 307306196Sjkim /* Create a single anonymous jail if one doesn't yet exist. */ 308284285Sjkim j = TAILQ_LAST(&cfjails, cfjails); 309284285Sjkim if (j == NULL) 310284285Sjkim j = add_jail(); 311284285Sjkim } 312284285Sjkim TAILQ_INIT(&nss); 313284285Sjkim if (p != NULL) { 314306196Sjkim name = p->name; 315306196Sjkim flags = p->flags; 316306196Sjkim /* 317284285Sjkim * Make a copy of the parameter's string list, 318284285Sjkim * which may be freed if it's overridden later. 319284285Sjkim */ 32068651Skris TAILQ_FOREACH(s, &p->val, tq) { 321284285Sjkim ns = emalloc(sizeof(struct cfstring)); 322284285Sjkim ns->s = estrdup(s->s); 323284285Sjkim ns->len = s->len; 324284285Sjkim STAILQ_INIT(&ns->vars); 325284285Sjkim STAILQ_FOREACH(v, &s->vars, tq) { 326284285Sjkim nv = emalloc(sizeof(struct cfvar)); 327285330Sjkim nv->name = strdup(v->name); 328284285Sjkim nv->pos = v->pos; 329284285Sjkim STAILQ_INSERT_TAIL(&ns->vars, nv, tq); 330284285Sjkim } 331284285Sjkim TAILQ_INSERT_TAIL(&nss, ns, tq); 332306196Sjkim } 333306196Sjkim } else { 334284285Sjkim flags = PF_APPEND; 335306196Sjkim if (ipnum != IP__NULL) { 336284285Sjkim name = intparams[ipnum].name; 337284285Sjkim flags |= intparams[ipnum].flags; 338284285Sjkim } else if ((cs = strchr(value, '='))) { 339284285Sjkim tname = alloca(cs - value + 1); 340284285Sjkim strlcpy(tname, value, cs - value + 1); 341284285Sjkim name = tname; 34255714Skris value = cs + 1; 343284285Sjkim } else { 344284285Sjkim name = value; 345284285Sjkim value = NULL; 346284285Sjkim } 347284285Sjkim if (value != NULL) { 348284285Sjkim ns = emalloc(sizeof(struct cfstring)); 349284285Sjkim ns->s = estrdup(value); 350284285Sjkim ns->len = strlen(value); 351284285Sjkim STAILQ_INIT(&ns->vars); 352285330Sjkim TAILQ_INSERT_TAIL(&nss, ns, tq); 353284285Sjkim } 354284285Sjkim } 355284285Sjkim 356284285Sjkim /* See if this parameter has already been added. */ 357280304Sjkim if (ipnum != IP__NULL) 358284285Sjkim dp = j->intparams[ipnum]; 35968651Skris else 360280304Sjkim TAILQ_FOREACH(dp, &j->params, tq) 361280304Sjkim if (!(dp->flags & PF_CONV) && equalopts(dp->name, name)) 362280304Sjkim break; 363280304Sjkim if (dp != NULL) { 364280304Sjkim /* Found it - append or replace. */ 365280304Sjkim if (strcmp(dp->name, name)) { 366280304Sjkim free(dp->name); 367280304Sjkim dp->name = estrdup(name); 368280304Sjkim } 36968651Skris if (!(flags & PF_APPEND) || TAILQ_EMPTY(&nss)) 370280304Sjkim free_param_strings(dp); 371280304Sjkim TAILQ_CONCAT(&dp->val, &nss, tq); 372280304Sjkim dp->flags |= flags; 373280304Sjkim } else { 374280304Sjkim /* Not found - add it. */ 375280304Sjkim np = emalloc(sizeof(struct cfparam)); 376280304Sjkim np->name = estrdup(name); 37755714Skris TAILQ_INIT(&np->val); 378280304Sjkim TAILQ_CONCAT(&np->val, &nss, tq); 379280304Sjkim np->flags = flags; 380280304Sjkim np->gen = 0; 381280304Sjkim TAILQ_INSERT_TAIL(&j->params, np, tq); 382280304Sjkim if (ipnum != IP__NULL) 383280304Sjkim j->intparams[ipnum] = np; 38455714Skris else 385280304Sjkim for (ipnum = IP__NULL + 1; ipnum < IP_NPARAM; ipnum++) 386280304Sjkim if (!(intparams[ipnum].flags & PF_CONV) && 38755714Skris equalopts(name, intparams[ipnum].name)) { 388280304Sjkim j->intparams[ipnum] = np; 389280304Sjkim np->flags |= intparams[ipnum].flags; 39059191Skris break; 391280304Sjkim } 39259191Skris } 393280304Sjkim} 394238405Sjkim 395280304Sjkim/* 396280304Sjkim * Return if a boolean parameter exists and is true. 397238405Sjkim */ 398280304Sjkimint 39959191Skrisbool_param(const struct cfparam *p) 400280304Sjkim{ 401280304Sjkim const char *cs; 40259191Skris 403280304Sjkim if (p == NULL) 404280304Sjkim return 0; 40559191Skris cs = strrchr(p->name, '.'); 406280304Sjkim return !strncmp(cs ? cs + 1 : p->name, "no", 2) ^ 407280304Sjkim (TAILQ_EMPTY(&p->val) || 40855714Skris !strcasecmp(TAILQ_LAST(&p->val, cfstrings)->s, "true") || 409280304Sjkim (strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10))); 410280304Sjkim} 411280304Sjkim 412280304Sjkim/* 413109998Smarkm * Set an integer if a parameter if it exists. 414280304Sjkim */ 415280304Sjkimint 416280304Sjkimint_param(const struct cfparam *p, int *ip) 417109998Smarkm{ 418280304Sjkim if (p == NULL || TAILQ_EMPTY(&p->val)) 419280304Sjkim return 0; 420280304Sjkim *ip = strtol(TAILQ_LAST(&p->val, cfstrings)->s, NULL, 10); 421280304Sjkim return 1; 422280304Sjkim} 423280304Sjkim 424280304Sjkim/* 425160814Ssimon * Return the string value of a scalar parameter if it exists. 426167612Ssimon */ 427280304Sjkimconst char * 428280304Sjkimstring_param(const struct cfparam *p) 429280304Sjkim{ 430280304Sjkim return (p && !TAILQ_EMPTY(&p->val) 431280304Sjkim ? TAILQ_LAST(&p->val, cfstrings)->s : NULL); 432280304Sjkim} 433280304Sjkim 434167612Ssimon/* 435167612Ssimon * Check syntax and values of internal parameters. Set some internal 436280304Sjkim * parameters based on the values of others. 437280304Sjkim */ 438280304Sjkimint 439280304Sjkimcheck_intparams(struct cfjail *j) 440280304Sjkim{ 441280304Sjkim struct cfparam *p; 442280304Sjkim struct cfstring *s; 443280304Sjkim FILE *f; 444280304Sjkim const char *val; 445280304Sjkim char *cs, *ep, *ln; 446280304Sjkim size_t lnlen; 447280304Sjkim int error; 448280304Sjkim#if defined(INET) || defined(INET6) 449306196Sjkim struct addrinfo hints; 450306196Sjkim struct addrinfo *ai0, *ai; 451306196Sjkim const char *hostname; 452306196Sjkim int gicode, defif, prefix; 453280304Sjkim#endif 454280304Sjkim#ifdef INET 45555714Skris struct in_addr addr4; 456280304Sjkim int ip4ok; 457280304Sjkim char avalue4[INET_ADDRSTRLEN]; 45868651Skris#endif 45968651Skris#ifdef INET6 46068651Skris struct in6_addr addr6; 46168651Skris int ip6ok; 462280304Sjkim char avalue6[INET6_ADDRSTRLEN]; 463280304Sjkim#endif 464280304Sjkim 465280304Sjkim error = 0; 466280304Sjkim /* Check format of boolan and integer values. */ 467280304Sjkim TAILQ_FOREACH(p, &j->params, tq) { 468280304Sjkim if (!TAILQ_EMPTY(&p->val) && (p->flags & (PF_BOOL | PF_INT))) { 469280304Sjkim val = TAILQ_LAST(&p->val, cfstrings)->s; 47068651Skris if (p->flags & PF_BOOL) { 47168651Skris if (strcasecmp(val, "false") && 47268651Skris strcasecmp(val, "true") && 47368651Skris ((void)strtol(val, &ep, 10), *ep)) { 47468651Skris jail_warnx(j, 47568651Skris "%s: unknown boolean value \"%s\"", 476280304Sjkim p->name, val); 477280304Sjkim error = -1; 478280304Sjkim } 479280304Sjkim } else { 480280304Sjkim (void)strtol(val, &ep, 10); 481280304Sjkim if (ep == val || *ep) { 482280304Sjkim jail_warnx(j, 48368651Skris "%s: non-integer value \"%s\"", 484280304Sjkim p->name, val); 485280304Sjkim error = -1; 486280304Sjkim } 487280304Sjkim } 488280304Sjkim } 48968651Skris } 49068651Skris 49168651Skris#if defined(INET) || defined(INET6) 49268651Skris /* 49368651Skris * The ip_hostname parameter looks up the hostname, and adds parameters 49468651Skris * for any IP addresses it finds. 495280304Sjkim */ 496280304Sjkim if (((j->flags & JF_OP_MASK) != JF_STOP || 497280304Sjkim j->intparams[IP_INTERFACE] != NULL) && 498280304Sjkim bool_param(j->intparams[IP_IP_HOSTNAME]) && 499280304Sjkim (hostname = string_param(j->intparams[KP_HOST_HOSTNAME]))) { 500280304Sjkim j->intparams[IP_IP_HOSTNAME] = NULL; 50168651Skris /* 50268651Skris * Silently ignore unsupported address families from 503280304Sjkim * DNS lookups. 504280304Sjkim */ 505280304Sjkim#ifdef INET 50659191Skris ip4ok = feature_present("inet"); 50759191Skris#endif 508160814Ssimon#ifdef INET6 50959191Skris ip6ok = feature_present("inet6"); 510109998Smarkm#endif 511280304Sjkim if ( 51259191Skris#if defined(INET) && defined(INET6) 513280304Sjkim ip4ok || ip6ok 514280304Sjkim#elif defined(INET) 515280304Sjkim ip4ok 516280304Sjkim#elif defined(INET6) 517280304Sjkim ip6ok 518280304Sjkim#endif 519280304Sjkim ) { 520160814Ssimon /* Look up the hostname (or get the address) */ 521280304Sjkim memset(&hints, 0, sizeof(hints)); 522280304Sjkim hints.ai_socktype = SOCK_STREAM; 523280304Sjkim hints.ai_family = 524280304Sjkim#if defined(INET) && defined(INET6) 525280304Sjkim ip4ok ? (ip6ok ? PF_UNSPEC : PF_INET) : PF_INET6; 526280304Sjkim#elif defined(INET) 527280304Sjkim PF_INET; 528280304Sjkim#elif defined(INET6) 529280304Sjkim PF_INET6; 530280304Sjkim#endif 531160814Ssimon gicode = getaddrinfo(hostname, NULL, &hints, &ai0); 532280304Sjkim if (gicode != 0) { 533280304Sjkim jail_warnx(j, "host.hostname %s: %s", hostname, 534280304Sjkim gai_strerror(gicode)); 535280304Sjkim error = -1; 536280304Sjkim } else { 537280304Sjkim /* 538280304Sjkim * Convert the addresses to ASCII so jailparam 539280304Sjkim * can convert them back. Errors are not 540280304Sjkim * expected here. 541280304Sjkim */ 542280304Sjkim for (ai = ai0; ai; ai = ai->ai_next) 543280304Sjkim switch (ai->ai_family) { 544280304Sjkim#ifdef INET 545280304Sjkim case AF_INET: 546280304Sjkim memcpy(&addr4, 547160814Ssimon &((struct sockaddr_in *) 548280304Sjkim (void *)ai->ai_addr)-> 549280304Sjkim sin_addr, sizeof(addr4)); 550280304Sjkim if (inet_ntop(AF_INET, 551280304Sjkim &addr4, avalue4, 552280304Sjkim INET_ADDRSTRLEN) == NULL) 553280304Sjkim err(1, "inet_ntop"); 554280304Sjkim add_param(j, NULL, KP_IP4_ADDR, 555280304Sjkim avalue4); 556280304Sjkim break; 557280304Sjkim#endif 558280304Sjkim#ifdef INET6 559280304Sjkim case AF_INET6: 560280304Sjkim memcpy(&addr6, 561280304Sjkim &((struct sockaddr_in6 *) 562280304Sjkim (void *)ai->ai_addr)-> 563280304Sjkim sin6_addr, sizeof(addr6)); 564280304Sjkim if (inet_ntop(AF_INET6, 565280304Sjkim &addr6, avalue6, 566280304Sjkim INET6_ADDRSTRLEN) == NULL) 567280304Sjkim err(1, "inet_ntop"); 568280304Sjkim add_param(j, NULL, KP_IP6_ADDR, 569280304Sjkim avalue6); 570280304Sjkim break; 571280304Sjkim#endif 572280304Sjkim } 573280304Sjkim freeaddrinfo(ai0); 574280304Sjkim } 575280304Sjkim } 576280304Sjkim } 577280304Sjkim 578280304Sjkim /* 579280304Sjkim * IP addresses may include an interface to set that address on, 580280304Sjkim * a netmask/suffix for that address and options for ifconfig. 581280304Sjkim * These are copied to an internal command parameter and then stripped 582280304Sjkim * so they won't be passed on to jailparam_set. 583280304Sjkim */ 584280304Sjkim defif = string_param(j->intparams[IP_INTERFACE]) != NULL; 585280304Sjkim#ifdef INET 586280304Sjkim if (j->intparams[KP_IP4_ADDR] != NULL) { 587280304Sjkim TAILQ_FOREACH(s, &j->intparams[KP_IP4_ADDR]->val, tq) { 588280304Sjkim cs = strchr(s->s, '|'); 589280304Sjkim if (cs || defif) 590280304Sjkim add_param(j, NULL, IP__IP4_IFADDR, s->s); 591280304Sjkim if (cs) { 592280304Sjkim strcpy(s->s, cs + 1); 593280304Sjkim s->len -= cs + 1 - s->s; 594280304Sjkim } 595280304Sjkim if ((cs = strchr(s->s, '/'))) { 596280304Sjkim prefix = strtol(cs + 1, &ep, 10); 597280304Sjkim if (*ep == '.' 598280304Sjkim ? inet_pton(AF_INET, cs + 1, &addr4) != 1 599280304Sjkim : *ep || prefix < 0 || prefix > 32) { 600280304Sjkim jail_warnx(j, 601280304Sjkim "ip4.addr: bad netmask \"%s\"", cs); 602280304Sjkim error = -1; 603280304Sjkim } 604280304Sjkim *cs = '\0'; 605280304Sjkim s->len = cs - s->s; 606280304Sjkim } 607280304Sjkim if ((cs = strchr(s->s, ' ')) != NULL) { 608280304Sjkim *cs = '\0'; 609280304Sjkim s->len = cs - s->s; 610280304Sjkim } 611280304Sjkim } 612280304Sjkim } 613280304Sjkim#endif 614280304Sjkim#ifdef INET6 615280304Sjkim if (j->intparams[KP_IP6_ADDR] != NULL) { 616280304Sjkim TAILQ_FOREACH(s, &j->intparams[KP_IP6_ADDR]->val, tq) { 617280304Sjkim cs = strchr(s->s, '|'); 618280304Sjkim if (cs || defif) 619280304Sjkim add_param(j, NULL, IP__IP6_IFADDR, s->s); 620280304Sjkim if (cs) { 621280304Sjkim strcpy(s->s, cs + 1); 622280304Sjkim s->len -= cs + 1 - s->s; 623280304Sjkim } 624280304Sjkim if ((cs = strchr(s->s, '/'))) { 625280304Sjkim prefix = strtol(cs + 1, &ep, 10); 626280304Sjkim if (*ep || prefix < 0 || prefix > 128) { 627280304Sjkim jail_warnx(j, 628280304Sjkim "ip6.addr: bad prefixlen \"%s\"", 629280304Sjkim cs); 630280304Sjkim error = -1; 631280304Sjkim } 632280304Sjkim *cs = '\0'; 633280304Sjkim s->len = cs - s->s; 634280304Sjkim } 635280304Sjkim if ((cs = strchr(s->s, ' ')) != NULL) { 636306196Sjkim *cs = '\0'; 637306196Sjkim s->len = cs - s->s; 638306196Sjkim } 639306196Sjkim } 640306196Sjkim } 641306196Sjkim#endif 642306196Sjkim#endif 643306196Sjkim 644306196Sjkim /* 645306196Sjkim * Read mount.fstab file(s), and treat each line as its own mount 646306196Sjkim * parameter. 647306196Sjkim */ 648306196Sjkim if (j->intparams[IP_MOUNT_FSTAB] != NULL) { 649306196Sjkim TAILQ_FOREACH(s, &j->intparams[IP_MOUNT_FSTAB]->val, tq) { 650306196Sjkim if (s->len == 0) 651306196Sjkim continue; 652306196Sjkim f = fopen(s->s, "r"); 653306196Sjkim if (f == NULL) { 654306196Sjkim jail_warnx(j, "mount.fstab: %s: %s", 655306196Sjkim s->s, strerror(errno)); 656306196Sjkim error = -1; 657280304Sjkim continue; 658280304Sjkim } 659280304Sjkim while ((ln = fgetln(f, &lnlen))) { 660280304Sjkim if ((cs = memchr(ln, '#', lnlen - 1))) 661280304Sjkim lnlen = cs - ln + 1; 662280304Sjkim if (ln[lnlen - 1] == '\n' || 663280304Sjkim ln[lnlen - 1] == '#') 66468651Skris ln[lnlen - 1] = '\0'; 665280304Sjkim else { 66659191Skris cs = alloca(lnlen + 1); 66759191Skris strlcpy(cs, ln, lnlen + 1); 66859191Skris ln = cs; 669238405Sjkim } 670280304Sjkim add_param(j, NULL, IP__MOUNT_FROM_FSTAB, ln); 671280304Sjkim } 672280304Sjkim fclose(f); 673280304Sjkim } 674280304Sjkim } 675280304Sjkim if (error) 676280304Sjkim failed(j); 677280304Sjkim return error; 678280304Sjkim} 679306196Sjkim 680280304Sjkim/* 681306196Sjkim * Import parameters into libjail's binary jailparam format. 682306196Sjkim */ 683306196Sjkimint 684306196Sjkimimport_params(struct cfjail *j) 685306196Sjkim{ 686306196Sjkim struct cfparam *p; 687306196Sjkim struct cfstring *s, *ts; 688306196Sjkim struct jailparam *jp; 689306196Sjkim char *value, *cs; 690306196Sjkim size_t vallen; 691306196Sjkim int error; 692306196Sjkim 693306196Sjkim error = 0; 694306196Sjkim j->njp = 0; 695306196Sjkim TAILQ_FOREACH(p, &j->params, tq) 696306196Sjkim if (!(p->flags & PF_INTERNAL)) 697306196Sjkim j->njp++; 698306196Sjkim j->jp = jp = emalloc(j->njp * sizeof(struct jailparam)); 699306196Sjkim TAILQ_FOREACH(p, &j->params, tq) { 700306196Sjkim if (p->flags & PF_INTERNAL) 701306196Sjkim continue; 702306196Sjkim if (jailparam_init(jp, p->name) < 0) { 703306196Sjkim error = -1; 704306196Sjkim jail_warnx(j, "%s", jail_errmsg); 705306196Sjkim jp++; 706306196Sjkim continue; 707306196Sjkim } 708306196Sjkim if (TAILQ_EMPTY(&p->val)) 709306196Sjkim value = NULL; 710306196Sjkim else if (!jp->jp_elemlen || 711306196Sjkim !TAILQ_NEXT(TAILQ_FIRST(&p->val), tq)) { 712306196Sjkim /* 713306196Sjkim * Scalar parameters silently discard multiple (array) 714306196Sjkim * values, keeping only the last value added. This 715306196Sjkim * lets values added from the command line append to 716306196Sjkim * arrays wthout pre-checking the type. 717306196Sjkim */ 718306196Sjkim value = TAILQ_LAST(&p->val, cfstrings)->s; 719306196Sjkim } else { 720306196Sjkim /* 721306196Sjkim * Convert arrays into comma-separated strings, which 722306196Sjkim * jailparam_import will then convert back into arrays. 723306196Sjkim */ 724306196Sjkim vallen = 0; 725306196Sjkim TAILQ_FOREACH(s, &p->val, tq) 726306196Sjkim vallen += s->len + 1; 727306196Sjkim value = alloca(vallen); 728306196Sjkim cs = value; 729306196Sjkim TAILQ_FOREACH_SAFE(s, &p->val, tq, ts) { 730306196Sjkim memcpy(cs, s->s, s->len); 731306196Sjkim cs += s->len + 1; 732306196Sjkim cs[-1] = ','; 733306196Sjkim } 734306196Sjkim value[vallen - 1] = '\0'; 735306196Sjkim } 736306196Sjkim if (jailparam_import(jp, value) < 0) { 737306196Sjkim error = -1; 738306196Sjkim jail_warnx(j, "%s", jail_errmsg); 739306196Sjkim } 740306196Sjkim jp++; 741306196Sjkim } 742306196Sjkim if (error) { 743306196Sjkim jailparam_free(j->jp, j->njp); 744306196Sjkim free(j->jp); 745306196Sjkim j->jp = NULL; 746306196Sjkim failed(j); 747306196Sjkim } 748306196Sjkim return error; 749306196Sjkim} 750306196Sjkim 751306196Sjkim/* 752306196Sjkim * Check if options are equal (with or without the "no" prefix). 753306196Sjkim */ 754306196Sjkimint 755280304Sjkimequalopts(const char *opt1, const char *opt2) 756280304Sjkim{ 757280304Sjkim char *p; 758280304Sjkim 759280304Sjkim /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 760280304Sjkim if (strcmp(opt1, opt2) == 0) 761280304Sjkim return (1); 762280304Sjkim /* "noopt" vs. "opt" */ 763280304Sjkim if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 764306196Sjkim return (1); 765306196Sjkim /* "opt" vs. "noopt" */ 766306196Sjkim if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 767306196Sjkim return (1); 768280304Sjkim while ((p = strchr(opt1, '.')) != NULL && 769306196Sjkim !strncmp(opt1, opt2, ++p - opt1)) { 770306196Sjkim opt2 += p - opt1; 771306196Sjkim opt1 = p; 772280304Sjkim /* "foo.noopt" vs. "foo.opt" */ 773280304Sjkim if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 774280304Sjkim return (1); 775280304Sjkim /* "foo.opt" vs. "foo.noopt" */ 776306196Sjkim if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 777280304Sjkim return (1); 778280304Sjkim } 779280304Sjkim return (0); 780280304Sjkim} 781280304Sjkim 782280304Sjkim/* 783238405Sjkim * See if a jail name matches a wildcard. 78459191Skris */ 78559191Skrisint 786109998Smarkmwild_jail_match(const char *jname, const char *wname) 787280304Sjkim{ 78859191Skris const char *jc, *jd, *wc, *wd; 789280304Sjkim 790280304Sjkim /* 791280304Sjkim * A non-final "*" component in the wild name matches a single jail 792280304Sjkim * component, and a final "*" matches one or more jail components. 79359191Skris */ 794280304Sjkim for (jc = jname, wc = wname; 795280304Sjkim (jd = strchr(jc, '.')) && (wd = strchr(wc, '.')); 796280304Sjkim jc = jd + 1, wc = wd + 1) 797280304Sjkim if (strncmp(jc, wc, jd - jc + 1) && strncmp(wc, "*.", 2)) 798280304Sjkim return 0; 799280304Sjkim return (!strcmp(jc, wc) || !strcmp(wc, "*")); 800280304Sjkim} 801280304Sjkim 802280304Sjkim/* 803280304Sjkim * Return if a jail name is a wildcard. 804280304Sjkim */ 805280304Sjkimint 806280304Sjkimwild_jail_name(const char *wname) 80759191Skris{ 80859191Skris const char *wc; 80959191Skris 810109998Smarkm for (wc = strchr(wname, '*'); wc; wc = strchr(wc + 1, '*')) 811280304Sjkim if ((wc == wname || wc[-1] == '.') && 812280304Sjkim (wc[1] == '\0' || wc[1] == '.')) 813280304Sjkim return 1; 814280304Sjkim return 0; 815280304Sjkim} 816280304Sjkim 817280304Sjkim/* 818280304Sjkim * Free a parameter record and all its strings and variables. 819280304Sjkim */ 820280304Sjkimstatic void 821280304Sjkimfree_param(struct cfparams *pp, struct cfparam *p) 822280304Sjkim{ 823280304Sjkim free(p->name); 824280304Sjkim free_param_strings(p); 825280304Sjkim TAILQ_REMOVE(pp, p, tq); 826280304Sjkim free(p); 827280304Sjkim} 828280304Sjkim 829280304Sjkimstatic void 830280304Sjkimfree_param_strings(struct cfparam *p) 831109998Smarkm{ 832109998Smarkm struct cfstring *s; 833280304Sjkim struct cfvar *v; 834280304Sjkim 835280304Sjkim while ((s = TAILQ_FIRST(&p->val))) { 836280304Sjkim free(s->s); 837280304Sjkim while ((v = STAILQ_FIRST(&s->vars))) { 838280304Sjkim free(v->name); 839280304Sjkim STAILQ_REMOVE_HEAD(&s->vars, tq); 840280304Sjkim free(v); 841280304Sjkim } 842280304Sjkim TAILQ_REMOVE(&p->val, s, tq); 843280304Sjkim free(s); 844280304Sjkim } 845280304Sjkim} 846280304Sjkim