diff options
Diffstat (limited to 'servconf.c')
-rw-r--r-- | servconf.c | 167 |
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); |
70 | static void add_one_listen_addr(ServerOptions *, const char *, | 75 | static void add_one_listen_addr(ServerOptions *, const char *, |
71 | const char *, int); | 76 | const char *, int); |
77 | void 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 */ |
74 | extern int use_privsep; | 82 | extern 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. */ |
546 | static struct { | 555 | static 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 | ||
1243 | int | 1253 | static int |
1244 | process_server_config_line(ServerOptions *options, char *line, | 1254 | process_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 | ||
2362 | int | ||
2363 | process_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 | ||
2261 | void | 2374 | void |
@@ -2294,12 +2407,13 @@ load_server_config(const char *filename, struct sshbuf *conf) | |||
2294 | 2407 | ||
2295 | void | 2408 | void |
2296 | parse_server_match_config(ServerOptions *options, | 2409 | parse_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 | ||
2446 | void | 2561 | void |
2447 | parse_server_config(ServerOptions *options, const char *filename, | 2562 | parse_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 | ||
2590 | void | ||
2591 | parse_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 | |||
2471 | static const char * | 2600 | static const char * |
2472 | fmt_multistate_int(int val, const struct multistate *m) | 2601 | fmt_multistate_int(int val, const struct multistate *m) |
2473 | { | 2602 | { |