aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie Gritton <jamie@FreeBSD.org>2023-06-07 00:19:12 +0000
committerJamie Gritton <jamie@FreeBSD.org>2023-06-07 00:19:12 +0000
commite82a62943529d1a7c1fcec39aec13eba69c671d6 (patch)
treeb800bdb7b0b9cf836f759ace9e5478678204f5b7
parenteb5bfdd06565b38939079bf29265264efb1e21c0 (diff)
downloadsrc-e82a62943529.tar.gz
src-e82a62943529.zip
jail: add ".include" directive to jail.conf
Jail config files can now include literal filenames and file globs. They can not (yet) include files based on variables/parameters.
-rw-r--r--usr.sbin/jail/config.c86
-rw-r--r--usr.sbin/jail/jail.conf.517
-rw-r--r--usr.sbin/jail/jailp.h27
-rw-r--r--usr.sbin/jail/jailparse.y47
4 files changed, 137 insertions, 40 deletions
diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c
index 52e1cbf05c28..35f40c123042 100644
--- a/usr.sbin/jail/config.c
+++ b/usr.sbin/jail/config.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <err.h>
+#include <glob.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,6 +47,8 @@ __FBSDID("$FreeBSD$");
#include "jailp.h"
+#define MAX_INCLUDE_DEPTH 32
+
struct ipspec {
const char *name;
unsigned flags;
@@ -58,8 +61,8 @@ 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);
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},
@@ -135,26 +138,10 @@ load_config(const char *cfname)
struct cfparam *p, *vp, *tp;
struct cfstring *s, *vs, *ns;
struct cfvar *v, *vv;
- struct cflex cflex;
char *ep;
- void *scanner;
int did_self, jseq, pgen;
- cflex.cfname = cfname;
- cflex.error = 0;
- yylex_init_extra(&cflex, &scanner);
- if (!strcmp(cfname, "-")) {
- cflex.cfname = "STDIN";
- yyset_in(stdin, scanner);
- } else {
- FILE *yfp = fopen(cfname, "r");
- if (!yfp)
- err(1, "%s", cfname);
- yyset_in(yfp, scanner);
- }
- if (yyparse(scanner) || cflex.error)
- exit(1);
- yylex_destroy(scanner);
+ parse_config(cfname, !strcmp(cfname, "-"));
/* Separate the wildcard jails out from the actual jails. */
jseq = 0;
@@ -281,6 +268,67 @@ load_config(const char *cfname)
}
}
+void
+include_config(void *scanner, const char *cfname)
+{
+ static unsigned int depth;
+ glob_t g = {0};
+ const char *slash;
+ char *fullpath = NULL;
+
+ /* Simple sanity check for include loops. */
+ if (++depth > MAX_INCLUDE_DEPTH)
+ errx(1, "maximum include depth exceeded");
+ /* Base relative pathnames on the current config file. */
+ if (yyget_in(scanner) != stdin && cfname[0] != '/') {
+ const char *outer_cfname = yyget_extra(scanner)->cfname;
+ if ((slash = strrchr(outer_cfname, '/')) != NULL) {
+ size_t dirlen = (slash - outer_cfname) + 1;
+
+ fullpath = emalloc(dirlen + strlen(cfname) + 1);
+ strncpy(fullpath, outer_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);
+ } else
+ parse_config(cfname, 0);
+ if (fullpath)
+ free(fullpath);
+ --depth;
+}
+
+static void
+parse_config(const char *cfname, int is_stdin)
+{
+ struct cflex cflex = {.cfname = cfname, .error = 0};
+ void *scanner;
+
+ yylex_init_extra(&cflex, &scanner);
+ if (is_stdin) {
+ cflex.cfname = "STDIN";
+ yyset_in(stdin, scanner);
+ } else {
+ FILE *yfp = fopen(cfname, "r");
+ if (!yfp)
+ err(1, "%s", cfname);
+ yyset_in(yfp, scanner);
+ }
+ if (yyparse(scanner) || cflex.error)
+ exit(1);
+ yylex_destroy(scanner);
+}
+
/*
* Create a new jail record.
*/
@@ -843,7 +891,7 @@ free_param(struct cfparams *pp, struct cfparam *p)
free(p);
}
-static void
+void
free_param_strings(struct cfparam *p)
{
struct cfstring *s;
diff --git a/usr.sbin/jail/jail.conf.5 b/usr.sbin/jail/jail.conf.5
index 67277e6d78ce..a6990c081a3b 100644
--- a/usr.sbin/jail/jail.conf.5
+++ b/usr.sbin/jail/jail.conf.5
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 8, 2022
+.Dd Jun 3, 2023
.Dt JAIL.CONF 5
.Os
.Sh NAME
@@ -163,6 +163,14 @@ would apply to jails with names like
.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 @@ bar {
mount.nodevfs;
persist; // Required because there are no processes
}
+
+# Include configurations from standard locations.
+\[char46]include "/etc/jail.conf.d/*.conf";
+\[char46]include "/etc/jail.*.conf";
+\[char46]include "/usr/local/etc/jail[.]conf";
+\[char46]include "/usr/local/etc/jail.conf.d/*.conf";
+\[char46]include "/usr/local/etc/jail.*.conf";
.Ed
.Sh SEE ALSO
.Xr jail_set 2 ,
diff --git a/usr.sbin/jail/jailp.h b/usr.sbin/jail/jailp.h
index 6ea4e3ec09a3..cd97063507c8 100644
--- a/usr.sbin/jail/jailp.h
+++ b/usr.sbin/jail/jailp.h
@@ -35,6 +35,7 @@
#include <sys/time.h>
#include <jail.h>
+#include <stdio.h>
#define CONF_FILE "/etc/jail.conf"
@@ -45,15 +46,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 */
@@ -215,6 +217,7 @@ extern int finish_command(struct cfjail *j);
extern struct cfjail *next_proc(int nonblock);
extern void load_config(const char *cfname);
+extern void include_config(void *scanner, const char *cfname);
extern struct cfjail *add_jail(void);
extern void add_param(struct cfjail *j, const struct cfparam *p,
enum intparam ipnum, const char *value);
@@ -226,6 +229,7 @@ extern int import_params(struct cfjail *j);
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);
@@ -237,6 +241,11 @@ extern int start_state(const char *target, int docf, unsigned state,
extern void requeue(struct cfjail *j, struct cfjails *queue);
extern void requeue_head(struct cfjail *j, struct cfjails *queue);
+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);
+
extern struct cfjails cfjails;
extern struct cfjails ready;
extern struct cfjails depend;
diff --git a/usr.sbin/jail/jailparse.y b/usr.sbin/jail/jailparse.y
index ccc311a76223..e4f2310c7fb4 100644
--- a/usr.sbin/jail/jailparse.y
+++ b/usr.sbin/jail/jailparse.y
@@ -73,16 +73,18 @@ conf :
| conf jail
| conf param ';'
{
- struct cfjail *j = current_jail;
+ if (!special_param($2, scanner)) {
+ struct cfjail *j = current_jail;
- if (j == NULL) {
- if (global_jail == NULL) {
- global_jail = add_jail();
- global_jail->name = estrdup("*");
+ if (j == NULL) {
+ if (global_jail == NULL) {
+ global_jail = add_jail();
+ global_jail->name = estrdup("*");
+ }
+ j = global_jail;
}
- j = global_jail;
+ TAILQ_INSERT_TAIL(&j->params, $2, tq);
}
- TAILQ_INSERT_TAIL(&j->params, $2, tq);
}
| conf ';'
;
@@ -141,6 +143,7 @@ param : name
{
$$ = $1;
TAILQ_CONCAT(&$$->val, $2, tq);
+ $$->flags |= PF_NAMEVAL;
free($2);
}
| error
@@ -230,10 +233,6 @@ string : STR
extern int YYLEX_DECL();
-extern struct cflex *yyget_extra(void *scanner);
-extern int yyget_lineno(void *scanner);
-extern char *yyget_text(void *scanner);
-
static void
YYERROR_DECL()
{
@@ -248,3 +247,29 @@ YYERROR_DECL()
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_APPEND | 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(scanner, s->s);
+ 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;
+}