diff --git a/SOURCES/openssh-8.0p1-client_alive_count_max.patch b/SOURCES/openssh-8.0p1-client_alive_count_max.patch new file mode 100644 index 0000000..e0f272f --- /dev/null +++ b/SOURCES/openssh-8.0p1-client_alive_count_max.patch @@ -0,0 +1,28 @@ +diff --git a/serverloop.c b/serverloop.c +index e16eabe2..a8c99e2e 100644 +--- a/serverloop.c ++++ b/serverloop.c +@@ -184,7 +184,8 @@ client_alive_check(struct ssh *ssh) + int r, channel_id; + + /* timeout, check to see how many we have had */ +- if (ssh_packet_inc_alive_timeouts(ssh) > ++ if (options.client_alive_count_max > 0 && ++ ssh_packet_inc_alive_timeouts(ssh) > + options.client_alive_count_max) { + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + logit("Timeout, client not responding from %s", remote_id); +diff --git a/sshd_config.5 b/sshd_config.5 +index d47cb0d2..2cddbd59 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -519,6 +519,9 @@ is set to 15, and + .Cm ClientAliveCountMax + is left at the default, unresponsive SSH clients + will be disconnected after approximately 45 seconds. ++Setting a zero ++.Cm ClientAliveCountMax ++disables connection termination. + .It Cm ClientAliveInterval + Sets a timeout interval in seconds after which if no data has been received + from the client, diff --git a/SOURCES/openssh-8.0p1-sshd_include.patch b/SOURCES/openssh-8.0p1-sshd_include.patch new file mode 100644 index 0000000..03c4636 --- /dev/null +++ b/SOURCES/openssh-8.0p1-sshd_include.patch @@ -0,0 +1,792 @@ +diff -up openssh-8.0p1/auth.c.sshdinclude openssh-8.0p1/auth.c +--- openssh-8.0p1/auth.c.sshdinclude 2021-10-20 15:18:49.740331098 +0200 ++++ openssh-8.0p1/auth.c 2021-10-20 15:19:41.324781344 +0200 +@@ -80,6 +80,7 @@ + + /* import */ + extern ServerOptions options; ++extern struct include_list includes; + extern int use_privsep; + extern struct sshbuf *loginmsg; + extern struct passwd *privsep_pw; +@@ -573,7 +574,7 @@ getpwnamallow(struct ssh *ssh, const cha + + 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 -up openssh-8.0p1/readconf.c.sshdinclude openssh-8.0p1/readconf.c +--- openssh-8.0p1/readconf.c.sshdinclude 2021-10-20 15:21:43.541848103 +0200 ++++ openssh-8.0p1/readconf.c 2021-10-20 15:22:06.302046768 +0200 +@@ -711,7 +711,7 @@ match_cfg_line(Options *options, char ** + static void + rm_env(Options *options, const char *arg, const char *filename, int linenum) + { +- int i, j; ++ int i, j, onum_send_env = options->num_send_env; + char *cp; + + /* Remove an environment variable */ +@@ -734,6 +734,11 @@ rm_env(Options *options, const char *arg + options->num_send_env--; + /* NB. don't increment i */ + } ++ if (onum_send_env != options->num_send_env) { ++ options->send_env = xrecallocarray(options->send_env, ++ onum_send_env, options->num_send_env, ++ sizeof(*options->send_env)); ++ } + } + + /* +diff -up openssh-8.0p1/regress/Makefile.sshdinclude openssh-8.0p1/regress/Makefile +--- openssh-8.0p1/regress/Makefile.sshdinclude 2021-10-20 15:18:49.742331115 +0200 ++++ openssh-8.0p1/regress/Makefile 2021-10-20 15:19:41.324781344 +0200 +@@ -82,6 +82,7 @@ LTESTS= connect \ + principals-command \ + cert-file \ + cfginclude \ ++ servcfginclude \ + allow-deny-users \ + authinfo + +@@ -118,7 +119,7 @@ CLEANFILES= *.core actual agent-key.* au + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ + ssh_proxy_envpass sshd.log sshd_config sshd_config_minimal \ +- sshd_config.orig sshd_proxy sshd_proxy.* sshd_proxy_bak \ ++ sshd_config.* sshd_proxy sshd_proxy.* sshd_proxy_bak \ + sshd_proxy_orig t10.out t10.out.pub t12.out t12.out.pub \ + t2.out t3.out t6.out1 t6.out2 t7.out t7.out.pub \ + t8.out t8.out.pub t9.out t9.out.pub testdata \ +diff -up openssh-8.0p1/regress/servcfginclude.sh.sshdinclude openssh-8.0p1/regress/servcfginclude.sh +--- openssh-8.0p1/regress/servcfginclude.sh.sshdinclude 2021-10-20 15:18:49.744331132 +0200 ++++ openssh-8.0p1/regress/servcfginclude.sh 2021-10-20 15:22:06.303046777 +0200 +@@ -0,0 +1,188 @@ ++# Placed in the Public Domain. ++ ++tid="server config include" ++ ++cat > $OBJ/sshd_config.i << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++Match host a ++ Banner /aa ++ ++Match host b ++ Banner /bb ++ Include $OBJ/sshd_config.i.* ++ ++Match host c ++ Include $OBJ/sshd_config.i.* ++ Banner /cc ++ ++Match host m ++ Include $OBJ/sshd_config.i.* ++ ++Match Host d ++ Banner /dd ++ ++Match Host e ++ Banner /ee ++ Include $OBJ/sshd_config.i.* ++ ++Match Host f ++ Include $OBJ/sshd_config.i.* ++ Banner /ff ++ ++Match Host n ++ Include $OBJ/sshd_config.i.* ++_EOF ++ ++cat > $OBJ/sshd_config.i.0 << _EOF ++Match host xxxxxx ++_EOF ++ ++cat > $OBJ/sshd_config.i.1 << _EOF ++Match host a ++ Banner /aaa ++ ++Match host b ++ Banner /bbb ++ ++Match host c ++ Banner /ccc ++ ++Match Host d ++ Banner /ddd ++ ++Match Host e ++ Banner /eee ++ ++Match Host f ++ Banner /fff ++_EOF ++ ++cat > $OBJ/sshd_config.i.2 << _EOF ++Match host a ++ Banner /aaaa ++ ++Match host b ++ Banner /bbbb ++ ++Match host c ++ Banner /cccc ++ ++Match Host d ++ Banner /dddd ++ ++Match Host e ++ Banner /eeee ++ ++Match Host f ++ Banner /ffff ++ ++Match all ++ Banner /xxxx ++_EOF ++ ++trial() { ++ _host="$1" ++ _exp="$2" ++ _desc="$3" ++ test -z "$_desc" && _desc="test match" ++ trace "$_desc host=$_host expect=$_exp" ++ ${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ ++ -C "host=$_host,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || ++ fatal "ssh config parse failed: $_desc host=$_host expect=$_exp" ++ _got=`grep -i '^banner ' $OBJ/sshd_config.out | awk '{print $2}'` ++ if test "x$_exp" != "x$_got" ; then ++ fail "$desc_ host $_host include fail: expected $_exp got $_got" ++ fi ++} ++ ++trial a /aa ++trial b /bb ++trial c /ccc ++trial d /dd ++trial e /ee ++trial f /fff ++trial m /xxxx ++trial n /xxxx ++trial x none ++ ++# Prepare an included config with an error. ++ ++cat > $OBJ/sshd_config.i.3 << _EOF ++Banner xxxx ++ Junk ++_EOF ++ ++trace "disallow invalid config host=a" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ ++ -C "host=a,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd include allowed invalid config" ++ ++trace "disallow invalid config host=x" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ ++ -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd include allowed invalid config" ++ ++rm -f $OBJ/sshd_config.i.* ++ ++# Ensure that a missing include is not fatal. ++cat > $OBJ/sshd_config.i << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++Include $OBJ/sshd_config.i.* ++Banner /aa ++_EOF ++ ++trial a /aa "missing include non-fatal" ++ ++# Ensure that Match/Host in an included config does not affect parent. ++cat > $OBJ/sshd_config.i.x << _EOF ++Match host x ++_EOF ++ ++trial a /aa "included file does not affect match state" ++ ++# Ensure the empty include directive is not accepted ++cat > $OBJ/sshd_config.i.x << _EOF ++Include ++_EOF ++ ++trace "disallow invalid with no argument" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i.x -T \ ++ -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd allowed Include with no argument" ++ ++# Ensure the Include before any Match block works as expected (bug #3122) ++cat > $OBJ/sshd_config.i << _EOF ++Banner /xx ++HostKey $OBJ/host.ssh-ed25519 ++Include $OBJ/sshd_config.i.2 ++Match host a ++ Banner /aaaa ++_EOF ++cat > $OBJ/sshd_config.i.2 << _EOF ++Match host a ++ Banner /aa ++_EOF ++ ++trace "Include before match blocks" ++trial a /aa "included file before match blocks is properly evaluated" ++ ++# Port in included file is correctly interpretted (bug #3169) ++cat > $OBJ/sshd_config.i << _EOF ++Include $OBJ/sshd_config.i.2 ++Port 7722 ++_EOF ++cat > $OBJ/sshd_config.i.2 << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++_EOF ++ ++trace "Port after included files" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ ++ -C "host=x,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || \ ++ fail "failed to parse Port after included files" ++_port=`grep -i '^port ' $OBJ/sshd_config.out | awk '{print $2}'` ++if test "x7722" != "x$_port" ; then ++ fail "The Port in included file was intertepretted wrongly. Expected 7722, got $_port" ++fi ++ ++# cleanup ++rm -f $OBJ/sshd_config.i $OBJ/sshd_config.i.* $OBJ/sshd_config.out +diff -up openssh-8.0p1/regress/test-exec.sh.sshdinclude openssh-8.0p1/regress/test-exec.sh +--- openssh-8.0p1/regress/test-exec.sh.sshdinclude 2021-10-20 15:18:49.746331150 +0200 ++++ openssh-8.0p1/regress/test-exec.sh 2021-10-20 15:19:41.324781344 +0200 +@@ -220,6 +220,7 @@ echo "exec ${SSH} -E${TEST_SSH_LOGFILE} + + chmod a+rx $OBJ/ssh-log-wrapper.sh + REAL_SSH="$SSH" ++REAL_SSHD="$SSHD" + SSH="$SSHLOGWRAP" + + # Some test data. We make a copy because some tests will overwrite it. +diff -up openssh-8.0p1/servconf.c.sshdinclude openssh-8.0p1/servconf.c +--- openssh-8.0p1/servconf.c.sshdinclude 2021-10-20 15:18:49.748331167 +0200 ++++ openssh-8.0p1/servconf.c 2021-10-20 15:22:06.303046777 +0200 +@@ -40,6 +40,11 @@ + #ifdef HAVE_UTIL_H + #include + #endif ++#ifdef USE_SYSTEM_GLOB ++# include ++#else ++# include "openbsd-compat/glob.h" ++#endif + + #include "openbsd-compat/sys-queue.h" + #include "xmalloc.h" +@@ -70,6 +75,9 @@ static void add_listen_addr(ServerOption + const char *, int); + static void add_one_listen_addr(ServerOptions *, const char *, + const char *, int); ++static 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; +@@ -528,7 +536,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, +@@ -540,9 +548,11 @@ 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 */ ++#define SSHCFG_MATCH_ONLY 0x08 /* Match only in conditional blocks; internal only */ + + /* Textual representation of the tokens. */ + static struct { +@@ -687,6 +697,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 }, +@@ -1259,13 +1270,14 @@ static const struct multistate multistat + { 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; +@@ -1274,6 +1286,8 @@ process_server_config_line(ServerOptions + 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) +@@ -1300,7 +1314,7 @@ process_server_config_line(ServerOptions + 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) { +@@ -1980,15 +1994,112 @@ process_server_config_line(ServerOptions + *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, arg2); ++ } 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, ++ (*inc_flags & SSHCFG_MATCH_ONLY ++ ? SSHCFG_MATCH_ONLY : (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 many 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]); ++ if ((item->contents = sshbuf_new()) == NULL) { ++ fatal("%s: sshbuf_new failed", ++ __func__); ++ } ++ load_server_config(item->filename, ++ item->contents); ++ parse_server_config_depth(options, ++ item->filename, item->contents, ++ includes, connectinfo, ++ (*inc_flags & SSHCFG_MATCH_ONLY ++ ? SSHCFG_MATCH_ONLY : (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 " + "option"); +- value = match_cfg_line(&cp, linenum, connectinfo); ++ value = match_cfg_line(&cp, linenum, ++ (*inc_flags & SSHCFG_NEVERMATCH ? NULL : connectinfo)); + if (value < 0) + fatal("%s line %d: Bad Match condition", filename, + linenum); +- *activep = value; ++ *activep = (*inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; ++ /* The MATCH_ONLY is applicable only until the first match block */ ++ *inc_flags &= ~SSHCFG_MATCH_ONLY; + break; + + case sKerberosUseKuserok: +@@ -2275,6 +2386,18 @@ process_server_config_line(ServerOptions + 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) ++{ ++ int inc_flags = 0; ++ ++ return process_server_config_line_depth(options, line, filename, ++ linenum, activep, connectinfo, &inc_flags, 0, includes); ++} ++ ++ + /* Reads the server configuration file. */ + + void +@@ -2313,12 +2436,13 @@ load_server_config(const char *filename, + + 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); + } + +@@ -2464,28 +2588,44 @@ copy_set_server_options(ServerOptions *d + #undef M_CP_STROPT + #undef M_CP_STRARRAYOPT + +-void +-parse_server_config(ServerOptions *options, const char *filename, +- struct sshbuf *conf, struct connection_info *connectinfo) ++#define SERVCONF_MAX_DEPTH 16 ++static 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) + { +- int active, linenum, bad_options = 0; ++ int linenum, bad_options = 0; + char *cp, *obuf, *cbuf; + +- debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); ++ if (depth < 0 || depth > SERVCONF_MAX_DEPTH) ++ fatal("Too many recursive configuration includes"); ++ ++ debug2("%s: config %s len %zu%s", __func__, filename, sshbuf_len(conf), ++ (flags & SSHCFG_NEVERMATCH ? " [checking syntax only]" : "")); + + 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); + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options", + filename, bad_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, (connectinfo ? SSHCFG_MATCH_ONLY : 0), &active, 0); + process_queued_listen_addrs(options); + } + +diff -up openssh-8.0p1/servconf.h.sshdinclude openssh-8.0p1/servconf.h +--- openssh-8.0p1/servconf.h.sshdinclude 2021-10-20 15:18:49.750331185 +0200 ++++ openssh-8.0p1/servconf.h 2021-10-20 15:19:41.325781353 +0200 +@@ -16,6 +16,8 @@ + #ifndef SERVCONF_H + #define SERVCONF_H + ++#include ++ + #define MAX_PORTS 256 /* Max # ports. */ + + #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ +@@ -234,6 +236,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 +@@ -273,12 +284,13 @@ struct connection_info *get_connection_i + 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 -up openssh-8.0p1/sshd_config.5.sshdinclude openssh-8.0p1/sshd_config.5 +--- openssh-8.0p1/sshd_config.5.sshdinclude 2021-10-20 15:18:49.754331220 +0200 ++++ openssh-8.0p1/sshd_config.5 2021-10-20 15:19:41.325781353 +0200 +@@ -825,7 +825,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 +diff -up openssh-8.0p1/sshd.c.sshdinclude openssh-8.0p1/sshd.c +--- openssh-8.0p1/sshd.c.sshdinclude 2021-10-20 15:18:49.752331202 +0200 ++++ openssh-8.0p1/sshd.c 2021-10-20 15:19:41.325781353 +0200 +@@ -257,6 +257,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; + +@@ -927,30 +930,45 @@ 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(inc, item->contents)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ } ++ + /* + * Protocol from reexec master to child: + * string configuration +- * string rngseed (only if OpenSSL is not self-seeded) ++ * string included_files[] { ++ * string selector ++ * string filename ++ * string contents ++ * } ++ * string rng_seed (if required) + */ +- 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 defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_send_rng_seed(m); + #endif +- + 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__); + } +@@ -958,14 +976,15 @@ send_rexec_state(int fd, struct sshbuf * + 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__); +@@ -973,14 +992,28 @@ recv_rexec_state(int fd, struct sshbuf * + 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) +- fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- if (conf != NULL && (r = sshbuf_put(conf, cp, len))) ++ 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 defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_recv_rng_seed(m); + #endif + ++ 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); + +@@ -1661,7 +1694,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; +@@ -1692,7 +1725,7 @@ main(int ac, char **av) + SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == SYSLOG_FACILITY_NOT_SET ? + SYSLOG_FACILITY_AUTH : options.log_facility, +- log_stderr || !inetd_flag); ++ log_stderr || !inetd_flag || debug_flag); + + /* + * Unset KRB5CCNAME, otherwise the user's session may inherit it from +@@ -1725,12 +1758,11 @@ main(int ac, char **av) + */ + (void)atomicio(vwrite, startup_pipe, "\0", 1); + } +- } +- else if (strcasecmp(config_file_name, "none") != 0) ++ } else if (strcasecmp(config_file_name, "none") != 0) + load_server_config(config_file_name, cfg); + + parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, +- cfg, NULL); ++ cfg, &includes, NULL); + + /* 'UsePAM no' is not supported in Fedora */ + if (! options.use_pam) +@@ -1946,7 +1978,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/SOURCES/openssh-8.7p1-upstream-cve-2021-41617.patch b/SOURCES/openssh-8.7p1-upstream-cve-2021-41617.patch new file mode 100644 index 0000000..a68aebd --- /dev/null +++ b/SOURCES/openssh-8.7p1-upstream-cve-2021-41617.patch @@ -0,0 +1,25 @@ +diff --git a/auth.c b/auth.c +index b8d1040d..0134d694 100644 +--- a/auth.c ++++ b/auth.c +@@ -56,6 +56,7 @@ + # include + #endif + #include ++#include + #ifdef HAVE_LOGIN_H + #include + #endif +@@ -2695,6 +2696,12 @@ subprocess(const char *tag, const char *command, + } + closefrom(STDERR_FILENO + 1); + ++ if (geteuid() == 0 && ++ initgroups(pw->pw_name, pw->pw_gid) == -1) { ++ error("%s: initgroups(%s, %u): %s", tag, ++ pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); ++ _exit(1); ++ } + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, diff --git a/SPECS/openssh.spec b/SPECS/openssh.spec index ab3b01e..03de9c6 100644 --- a/SPECS/openssh.spec +++ b/SPECS/openssh.spec @@ -66,7 +66,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 8.0p1 -%global openssh_rel 10 +%global openssh_rel 13 %global pam_ssh_agent_ver 0.10.3 %global pam_ssh_agent_rel 7 @@ -245,6 +245,18 @@ Patch976: openssh-8.0p1-restore-nonblock.patch Patch977: openssh-8.0p1-cve-2020-14145.patch # sshd -T requires -C when "Match" is used in sshd_config (#1836277) Patch978: openssh-8.0p1-sshd_config.patch +# CVE-2021-41617 +Patch980: openssh-8.7p1-upstream-cve-2021-41617.patch +# support sshd Include directive +# upstream commits: +# c2bd7f74b0e0f3a3ee9d19ac549e6ba89013abaf~1..677d0ece67634262b3b96c3cd6410b19f3a603b7 +# 8bdc3bb7cf4c82c3344cfcb82495a43406e87e83 +# 47adfdc07f4f8ea0064a1495500244de08d311ed~1..7af1e92cd289b7eaa9a683e9a6f2fddd98f37a01 +Patch981: openssh-8.0p1-sshd_include.patch +# Port upstream ClientAliveCountMax behaviour +# upstream commit: +# 69334996ae203c51c70bf01d414c918a44618f8e +Patch982: openssh-8.0p1-client_alive_count_max.patch License: BSD Group: Applications/Internet @@ -470,6 +482,9 @@ popd %patch976 -p1 -b .restore-nonblock %patch977 -p1 -b .cve-2020-14145 %patch978 -p1 -b .sshd_config +%patch980 -p1 -b .cve-2021-41617 +%patch981 -p1 -b .sshdinclude +%patch982 -p1 -b .client_alive_count_max %patch200 -p1 -b .audit %patch201 -p1 -b .audit-race @@ -761,6 +776,15 @@ getent passwd sshd >/dev/null || \ %endif %changelog +* Tue Oct 26 2021 Dmitry Belyavskiy - 8.0p1-13 +- Upstream: ClientAliveCountMax=0 disable the connection killing behaviour (#2015828) + +* Wed Oct 20 2021 Dmitry Belyavskiy - 8.0p1-12 +- Add support for "Include" directive in sshd_config file (#1926103) + +* Fri Oct 01 2021 Dmitry Belyavskiy - 8.0p1-11 +- CVE-2021-41617 upstream fix (#2008885) + * Mon Jun 21 2021 Dmitry Belyavskiy - 8.0p1-10 - sshd -T requires -C when "Match" is used in sshd_config (#1836277)