The branch main has been updated by jamie:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=e82a62943529d1a7c1fcec39aec13eba69c671d6

commit e82a62943529d1a7c1fcec39aec13eba69c671d6
Author:     Jamie Gritton <ja...@freebsd.org>
AuthorDate: 2023-06-07 00:19:12 +0000
Commit:     Jamie Gritton <ja...@freebsd.org>
CommitDate: 2023-06-07 00:19:12 +0000

    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.
---
 usr.sbin/jail/config.c    | 86 ++++++++++++++++++++++++++++++++++++-----------
 usr.sbin/jail/jail.conf.5 | 17 +++++++++-
 usr.sbin/jail/jailp.h     | 27 ++++++++++-----
 usr.sbin/jail/jailparse.y | 47 ++++++++++++++++++++------
 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;
+}

Reply via email to