summaryrefslogtreecommitdiff
path: root/servconf.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-01-31 22:42:45 +0000
committerDamien Miller <djm@mindrot.org>2020-02-01 10:20:24 +1100
commitc2bd7f74b0e0f3a3ee9d19ac549e6ba89013abaf (patch)
treef90d36f2501a863ff0c3d1041d93a2ef827c54d1 /servconf.c
parentba261a1dd33266168ead4f8f40446dcece4d1600 (diff)
upstream: Add a sshd_config "Include" directive to allow inclusion
of files. This has sensible semantics wrt Match blocks and accepts glob(3) patterns to specify the included files. Based on patch by Jakub Jelen in bz2468; feedback and ok markus@ OpenBSD-Commit-ID: 36ed0e845b872e33f03355b936a4fff02d5794ff
Diffstat (limited to 'servconf.c')
-rw-r--r--servconf.c167
1 files changed, 148 insertions, 19 deletions
diff --git a/servconf.c b/servconf.c
index 1e0718139..70f5f73f0 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,5 +1,5 @@
1 1
2/* $OpenBSD: servconf.c,v 1.359 2020/01/23 10:24:29 dtucker Exp $ */ 2/* $OpenBSD: servconf.c,v 1.360 2020/01/31 22:42:45 djm Exp $ */
3/* 3/*
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved 5 * All rights reserved
@@ -40,6 +40,11 @@
40#ifdef HAVE_UTIL_H 40#ifdef HAVE_UTIL_H
41#include <util.h> 41#include <util.h>
42#endif 42#endif
43#ifdef USE_SYSTEM_GLOB
44# include <glob.h>
45#else
46# include "openbsd-compat/glob.h"
47#endif
43 48
44#include "openbsd-compat/sys-queue.h" 49#include "openbsd-compat/sys-queue.h"
45#include "xmalloc.h" 50#include "xmalloc.h"
@@ -69,6 +74,9 @@ static void add_listen_addr(ServerOptions *, const char *,
69 const char *, int); 74 const char *, int);
70static void add_one_listen_addr(ServerOptions *, const char *, 75static void add_one_listen_addr(ServerOptions *, const char *,
71 const char *, int); 76 const char *, int);
77void parse_server_config_depth(ServerOptions *options, const char *filename,
78 struct sshbuf *conf, struct include_list *includes,
79 struct connection_info *connectinfo, int flags, int *activep, int depth);
72 80
73/* Use of privilege separation or not */ 81/* Use of privilege separation or not */
74extern int use_privsep; 82extern int use_privsep;
@@ -526,7 +534,7 @@ typedef enum {
526 sAcceptEnv, sSetEnv, sPermitTunnel, 534 sAcceptEnv, sSetEnv, sPermitTunnel,
527 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 535 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
528 sUsePrivilegeSeparation, sAllowAgentForwarding, 536 sUsePrivilegeSeparation, sAllowAgentForwarding,
529 sHostCertificate, 537 sHostCertificate, sInclude,
530 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, 538 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
531 sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, 539 sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
532 sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, 540 sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum,
@@ -538,9 +546,10 @@ typedef enum {
538 sDeprecated, sIgnore, sUnsupported 546 sDeprecated, sIgnore, sUnsupported
539} ServerOpCodes; 547} ServerOpCodes;
540 548
541#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ 549#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */
542#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ 550#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */
543#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) 551#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH)
552#define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */
544 553
545/* Textual representation of the tokens. */ 554/* Textual representation of the tokens. */
546static struct { 555static struct {
@@ -669,6 +678,7 @@ static struct {
669 { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, 678 { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
670 { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, 679 { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
671 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, 680 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
681 { "include", sInclude, SSHCFG_ALL },
672 { "ipqos", sIPQoS, SSHCFG_ALL }, 682 { "ipqos", sIPQoS, SSHCFG_ALL },
673 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, 683 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
674 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, 684 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
@@ -1240,13 +1250,14 @@ static const struct multistate multistate_tcpfwd[] = {
1240 { NULL, -1 } 1250 { NULL, -1 }
1241}; 1251};
1242 1252
1243int 1253static int
1244process_server_config_line(ServerOptions *options, char *line, 1254process_server_config_line_depth(ServerOptions *options, char *line,
1245 const char *filename, int linenum, int *activep, 1255 const char *filename, int linenum, int *activep,
1246 struct connection_info *connectinfo) 1256 struct connection_info *connectinfo, int inc_flags, int depth,
1257 struct include_list *includes)
1247{ 1258{
1248 char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; 1259 char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p;
1249 int cmdline = 0, *intptr, value, value2, n, port; 1260 int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found;
1250 SyslogFacility *log_facility_ptr; 1261 SyslogFacility *log_facility_ptr;
1251 LogLevel *log_level_ptr; 1262 LogLevel *log_level_ptr;
1252 ServerOpCodes opcode; 1263 ServerOpCodes opcode;
@@ -1255,6 +1266,8 @@ process_server_config_line(ServerOptions *options, char *line,
1255 long long val64; 1266 long long val64;
1256 const struct multistate *multistate_ptr; 1267 const struct multistate *multistate_ptr;
1257 const char *errstr; 1268 const char *errstr;
1269 struct include_item *item;
1270 glob_t gbuf;
1258 1271
1259 /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ 1272 /* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1260 if ((len = strlen(line)) == 0) 1273 if ((len = strlen(line)) == 0)
@@ -1281,7 +1294,7 @@ process_server_config_line(ServerOptions *options, char *line,
1281 cmdline = 1; 1294 cmdline = 1;
1282 activep = &cmdline; 1295 activep = &cmdline;
1283 } 1296 }
1284 if (*activep && opcode != sMatch) 1297 if (*activep && opcode != sMatch && opcode != sInclude)
1285 debug3("%s:%d setting %s %s", filename, linenum, arg, cp); 1298 debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
1286 if (*activep == 0 && !(flags & SSHCFG_MATCH)) { 1299 if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
1287 if (connectinfo == NULL) { 1300 if (connectinfo == NULL) {
@@ -1954,6 +1967,96 @@ process_server_config_line(ServerOptions *options, char *line,
1954 *intptr = value; 1967 *intptr = value;
1955 break; 1968 break;
1956 1969
1970 case sInclude:
1971 if (cmdline) {
1972 fatal("Include directive not supported as a "
1973 "command-line option");
1974 }
1975 value = 0;
1976 while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') {
1977 value++;
1978 found = 0;
1979 if (*arg2 != '/' && *arg2 != '~') {
1980 xasprintf(&arg, "%s/%s", SSHDIR, arg);
1981 } else
1982 arg = xstrdup(arg2);
1983
1984 /*
1985 * Don't let included files clobber the containing
1986 * file's Match state.
1987 */
1988 oactive = *activep;
1989
1990 /* consult cache of include files */
1991 TAILQ_FOREACH(item, includes, entry) {
1992 if (strcmp(item->selector, arg) != 0)
1993 continue;
1994 if (item->filename != NULL) {
1995 parse_server_config_depth(options,
1996 item->filename, item->contents,
1997 includes, connectinfo,
1998 (oactive ? 0 : SSHCFG_NEVERMATCH),
1999 activep, depth + 1);
2000 }
2001 found = 1;
2002 *activep = oactive;
2003 }
2004 if (found != 0) {
2005 free(arg);
2006 continue;
2007 }
2008
2009 /* requested glob was not in cache */
2010 debug2("%s line %d: new include %s",
2011 filename, linenum, arg);
2012 if ((r = glob(arg, 0, NULL, &gbuf)) != 0) {
2013 if (r != GLOB_NOMATCH) {
2014 fatal("%s line %d: include \"%s\" "
2015 "glob failed", filename,
2016 linenum, arg);
2017 }
2018 /*
2019 * If no entry matched then record a
2020 * placeholder to skip later glob calls.
2021 */
2022 debug2("%s line %d: no match for %s",
2023 filename, linenum, arg);
2024 item = xcalloc(1, sizeof(*item));
2025 item->selector = strdup(arg);
2026 TAILQ_INSERT_TAIL(includes,
2027 item, entry);
2028 }
2029 if (gbuf.gl_pathc > INT_MAX)
2030 fatal("%s: too many glob results", __func__);
2031 for (n = 0; n < (int)gbuf.gl_pathc; n++) {
2032 debug2("%s line %d: including %s",
2033 filename, linenum, gbuf.gl_pathv[n]);
2034 item = xcalloc(1, sizeof(*item));
2035 item->selector = strdup(arg);
2036 item->filename = strdup(gbuf.gl_pathv[n]);
2037 if ((item->contents = sshbuf_new()) == NULL) {
2038 fatal("%s: sshbuf_new failed",
2039 __func__);
2040 }
2041 load_server_config(item->filename,
2042 item->contents);
2043 parse_server_config_depth(options,
2044 item->filename, item->contents,
2045 includes, connectinfo,
2046 (oactive ? 0 : SSHCFG_NEVERMATCH),
2047 activep, depth + 1);
2048 *activep = oactive;
2049 TAILQ_INSERT_TAIL(includes, item, entry);
2050 }
2051 globfree(&gbuf);
2052 free(arg);
2053 }
2054 if (value == 0) {
2055 fatal("%s line %d: Include missing filename argument",
2056 filename, linenum);
2057 }
2058 break;
2059
1957 case sMatch: 2060 case sMatch:
1958 if (cmdline) 2061 if (cmdline)
1959 fatal("Match directive not supported as a command-line " 2062 fatal("Match directive not supported as a command-line "
@@ -1962,7 +2065,7 @@ process_server_config_line(ServerOptions *options, char *line,
1962 if (value < 0) 2065 if (value < 0)
1963 fatal("%s line %d: Bad Match condition", filename, 2066 fatal("%s line %d: Bad Match condition", filename,
1964 linenum); 2067 linenum);
1965 *activep = value; 2068 *activep = (inc_flags & SSHCFG_NEVERMATCH) ? 0 : value;
1966 break; 2069 break;
1967 2070
1968 case sPermitListen: 2071 case sPermitListen:
@@ -2256,6 +2359,16 @@ process_server_config_line(ServerOptions *options, char *line,
2256 return 0; 2359 return 0;
2257} 2360}
2258 2361
2362int
2363process_server_config_line(ServerOptions *options, char *line,
2364 const char *filename, int linenum, int *activep,
2365 struct connection_info *connectinfo, struct include_list *includes)
2366{
2367 return process_server_config_line_depth(options, line, filename,
2368 linenum, activep, connectinfo, 0, 0, includes);
2369}
2370
2371
2259/* Reads the server configuration file. */ 2372/* Reads the server configuration file. */
2260 2373
2261void 2374void
@@ -2294,12 +2407,13 @@ load_server_config(const char *filename, struct sshbuf *conf)
2294 2407
2295void 2408void
2296parse_server_match_config(ServerOptions *options, 2409parse_server_match_config(ServerOptions *options,
2297 struct connection_info *connectinfo) 2410 struct include_list *includes, struct connection_info *connectinfo)
2298{ 2411{
2299 ServerOptions mo; 2412 ServerOptions mo;
2300 2413
2301 initialize_server_options(&mo); 2414 initialize_server_options(&mo);
2302 parse_server_config(&mo, "reprocess config", cfg, connectinfo); 2415 parse_server_config(&mo, "reprocess config", cfg, includes,
2416 connectinfo);
2303 copy_set_server_options(options, &mo, 0); 2417 copy_set_server_options(options, &mo, 0);
2304} 2418}
2305 2419
@@ -2443,22 +2557,27 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
2443#undef M_CP_STROPT 2557#undef M_CP_STROPT
2444#undef M_CP_STRARRAYOPT 2558#undef M_CP_STRARRAYOPT
2445 2559
2560#define SERVCONF_MAX_DEPTH 16
2446void 2561void
2447parse_server_config(ServerOptions *options, const char *filename, 2562parse_server_config_depth(ServerOptions *options, const char *filename,
2448 struct sshbuf *conf, struct connection_info *connectinfo) 2563 struct sshbuf *conf, struct include_list *includes,
2564 struct connection_info *connectinfo, int flags, int *activep, int depth)
2449{ 2565{
2450 int active, linenum, bad_options = 0; 2566 int linenum, bad_options = 0;
2451 char *cp, *obuf, *cbuf; 2567 char *cp, *obuf, *cbuf;
2452 2568
2569 if (depth < 0 || depth > SERVCONF_MAX_DEPTH)
2570 fatal("Too many recursive configuration includes");
2571
2453 debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); 2572 debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf));
2454 2573
2455 if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) 2574 if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL)
2456 fatal("%s: sshbuf_dup_string failed", __func__); 2575 fatal("%s: sshbuf_dup_string failed", __func__);
2457 active = connectinfo ? 0 : 1;
2458 linenum = 1; 2576 linenum = 1;
2459 while ((cp = strsep(&cbuf, "\n")) != NULL) { 2577 while ((cp = strsep(&cbuf, "\n")) != NULL) {
2460 if (process_server_config_line(options, cp, filename, 2578 if (process_server_config_line_depth(options, cp,
2461 linenum++, &active, connectinfo) != 0) 2579 filename, linenum++, activep, connectinfo, flags,
2580 depth, includes) != 0)
2462 bad_options++; 2581 bad_options++;
2463 } 2582 }
2464 free(obuf); 2583 free(obuf);
@@ -2468,6 +2587,16 @@ parse_server_config(ServerOptions *options, const char *filename,
2468 process_queued_listen_addrs(options); 2587 process_queued_listen_addrs(options);
2469} 2588}
2470 2589
2590void
2591parse_server_config(ServerOptions *options, const char *filename,
2592 struct sshbuf *conf, struct include_list *includes,
2593 struct connection_info *connectinfo)
2594{
2595 int active = connectinfo ? 0 : 1;
2596 parse_server_config_depth(options, filename, conf, includes,
2597 connectinfo, 0, &active, 0);
2598}
2599
2471static const char * 2600static const char *
2472fmt_multistate_int(int val, const struct multistate *m) 2601fmt_multistate_int(int val, const struct multistate *m)
2473{ 2602{