Page MenuHomeFreeBSD

D40188.id122676.diff
No OneTemporary

D40188.id122676.diff

diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c
--- a/usr.sbin/jail/config.c
+++ b/usr.sbin/jail/config.c
@@ -38,6 +38,7 @@
#include <netinet/in.h>
#include <err.h>
+#include <glob.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
@@ -51,15 +52,16 @@
unsigned flags;
};
-extern FILE *yyin;
-extern int yynerrs;
-
-extern int yyparse(void);
+extern int yylex_init_extra(struct cflex *extra, void *scanner);
+extern int yylex_destroy(void *scanner);
+extern int yyparse(void *scanner);
+extern int yyset_in(FILE *fp, void *scanner);
struct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
+static void parse_config(const char *fname, int is_stdin,
+ struct cfjail *injail);
static void free_param(struct cfparams *pp, struct cfparam *p);
-static void free_param_strings(struct cfparam *p);
static const struct ipspec intparams[] = {
[IP_ALLOW_DYING] = {"allow.dying", PF_INTERNAL | PF_BOOL},
@@ -124,10 +126,11 @@
};
/*
- * Parse the jail configuration file.
+ * Parse a jail config file, either initially from main, or from
+ * an include within another config file.
*/
void
-load_config(void)
+load_config(const char *cfname)
{
struct cfjails wild;
struct cfparams opp;
@@ -138,16 +141,7 @@
char *ep;
int did_self, jseq, pgen;
- if (!strcmp(cfname, "-")) {
- cfname = "STDIN";
- yyin = stdin;
- } else {
- yyin = fopen(cfname, "r");
- if (!yyin)
- err(1, "%s", cfname);
- }
- if (yyparse() || yynerrs)
- exit(1);
+ parse_config(cfname, !strcmp(cfname, "-"), NULL);
/* Separate the wildcard jails out from the actual jails. */
jseq = 0;
@@ -274,6 +268,64 @@
}
}
+void
+include_config(const char *cfname, struct cflex *cflex, int from_stdin)
+{
+ static unsigned int depth;
+ glob_t g;
+ const char *slash;
+ char *fullpath = NULL;
+
+ /* Basic sanity check for include loops. */
+ if (++depth > 32)
+ errx(1, "maximum include depth exceeded");
+ /* Base relative pathnames on the current config file. */
+ if (!from_stdin && cfname[0] != '/' &&
+ (slash = strrchr(cflex->cfname, '/')) != NULL) {
+ size_t dirlen = (slash - cflex->cfname) + 1;
+
+ fullpath = emalloc(dirlen + strlen(cfname) + 1);
+ strncpy(fullpath, cflex->cfname, dirlen);
+ strcpy(fullpath + dirlen, cfname);
+ cfname = fullpath;
+ }
+ /*
+ * Check if the include statement had a filename glob.
+ * Globbing doesn't need to catch any files, but a non-glob
+ * file needs to exist (enforced by parse_config).
+ */
+ if (glob(cfname, GLOB_NOCHECK, NULL, &g) != 0)
+ errx(1, "%s: filename glob failed", cfname);
+ if (g.gl_flags & GLOB_MAGCHAR) {
+ for (size_t gi = 0; gi < g.gl_matchc; gi++)
+ parse_config(g.gl_pathv[gi], 0, cflex->injail);
+ } else
+ parse_config(cfname, 0, cflex->injail);
+ if (fullpath)
+ free(fullpath);
+ --depth;
+}
+
+static void
+parse_config(const char *cfname, int is_stdin, struct cfjail *injail)
+{
+ struct cflex cflex = {.cfname = cfname, .injail = injail, .error = 0};
+ void *scanner;
+
+ yylex_init_extra(&cflex, &scanner);
+ if (is_stdin) {
+ yyset_in(stdin, scanner);
+ } else {
+ FILE *cfp = fopen(cfname, "r");
+ if (!cfp)
+ err(1, "%s", cfname);
+ yyset_in(cfp, scanner);
+ }
+ if (yyparse(scanner) || cflex.error)
+ exit(1);
+ yylex_destroy(scanner);
+}
+
/*
* Create a new jail record.
*/
@@ -836,7 +888,7 @@
free(p);
}
-static void
+void
free_param_strings(struct cfparam *p)
{
struct cfstring *s;
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c
--- a/usr.sbin/jail/jail.c
+++ b/usr.sbin/jail/jail.c
@@ -56,7 +56,6 @@
int rev;
};
-const char *cfname;
int iflag;
int note_remove;
int verbose;
@@ -137,6 +136,7 @@
struct stat st;
FILE *jfp;
struct cfjail *j;
+ const char *cfname;
char *JidFile;
size_t sysvallen;
unsigned op, pi;
@@ -287,7 +287,7 @@
} else if (op == JF_STOP || op == JF_SHOW) {
/* Just print list of all configured non-wildcard jails */
if (op == JF_SHOW) {
- load_config();
+ load_config(cfname);
show_jails();
exit(0);
}
@@ -300,7 +300,7 @@
usage();
if ((docf = !Rflag &&
(!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
- load_config();
+ load_config(cfname);
note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
/* Single jail specified on the command line */
@@ -348,7 +348,7 @@
/* From the config file, perhaps with a specified jail */
if (Rflag || !docf)
usage();
- load_config();
+ load_config(cfname);
}
/* Find out which jails will be run. */
diff --git a/usr.sbin/jail/jail.conf.5 b/usr.sbin/jail/jail.conf.5
--- a/usr.sbin/jail/jail.conf.5
+++ b/usr.sbin/jail/jail.conf.5
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 8, 2022
+.Dd May 20, 2023
.Dt JAIL.CONF 5
.Os
.Sh NAME
@@ -163,6 +163,14 @@
.Dq foo.bar
and
.Dq foo.bar.baz .
+.Ss Includes
+A line of the form
+.Bd -literal -offset ident
+.include "filename";
+.Ed
+.Pp
+will include another file in the configuration. The filename must be
+a literal string, and cannot contain variable expansions.
.Ss Comments
The configuration file may contain comments in the common C, C++, and
shell formats:
@@ -212,6 +220,13 @@
mount.nodevfs;
persist; // Required because there are no processes
}
+
+# Include configurations from standard locations.
+.include "/etc/jail.conf.d/*.conf";
+.include "/etc/jail.*.conf";
+.include "/usr/local/etc/jail[.]conf";
+.include "/usr/local/etc/jail.conf.d/*.conf";
+.include "/usr/local/etc/jail.*.conf";
.Ed
.Sh SEE ALSO
.Xr jail_set 2 ,
diff --git a/usr.sbin/jail/jaillex.l b/usr.sbin/jail/jaillex.l
--- a/usr.sbin/jail/jaillex.l
+++ b/usr.sbin/jail/jaillex.l
@@ -38,19 +38,22 @@
#include "jailp.h"
#include "y.tab.h"
-extern int yynerrs;
+#define YY_DECL int yylex(YYSTYPE *yylval, yyscan_t yyscanner)
+#define YY_EXTRA_TYPE struct cflex*
-static ssize_t text2lval(size_t triml, size_t trimr, int tovar);
+extern YY_DECL;
static int instr;
-static int lineno = 1;
-#define YY_DECL int yylex(void)
+static ssize_t text2lval(size_t triml, size_t trimr, int tovar,
+ YYSTYPE *yylval, yyscan_t scanner);
%}
%option noyywrap
%option noinput
%option nounput
+%option reentrant
+%option yylineno
%start _ DQ
@@ -60,18 +63,8 @@
<_>[ \t]+ instr = 0;
<_>#.* ;
<_>\/\/.* ;
-<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ {
- const char *s;
-
- for (s = yytext; s < yytext + yyleng; s++)
- if (*s == '\n')
- lineno++;
- instr = 0;
- }
-<_>\n {
- lineno++;
- instr = 0;
- }
+<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ instr = 0;
+<_>\n instr = 0;
/* Reserved tokens */
<_>\+= {
@@ -87,13 +80,13 @@
<_,DQ>[A-Za-z0-9_!%&()\-.:<>?@\[\]^`|~]+ |
<_,DQ>\\(.|\n|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}) |
<_,DQ>[$*+/\\] {
- (void)text2lval(0, 0, 0);
+ (void)text2lval(0, 0, 0, yylval, yyscanner);
return instr ? STR1 : (instr = 1, STR);
}
/* Single and double quoted strings */
<_>'([^\'\\]|\\(.|\n))*' {
- (void)text2lval(1, 1, 0);
+ (void)text2lval(1, 1, 0, yylval, yyscanner);
return instr ? STR1 : (instr = 1, STR);
}
<_>\"([^"\\]|\\(.|\n))*\" |
@@ -102,7 +95,8 @@
ssize_t atvar;
skip = yytext[0] == '"' ? 1 : 0;
- atvar = text2lval(skip, 1, 1);
+ atvar = text2lval(skip, 1, 1, yylval,
+ yyscanner);
if (atvar < 0)
BEGIN _;
else {
@@ -120,32 +114,32 @@
/* Variables, single-word or bracketed */
<_,DQ>$[A-Za-z_][A-Za-z_0-9]* {
- (void)text2lval(1, 0, 0);
+ (void)text2lval(1, 0, 0, yylval, yyscanner);
return instr ? VAR1 : (instr = 1, VAR);
}
<_>$\{([^\n{}]|\\(.|\n))*\} |
<DQ>$\{([^\n\"{}]|\\(.|\n))*\} {
- (void)text2lval(2, 1, 0);
+ (void)text2lval(2, 1, 0, yylval, yyscanner);
return instr ? VAR1 : (instr = 1, VAR);
}
/* Partially formed bits worth complaining about */
<_>\/\*([^*]|(\*+([^*\/])))*\** {
warnx("%s line %d: unterminated comment",
- cfname, lineno);
- yynerrs++;
+ yyextra->cfname, yylineno);
+ yyextra->error = 1;
}
<_>'([^\n'\\]|\\.)* |
<_>\"([^\n\"\\]|\\.)* {
warnx("%s line %d: unterminated string",
- cfname, lineno);
- yynerrs++;
+ yyextra->cfname, yylineno);
+ yyextra->error = 1;
}
<_>$\{([^\n{}]|\\.)* |
<DQ>$\{([^\n\"{}]|\\.)* {
warnx("%s line %d: unterminated variable",
- cfname, lineno);
- yynerrs++;
+ yyextra->cfname, yylineno);
+ yyextra->error = 1;
}
/* A hack because "<0>" rules aren't allowed */
@@ -157,28 +151,19 @@
%%
-void
-yyerror(const char *s)
-{
- if (!yytext)
- warnx("%s line %d: %s", cfname, lineno, s);
- else if (!yytext[0])
- warnx("%s: unexpected EOF", cfname);
- else
- warnx("%s line %d: %s: %s", cfname, lineno, yytext, s);
-}
-
/*
* Copy string from yytext to yylval, handling backslash escapes,
* and optionally stopping at the beginning of a variable.
*/
static ssize_t
-text2lval(size_t triml, size_t trimr, int tovar)
+text2lval(size_t triml, size_t trimr, int tovar, YYSTYPE *yylval,
+ yyscan_t scanner)
{
char *d;
const char *s, *se;
- yylval.cs = d = emalloc(yyleng - trimr - triml + 1);
+ struct yyguts_t *yyg = scanner;
+ yylval->cs = d = emalloc(yyleng - trimr - triml + 1);
se = yytext + (yyleng - trimr);
for (s = yytext + triml; s < se; s++, d++) {
if (*s != '\\') {
@@ -186,8 +171,6 @@
*d = '\0';
return s - yytext;
}
- if (*s == '\n')
- lineno++;
*d = *s;
continue;
}
@@ -209,8 +192,8 @@
case 'r': *d = '\r'; break;
case 't': *d = '\t'; break;
case 'v': *d = '\v'; break;
- case '\n': d--; lineno++; break;
default: *d = *s; break;
+ case '\n': d--; break;
case 'x':
*d = 0;
if (s + 1 >= se)
diff --git a/usr.sbin/jail/jailp.h b/usr.sbin/jail/jailp.h
--- a/usr.sbin/jail/jailp.h
+++ b/usr.sbin/jail/jailp.h
@@ -45,15 +45,16 @@
#define DF_LIGHT 0x02 /* Implied dependency on jail existence only */
#define DF_NOFAIL 0x04 /* Don't propagate failed jails */
-#define PF_VAR 0x01 /* This is a variable, not a true parameter */
-#define PF_APPEND 0x02 /* Append to existing parameter list */
-#define PF_BAD 0x04 /* Unable to resolve parameter value */
-#define PF_INTERNAL 0x08 /* Internal parameter, not passed to kernel */
-#define PF_BOOL 0x10 /* Boolean parameter */
-#define PF_INT 0x20 /* Integer parameter */
-#define PF_CONV 0x40 /* Parameter duplicated in converted form */
-#define PF_REV 0x80 /* Run commands in reverse order on stopping */
-#define PF_IMMUTABLE 0x100 /* Immutable parameter */
+#define PF_VAR 0x0001 /* This is a variable, not a true parameter */
+#define PF_APPEND 0x0002 /* Append to existing parameter list */
+#define PF_BAD 0x0004 /* Unable to resolve parameter value */
+#define PF_INTERNAL 0x0008 /* Internal parameter, not passed to kernel */
+#define PF_BOOL 0x0010 /* Boolean parameter */
+#define PF_INT 0x0020 /* Integer parameter */
+#define PF_CONV 0x0040 /* Parameter duplicated in converted form */
+#define PF_REV 0x0080 /* Run commands in reverse order on stopping */
+#define PF_IMMUTABLE 0x0100 /* Immutable parameter */
+#define PF_NAMEVAL 0x0200 /* Parameter is in "name value" form */
#define JF_START 0x0001 /* -c */
#define JF_SET 0x0002 /* -m */
@@ -176,6 +177,7 @@
struct cfparams params;
struct cfdepends dep[2];
struct cfjails *queue;
+ struct cfjail *cfparent;
struct cfparam *intparams[IP_NPARAM];
struct cfstring *comstring;
struct jailparam *jp;
@@ -196,6 +198,12 @@
unsigned flags;
};
+struct cflex {
+ const char *cfname;
+ struct cfjail *injail;
+ int error;
+};
+
extern void *emalloc(size_t);
extern void *erealloc(void *, size_t);
extern char *estrdup(const char *);
@@ -208,7 +216,9 @@
extern int finish_command(struct cfjail *j);
extern struct cfjail *next_proc(int nonblock);
-extern void load_config(void);
+extern void load_config(const char *cfname);
+extern void include_config(const char *cfname, struct cflex *cflex,
+ int from_stdin);
extern struct cfjail *add_jail(void);
extern void add_param(struct cfjail *j, const struct cfparam *p,
enum intparam ipnum, const char *value);
@@ -220,6 +230,7 @@
extern int equalopts(const char *opt1, const char *opt2);
extern int wild_jail_name(const char *wname);
extern int wild_jail_match(const char *jname, const char *wname);
+extern void free_param_strings(struct cfparam *p);
extern void dep_setup(int docf);
extern int dep_check(struct cfjail *j);
@@ -231,13 +242,9 @@
extern void requeue(struct cfjail *j, struct cfjails *queue);
extern void requeue_head(struct cfjail *j, struct cfjails *queue);
-extern void yyerror(const char *);
-extern int yylex(void);
-
extern struct cfjails cfjails;
extern struct cfjails ready;
extern struct cfjails depend;
-extern const char *cfname;
extern int iflag;
extern int note_remove;
extern int paralimit;
diff --git a/usr.sbin/jail/jailparse.y b/usr.sbin/jail/jailparse.y
--- a/usr.sbin/jail/jailparse.y
+++ b/usr.sbin/jail/jailparse.y
@@ -30,6 +30,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <err.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -52,12 +54,17 @@
%token PLEQ
%token <cs> STR STR1 VAR VAR1
-%type <j> jail
+%type <j> jail jail_name
%type <pp> param_l
%type <p> param name
%type <ss> value
%type <s> string
+%pure-parser
+
+%lex-param { void *scanner }
+%parse-param { void *scanner }
+
%%
/*
@@ -70,35 +77,63 @@
;
| conf param ';'
{
- struct cfjail *j;
-
- j = TAILQ_LAST(&cfjails, cfjails);
- if (!j || strcmp(j->name, "*")) {
- j = add_jail();
- j->name = estrdup("*");
+ if (!special_param($2, scanner)) {
+ struct cfjail *j = yyget_extra(scanner)->injail;
+ if (!j)
+ {
+ j = TAILQ_LAST(&cfjails, cfjails);
+ if (!j || strcmp(j->name, "*")) {
+ j = add_jail();
+ j->name = estrdup("*");
+ }
+ }
+ TAILQ_INSERT_TAIL(&j->params, $2, tq);
}
- TAILQ_INSERT_TAIL(&j->params, $2, tq);
}
| conf ';'
-jail : STR '{' param_l '}'
+/*
+ * Allow nested jail definitions by prepending the outer jail name to
+ * the inner one.
+ */
+jail : jail_name '{' param_l '}'
{
- $$ = add_jail();
- $$->name = $1;
+ $$ = $1;
TAILQ_CONCAT(&$$->params, $3, tq);
free($3);
+ yyget_extra(scanner)->injail = $1->cfparent;
}
;
+jail_name : STR
+ {
+ $$ = add_jail();
+ $$->cfparent = yyget_extra(scanner)->injail;
+ yyget_extra(scanner)->injail = $$;
+ if ($$->cfparent != NULL) {
+ size_t parentlen = strlen($$->cfparent->name);
+ $$->name = emalloc(parentlen + strlen($1) + 2);
+ strcpy($$->name, $$->cfparent->name);
+ $$->name[parentlen++] = '.';
+ strcpy($$->name + parentlen, $1);
+ free($1);
+ } else
+ $$->name = $1;
+ }
+
param_l :
{
$$ = emalloc(sizeof(struct cfparams));
TAILQ_INIT($$);
}
+ | param_l jail
+ ;
| param_l param ';'
{
- $$ = $1;
- TAILQ_INSERT_TAIL($$, $2, tq);
+ if (!special_param($2, scanner)) {
+ $$ = $1;
+ TAILQ_INSERT_TAIL($$, $2, tq);
+ }
}
| param_l ';'
;
@@ -128,10 +163,12 @@
{
$$ = $1;
TAILQ_CONCAT(&$$->val, $2, tq);
+ $$->flags |= PF_NAMEVAL;
free($2);
}
| error
{
+ yyget_extra(scanner)->error = 1;
}
;
@@ -216,3 +253,52 @@
;
%%
+
+extern int YYLEX_DECL();
+
+extern struct cflex *yyget_extra(void *scanner);
+extern FILE *yyget_in(void *scanner);
+extern int yyget_lineno(void *scanner);
+extern char *yyget_text(void *scanner);
+
+static void
+YYERROR_DECL()
+{
+ if (!yyget_text(scanner))
+ warnx("%s line %d: %s",
+ yyget_extra(scanner)->cfname, yyget_lineno(scanner), s);
+ else if (!yyget_text(scanner)[0])
+ warnx("%s: unexpected EOF",
+ yyget_extra(scanner)->cfname);
+ else
+ warnx("%s line %d: %s: %s",
+ yyget_extra(scanner)->cfname, yyget_lineno(scanner),
+ yyget_text(scanner), s);
+}
+
+/* Handle special parameters (i.e. the include directive).
+ * Return true if the parameter was specially handled.
+ */
+static int
+special_param(struct cfparam *p, void *scanner)
+{
+ if ((p->flags & (PF_VAR | PF_NAMEVAL)) != PF_NAMEVAL
+ || strcmp(p->name, ".include"))
+ return 0;
+ struct cfstring *s;
+ TAILQ_FOREACH(s, &p->val, tq) {
+ if (STAILQ_EMPTY(&s->vars))
+ include_config(s->s, yyget_extra(scanner),
+ yyget_in(scanner) == stdin);
+ else {
+ warnx("%s line %d: "
+ "variables not permitted in '.include' filename",
+ yyget_extra(scanner)->cfname,
+ yyget_lineno(scanner));
+ yyget_extra(scanner)->error = 1;
+ }
+ }
+ free_param_strings(p);
+ free(p);
+ return 1;
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 29, 4:59 PM (18 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17843277
Default Alt Text
D40188.id122676.diff (16 KB)

Event Timeline