Bugzilla – Attachment 3350 Details for
Bug 2468
Option to include external files to sshd_config
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
revised patch
sshd_config_include.diff (text/plain), 16.38 KB, created by
Damien Miller
on 2020-01-24 15:52:38 AEDT
(
hide
)
Description:
revised patch
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2020-01-24 15:52:38 AEDT
Size:
16.38 KB
patch
obsolete
>commit 97dc96f22e47314f9142020e4abf2980c11b0c4a >Author: Damien Miller <djm@mindrot.org> >Date: Fri Jan 24 15:51:01 2020 +1100 > > sshd_config Include support > > Based on patch by Jakub Jelen with some rework by myself. > >diff --git a/auth.c b/auth.c >index 18bbfc8..3d72bb9 100644 >--- a/auth.c >+++ b/auth.c >@@ -67,6 +67,7 @@ > > /* import */ > extern ServerOptions options; >+extern struct include_list includes; > extern int use_privsep; > extern struct sshauthopt *auth_opts; > >@@ -477,7 +478,7 @@ getpwnamallow(struct ssh *ssh, const char *user) > > ci = get_connection_info(ssh, 1, options.use_dns); > ci->user = user; >- parse_server_match_config(&options, ci); >+ parse_server_match_config(&options, &includes, ci); > log_change_level(options.log_level); > process_permitopen(ssh, &options); > >diff --git a/servconf.c b/servconf.c >index 78540b2..6d7c97b 100644 >--- a/servconf.c >+++ b/servconf.c >@@ -21,6 +21,7 @@ > #include <net/route.h> > > #include <ctype.h> >+#include <glob.h> > #include <netdb.h> > #include <pwd.h> > #include <stdio.h> >@@ -60,6 +61,9 @@ static void add_listen_addr(ServerOptions *, const char *, > const char *, int); > static void add_one_listen_addr(ServerOptions *, const char *, > const char *, int); >+void parse_server_config_depth(ServerOptions *options, const char *filename, >+ struct sshbuf *conf, struct include_list *includes, >+ struct connection_info *connectinfo, int flags, int *activep, int depth); > > /* Use of privilege separation or not */ > extern int use_privsep; >@@ -492,7 +496,7 @@ typedef enum { > sAcceptEnv, sSetEnv, sPermitTunnel, > sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, > sUsePrivilegeSeparation, sAllowAgentForwarding, >- sHostCertificate, >+ sHostCertificate, sInclude, > sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, > sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, > sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, >@@ -504,9 +508,10 @@ typedef enum { > sDeprecated, sIgnore, sUnsupported > } ServerOpCodes; > >-#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ >-#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ >-#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) >+#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */ >+#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ >+#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) >+#define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */ > > /* Textual representation of the tokens. */ > static struct { >@@ -619,6 +624,7 @@ static struct { > { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, > { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, > { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, >+ { "include", sInclude, SSHCFG_ALL }, > { "ipqos", sIPQoS, SSHCFG_ALL }, > { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, > { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, >@@ -1183,13 +1189,14 @@ static const struct multistate multistate_tcpfwd[] = { > { NULL, -1 } > }; > >-int >-process_server_config_line(ServerOptions *options, char *line, >+static int >+process_server_config_line_depth(ServerOptions *options, char *line, > const char *filename, int linenum, int *activep, >- struct connection_info *connectinfo) >+ struct connection_info *connectinfo, int inc_flags, int depth, >+ struct include_list *includes) > { > char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; >- int cmdline = 0, *intptr, value, value2, n, port; >+ int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; > SyslogFacility *log_facility_ptr; > LogLevel *log_level_ptr; > ServerOpCodes opcode; >@@ -1198,6 +1205,8 @@ process_server_config_line(ServerOptions *options, char *line, > long long val64; > const struct multistate *multistate_ptr; > const char *errstr; >+ struct include_item *item; >+ glob_t gbuf; > > /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ > if ((len = strlen(line)) == 0) >@@ -1224,7 +1233,7 @@ process_server_config_line(ServerOptions *options, char *line, > cmdline = 1; > activep = &cmdline; > } >- if (*activep && opcode != sMatch) >+ if (*activep && opcode != sMatch && opcode != sInclude) > debug3("%s:%d setting %s %s", filename, linenum, arg, cp); > if (*activep == 0 && !(flags & SSHCFG_MATCH)) { > if (connectinfo == NULL) { >@@ -1891,6 +1900,93 @@ process_server_config_line(ServerOptions *options, char *line, > *intptr = value; > break; > >+ case sInclude: >+ if (cmdline) { >+ fatal("Include directive not supported as a " >+ "command-line option"); >+ } >+ value = 0; >+ while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') { >+ value++; >+ found = 0; >+ if (*arg2 != '/' && *arg2 != '~') { >+ xasprintf(&arg, "%s/%s", SSHDIR, arg); >+ } else >+ arg = xstrdup(arg2); >+ >+ /* >+ * Don't let included files clobber the containing >+ * file's Match state. >+ */ >+ oactive = *activep; >+ >+ /* consult cache of include files */ >+ TAILQ_FOREACH(item, includes, entry) { >+ if (strcmp(item->selector, arg) != 0) >+ continue; >+ if (item->filename != NULL) { >+ parse_server_config_depth(options, >+ item->filename, item->contents, >+ includes, connectinfo, >+ (oactive ? 0 : SSHCFG_NEVERMATCH), >+ activep, depth + 1); >+ } >+ found = 1; >+ *activep = oactive; >+ } >+ if (found != 0) { >+ free(arg); >+ continue; >+ } >+ >+ /* requested glob was not in cache */ >+ debug2("%s line %d: new include %s", >+ filename, linenum, arg); >+ if ((r = glob(arg, 0, NULL, &gbuf)) != 0) { >+ if (r != GLOB_NOMATCH) { >+ fatal("%s line %d: include \"%s\" " >+ "glob failed", filename, >+ linenum, arg); >+ } >+ /* >+ * If no entry matched then record a >+ * placeholder to skip later glob calls. >+ */ >+ debug2("%s line %d: no match for %s", >+ filename, linenum, arg); >+ item = xcalloc(1, sizeof(*item)); >+ item->selector = strdup(arg); >+ TAILQ_INSERT_TAIL(includes, >+ item, entry); >+ } >+ if (gbuf.gl_pathc > INT_MAX) >+ fatal("%s: too manu glob results", __func__); >+ for (n = 0; n < (int)gbuf.gl_pathc; n++) { >+ debug2("%s line %d: including %s", >+ filename, linenum, gbuf.gl_pathv[n]); >+ item = xcalloc(1, sizeof(*item)); >+ item->selector = strdup(arg); >+ item->filename = strdup(gbuf.gl_pathv[n]); >+ item->contents = sshbuf_new(); >+ load_server_config(item->filename, >+ item->contents); >+ parse_server_config_depth(options, >+ item->filename, item->contents, >+ includes, connectinfo, >+ (oactive ? 0 : SSHCFG_NEVERMATCH), >+ activep, depth + 1); >+ *activep = oactive; >+ TAILQ_INSERT_TAIL(includes, item, entry); >+ } >+ globfree(&gbuf); >+ free(arg); >+ } >+ if (value == 0) { >+ fatal("%s line %d: Include missing filename argument", >+ filename, linenum); >+ } >+ break; >+ > case sMatch: > if (cmdline) > fatal("Match directive not supported as a command-line " >@@ -1899,7 +1995,7 @@ process_server_config_line(ServerOptions *options, char *line, > if (value < 0) > fatal("%s line %d: Bad Match condition", filename, > linenum); >- *activep = value; >+ *activep = (inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; > break; > > case sPermitListen: >@@ -2193,6 +2289,16 @@ process_server_config_line(ServerOptions *options, char *line, > return 0; > } > >+int >+process_server_config_line(ServerOptions *options, char *line, >+ const char *filename, int linenum, int *activep, >+ struct connection_info *connectinfo, struct include_list *includes) >+{ >+ return process_server_config_line_depth(options, line, filename, >+ linenum, activep, connectinfo, 0, 0, includes); >+} >+ >+ > /* Reads the server configuration file. */ > > void >@@ -2231,12 +2337,13 @@ load_server_config(const char *filename, struct sshbuf *conf) > > void > parse_server_match_config(ServerOptions *options, >- struct connection_info *connectinfo) >+ struct include_list *includes, struct connection_info *connectinfo) > { > ServerOptions mo; > > initialize_server_options(&mo); >- parse_server_config(&mo, "reprocess config", cfg, connectinfo); >+ parse_server_config(&mo, "reprocess config", cfg, includes, >+ connectinfo); > copy_set_server_options(options, &mo, 0); > } > >@@ -2380,22 +2487,27 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) > #undef M_CP_STROPT > #undef M_CP_STRARRAYOPT > >+#define SERVCONF_MAX_DEPTH 16 > void >-parse_server_config(ServerOptions *options, const char *filename, >- struct sshbuf *conf, struct connection_info *connectinfo) >+parse_server_config_depth(ServerOptions *options, const char *filename, >+ struct sshbuf *conf, struct include_list *includes, >+ struct connection_info *connectinfo, int flags, int *activep, int depth) > { >- int active, linenum, bad_options = 0; >+ int linenum, bad_options = 0; > char *cp, *obuf, *cbuf; > >+ if (depth < 0 || depth > SERVCONF_MAX_DEPTH) >+ fatal("Too many recursive configuration includes"); >+ > debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); > > if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) > fatal("%s: sshbuf_dup_string failed", __func__); >- active = connectinfo ? 0 : 1; > linenum = 1; > while ((cp = strsep(&cbuf, "\n")) != NULL) { >- if (process_server_config_line(options, cp, filename, >- linenum++, &active, connectinfo) != 0) >+ if (process_server_config_line_depth(options, cp, >+ filename, linenum++, activep, connectinfo, flags, >+ depth, includes) != 0) > bad_options++; > } > free(obuf); >@@ -2405,6 +2517,16 @@ parse_server_config(ServerOptions *options, const char *filename, > process_queued_listen_addrs(options); > } > >+void >+parse_server_config(ServerOptions *options, const char *filename, >+ struct sshbuf *conf, struct include_list *includes, >+ struct connection_info *connectinfo) >+{ >+ int active = connectinfo ? 0 : 1; >+ parse_server_config_depth(options, filename, conf, includes, >+ connectinfo, 0, &active, 0); >+} >+ > static const char * > fmt_multistate_int(int val, const struct multistate *m) > { >diff --git a/servconf.h b/servconf.h >index 931b3e6..ac6486f 100644 >--- a/servconf.h >+++ b/servconf.h >@@ -16,6 +16,8 @@ > #ifndef SERVCONF_H > #define SERVCONF_H > >+#include <sys/queue.h> >+ > #define MAX_PORTS 256 /* Max # ports. */ > > #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ >@@ -228,6 +230,15 @@ struct connection_info { > * unspecified */ > }; > >+/* List of included files for re-exec from the parsed configuration */ >+struct include_item { >+ char *selector; >+ char *filename; >+ struct sshbuf *contents; >+ TAILQ_ENTRY(include_item) entry; >+}; >+TAILQ_HEAD(include_list, include_item); >+ > > /* > * These are string config options that must be copied between the >@@ -267,12 +278,13 @@ struct connection_info *get_connection_info(struct ssh *, int, int); > void initialize_server_options(ServerOptions *); > void fill_default_server_options(ServerOptions *); > int process_server_config_line(ServerOptions *, char *, const char *, int, >- int *, struct connection_info *); >+ int *, struct connection_info *, struct include_list *includes); > void process_permitopen(struct ssh *ssh, ServerOptions *options); > void load_server_config(const char *, struct sshbuf *); > void parse_server_config(ServerOptions *, const char *, struct sshbuf *, >- struct connection_info *); >-void parse_server_match_config(ServerOptions *, struct connection_info *); >+ struct include_list *includes, struct connection_info *); >+void parse_server_match_config(ServerOptions *, >+ struct include_list *includes, struct connection_info *); > int parse_server_match_testspec(struct connection_info *, char *); > int server_match_spec_complete(struct connection_info *); > void copy_set_server_options(ServerOptions *, ServerOptions *, int); >diff --git a/sshd.c b/sshd.c >index f6139fe..dbeb478 100644 >--- a/sshd.c >+++ b/sshd.c >@@ -232,6 +232,9 @@ struct sshauthopt *auth_opts = NULL; > /* sshd_config buffer */ > struct sshbuf *cfg; > >+/* Included files from the configuration file */ >+struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); >+ > /* message to be displayed after login */ > struct sshbuf *loginmsg; > >@@ -827,24 +830,41 @@ usage(void) > static void > send_rexec_state(int fd, struct sshbuf *conf) > { >- struct sshbuf *m; >+ struct sshbuf *m = NULL, *inc = NULL; >+ struct include_item *item = NULL; > int r; > > debug3("%s: entering fd = %d config len %zu", __func__, fd, > sshbuf_len(conf)); > >+ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) >+ fatal("%s: sshbuf_new failed", __func__); >+ >+ /* pack includes into a string */ >+ TAILQ_FOREACH(item, &includes, entry) { >+ if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 || >+ (r = sshbuf_put_cstring(inc, item->filename)) != 0 || >+ (r = sshbuf_put_stringb(m, item->contents)) != 0) >+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); >+ } >+ > /* > * Protocol from reexec master to child: > * string configuration >+ * string included_files[] { >+ * string selector >+ * string filename >+ * string contents >+ * } > */ >- if ((m = sshbuf_new()) == NULL) >- fatal("%s: sshbuf_new failed", __func__); >- if ((r = sshbuf_put_stringb(m, conf)) != 0) >+ if ((r = sshbuf_put_stringb(m, conf)) != 0 || >+ (r = sshbuf_put_stringb(m, inc)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if (ssh_msg_send(fd, 0, m) == -1) > fatal("%s: ssh_msg_send failed", __func__); > > sshbuf_free(m); >+ sshbuf_free(inc); > > debug3("%s: done", __func__); > } >@@ -852,14 +872,15 @@ send_rexec_state(int fd, struct sshbuf *conf) > static void > recv_rexec_state(int fd, struct sshbuf *conf) > { >- struct sshbuf *m; >+ struct sshbuf *m, *inc; > u_char *cp, ver; > size_t len; > int r; >+ struct include_item *item; > > debug3("%s: entering fd = %d", __func__, fd); > >- if ((m = sshbuf_new()) == NULL) >+ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if (ssh_msg_recv(fd, m) == -1) > fatal("%s: ssh_msg_recv failed", __func__); >@@ -867,11 +888,24 @@ recv_rexec_state(int fd, struct sshbuf *conf) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if (ver != 0) > fatal("%s: rexec version mismatch", __func__); >- if ((r = sshbuf_get_string(m, &cp, &len)) != 0) >+ if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || >+ (r = sshbuf_get_stringb(m, inc)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); >+ > if (conf != NULL && (r = sshbuf_put(conf, cp, len))) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > >+ while (sshbuf_len(inc) != 0) { >+ item = xcalloc(1, sizeof(*item)); >+ if ((item->contents = sshbuf_new()) == NULL) >+ fatal("%s: sshbuf_new failed", __func__); >+ if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 || >+ (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 || >+ (r = sshbuf_get_stringb(inc, item->contents)) != 0) >+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); >+ TAILQ_INSERT_TAIL(&includes, item, entry); >+ } >+ > free(cp); > sshbuf_free(m); > >@@ -1479,7 +1513,7 @@ main(int ac, char **av) > case 'o': > line = xstrdup(optarg); > if (process_server_config_line(&options, line, >- "command-line", 0, NULL, NULL) != 0) >+ "command-line", 0, NULL, NULL, &includes) != 0) > exit(1); > free(line); > break; >@@ -1545,7 +1579,7 @@ main(int ac, char **av) > load_server_config(config_file_name, cfg); > > parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, >- cfg, NULL); >+ cfg, &includes, NULL); > > /* Fill in default values for those options not explicitly set. */ > fill_default_server_options(&options); >@@ -1755,7 +1789,7 @@ main(int ac, char **av) > if (connection_info == NULL) > connection_info = get_connection_info(ssh, 0, 0); > connection_info->test = 1; >- parse_server_match_config(&options, connection_info); >+ parse_server_match_config(&options, &includes, connection_info); > dump_config(&options); > } > >diff --git a/sshd_config.5 b/sshd_config.5 >index cb5d8cb..3ab52e0 100644 >--- a/sshd_config.5 >+++ b/sshd_config.5 >@@ -812,7 +812,20 @@ during > and use only the system-wide known hosts file > .Pa /etc/ssh/known_hosts . > The default is >-.Cm no . >+.Dq no . >+.It Cm Include >+Include the specified configuration file(s). >+Multiple path names may be specified and each pathname may contain >+.Xr glob 7 >+wildcards. >+Files without absolute paths are assumed to be in >+.Pa /etc/ssh . >+A >+.Cm Include >+directive may appear inside a >+.Cm Match >+block >+to perform conditional inclusion. > .It Cm IPQoS > Specifies the IPv4 type-of-service or DSCP class for the connection. > Accepted values are
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2468
:
2706
|
2869
|
3223
|
3250
|
3333
| 3350 |
3351