diff --git a/openssh-7.9p1-match-final.patch b/openssh-7.9p1-match-final.patch new file mode 100644 index 0000000..4c28fcf --- /dev/null +++ b/openssh-7.9p1-match-final.patch @@ -0,0 +1,306 @@ +commit 9e34e0c59ab04514f9de9934a772283f7f372afe +Author: djm@openbsd.org +Date: Fri Nov 23 05:08:07 2018 +0000 + + upstream: add a ssh_config "Match final" predicate + + Matches in same pass as "Match canonical" but doesn't require + hostname canonicalisation be enabled. bz#2906 ok markus + + OpenBSD-Commit-ID: fba1dfe9f6e0cabcd0e2b3be13f7a434199beffa + +diff --git a/readconf.c b/readconf.c +index 7850f2f5..7331ef5a 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -133,10 +133,11 @@ + + static int read_config_file_depth(const char *filename, struct passwd *pw, + const char *host, const char *original_host, Options *options, +- int flags, int *activep, int depth); ++ int flags, int *activep, int *want_final_pass, int depth); + static int process_config_line_depth(Options *options, struct passwd *pw, + const char *host, const char *original_host, char *line, +- const char *filename, int linenum, int *activep, int flags, int depth); ++ const char *filename, int linenum, int *activep, int flags, ++ int *want_final_pass, int depth); + + /* Keyword tokens. */ + +@@ -539,8 +540,8 @@ execute_in_shell(const char *cmd) + */ + static int + match_cfg_line(Options *options, char **condition, struct passwd *pw, +- const char *host_arg, const char *original_host, int post_canon, +- const char *filename, int linenum) ++ const char *host_arg, const char *original_host, int final_pass, ++ int *want_final_pass, const char *filename, int linenum) + { + char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; + const char *ruser; +@@ -554,7 +555,7 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, + */ + port = options->port <= 0 ? default_ssh_port() : options->port; + ruser = options->user == NULL ? pw->pw_name : options->user; +- if (post_canon) { ++ if (final_pass) { + host = xstrdup(options->hostname); + } else if (options->hostname != NULL) { + /* NB. Please keep in sync with ssh.c:main() */ +@@ -586,8 +587,16 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, + goto out; + } + attributes++; +- if (strcasecmp(attrib, "canonical") == 0) { +- r = !!post_canon; /* force bitmask member to boolean */ ++ if (strcasecmp(attrib, "canonical") == 0 || ++ strcasecmp(attrib, "final") == 0) { ++ /* ++ * If the config requests "Match final" then remember ++ * this so we can perform a second pass later. ++ */ ++ if (strcasecmp(attrib, "final") == 0 && ++ want_final_pass != NULL) ++ *want_final_pass = 1; ++ r = !!final_pass; /* force bitmask member to boolean */ + if (r == (negate ? 1 : 0)) + this_result = result = 0; + debug3("%.200s line %d: %smatched '%s'", +@@ -824,14 +833,14 @@ process_config_line(Options *options, struct passwd *pw, const char *host, + int linenum, int *activep, int flags) + { + return process_config_line_depth(options, pw, host, original_host, +- line, filename, linenum, activep, flags, 0); ++ line, filename, linenum, activep, flags, NULL, 0); + } + + #define WHITESPACE " \t\r\n" + static int + process_config_line_depth(Options *options, struct passwd *pw, const char *host, + const char *original_host, char *line, const char *filename, +- int linenum, int *activep, int flags, int depth) ++ int linenum, int *activep, int flags, int *want_final_pass, int depth) + { + char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; + char **cpptr, fwdarg[256]; +@@ -1339,7 +1348,8 @@ parse_keytypes: + fatal("Host directive not supported as a command-line " + "option"); + value = match_cfg_line(options, &s, pw, host, original_host, +- flags & SSHCONF_POSTCANON, filename, linenum); ++ flags & SSHCONF_FINAL, want_final_pass, ++ filename, linenum); + if (value < 0) + fatal("%.200s line %d: Bad Match condition", filename, + linenum); +@@ -1548,7 +1558,7 @@ parse_keytypes: + pw, host, original_host, options, + flags | SSHCONF_CHECKPERM | + (oactive ? 0 : SSHCONF_NEVERMATCH), +- activep, depth + 1); ++ activep, want_final_pass, depth + 1); + if (r != 1 && errno != ENOENT) { + fatal("Can't open user config file " + "%.100s: %.100s", gl.gl_pathv[i], +@@ -1751,19 +1761,20 @@ parse_keytypes: + */ + int + read_config_file(const char *filename, struct passwd *pw, const char *host, +- const char *original_host, Options *options, int flags) ++ const char *original_host, Options *options, int flags, ++ int *want_final_pass) + { + int active = 1; + + return read_config_file_depth(filename, pw, host, original_host, +- options, flags, &active, 0); ++ options, flags, &active, want_final_pass, 0); + } + + #define READCONF_MAX_DEPTH 16 + static int + read_config_file_depth(const char *filename, struct passwd *pw, + const char *host, const char *original_host, Options *options, +- int flags, int *activep, int depth) ++ int flags, int *activep, int *want_final_pass, int depth) + { + FILE *f; + char *line = NULL; +@@ -1798,7 +1809,8 @@ read_config_file_depth(const char *filename, struct passwd *pw, + /* Update line number counter. */ + linenum++; + if (process_config_line_depth(options, pw, host, original_host, +- line, filename, linenum, activep, flags, depth) != 0) ++ line, filename, linenum, activep, flags, want_final_pass, ++ depth) != 0) + bad_options++; + } + free(line); +diff --git a/readconf.h b/readconf.h +index fc7e3825..8e36bf32 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -185,7 +185,7 @@ typedef struct { + + #define SSHCONF_CHECKPERM 1 /* check permissions on config file */ + #define SSHCONF_USERCONF 2 /* user provided config file not system */ +-#define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */ ++#define SSHCONF_FINAL 4 /* Final pass over config, after canon. */ + #define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ + + #define SSH_UPDATE_HOSTKEYS_NO 0 +@@ -203,7 +203,7 @@ void fill_default_options_for_canonicalization(Options *); + int process_config_line(Options *, struct passwd *, const char *, + const char *, char *, const char *, int, int *, int); + int read_config_file(const char *, struct passwd *, const char *, +- const char *, Options *, int); ++ const char *, Options *, int, int *); + int parse_forward(struct Forward *, const char *, int, int); + int parse_jump(const char *, Options *, int); + int parse_ssh_uri(const char *, char **, char **, int *); +diff --git a/ssh-keysign.c b/ssh-keysign.c +index 8f487b8c..7ea5ad0e 100644 +--- a/ssh-keysign.c ++++ b/ssh-keysign.c +@@ -208,7 +208,8 @@ main(int argc, char **argv) + + /* verify that ssh-keysign is enabled by the admin */ + initialize_options(&options); +- (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0); ++ (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", ++ &options, 0, NULL); + fill_default_options(&options); + if (options.enable_ssh_keysign != 1) + fatal("ssh-keysign not enabled in %s", +diff --git a/ssh.c b/ssh.c +index 1ac903d1..c6cb7847 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -527,7 +527,8 @@ check_load(int r, const char *path, const char *message) + * file if the user specifies a config file on the command line. + */ + static void +-process_config_files(const char *host_name, struct passwd *pw, int post_canon) ++process_config_files(const char *host_name, struct passwd *pw, int final_pass, ++ int *want_final_pass) + { + char buf[PATH_MAX]; + int r; +@@ -535,7 +536,8 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon) + if (config != NULL) { + if (strcasecmp(config, "none") != 0 && + !read_config_file(config, pw, host, host_name, &options, +- SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0))) ++ SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), ++ want_final_pass)) + fatal("Can't open user config file %.100s: " + "%.100s", config, strerror(errno)); + } else { +@@ -544,12 +546,12 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon) + if (r > 0 && (size_t)r < sizeof(buf)) + (void)read_config_file(buf, pw, host, host_name, + &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | +- (post_canon ? SSHCONF_POSTCANON : 0)); ++ (final_pass ? SSHCONF_FINAL : 0), want_final_pass); + + /* Read systemwide configuration file after user config. */ + (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, + host, host_name, &options, +- post_canon ? SSHCONF_POSTCANON : 0); ++ final_pass ? SSHCONF_FINAL : 0, want_final_pass); + } + } + +@@ -581,7 +583,7 @@ main(int ac, char **av) + { + struct ssh *ssh = NULL; + int i, r, opt, exit_status, use_syslog, direct, timeout_ms; +- int was_addr, config_test = 0, opt_terminated = 0; ++ int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; + char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile; + char cname[NI_MAXHOST]; + struct stat st; +@@ -1089,7 +1091,9 @@ main(int ac, char **av) + ); + + /* Parse the configuration files */ +- process_config_files(host_arg, pw, 0); ++ process_config_files(host_arg, pw, 0, &want_final_pass); ++ if (want_final_pass) ++ debug("configuration requests final Match pass"); + + /* Hostname canonicalisation needs a few options filled. */ + fill_default_options_for_canonicalization(&options); +@@ -1146,12 +1150,17 @@ main(int ac, char **av) + * If canonicalisation is enabled then re-parse the configuration + * files as new stanzas may match. + */ +- if (options.canonicalize_hostname != 0) { +- debug("Re-reading configuration after hostname " +- "canonicalisation"); ++ if (options.canonicalize_hostname != 0 && !want_final_pass) { ++ debug("hostname canonicalisation enabled, " ++ "will re-parse configuration"); ++ want_final_pass = 1; ++ } ++ ++ if (want_final_pass) { ++ debug("re-parsing configuration"); + free(options.hostname); + options.hostname = xstrdup(host); +- process_config_files(host_arg, pw, 1); ++ process_config_files(host_arg, pw, 1, NULL); + /* + * Address resolution happens early with canonicalisation + * enabled and the port number may have changed since, so +diff --git a/ssh_config.5 b/ssh_config.5 +index 4d5b01d3..58a5fa1c 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -139,6 +139,7 @@ or the single token + which always matches. + The available criteria keywords are: + .Cm canonical , ++.Cm final , + .Cm exec , + .Cm host , + .Cm originalhost , +@@ -148,12 +149,15 @@ and + The + .Cm all + criteria must appear alone or immediately after +-.Cm canonical . ++.Cm canonical ++or ++.Cm final . + Other criteria may be combined arbitrarily. + All criteria but + .Cm all +-and + .Cm canonical ++and ++.Cm final + require an argument. + Criteria may be negated by prepending an exclamation mark + .Pq Sq !\& . +@@ -166,6 +170,20 @@ after hostname canonicalization (see the + option.) + This may be useful to specify conditions that work with canonical host + names only. ++.Pp ++The ++.Cm final ++keyword requests that the configuration be re-parsed (regardless of whether ++.Cm CanonicalizeHostname ++is enabled), and matches only during this final pass. ++If ++.Cm CanonicalizeHostname ++is enabled, then ++.Cm canonical ++and ++.Cm final ++match during the same pass. ++.Pp + The + .Cm exec + keyword executes the specified command under the user's shell. diff --git a/openssh.spec b/openssh.spec index ea08cfe..2b9e369 100644 --- a/openssh.spec +++ b/openssh.spec @@ -226,6 +226,9 @@ Patch953: openssh-7.8p1-scp-ipv6.patch # Allow to disable RSA signatures with SHA-1 in server # https://bugzilla.mindrot.org/show_bug.cgi?id=2746 Patch954: openssh-7.9p1-disable-sha1.patch +# Backport Match final so the crypto-policies do not break canonicalization (#1630166) +# https://bugzilla.mindrot.org/show_bug.cgi?id=2906 +Patch955: openssh-7.9p1-match-final.patch License: BSD Group: Applications/Internet @@ -450,6 +453,7 @@ popd %patch953 -p1 -b .scp-ipv6 %patch808 -p1 -b .gsskex-method %patch954 -p1 -b .disable-sha1 +%patch955 -p1 -b .match-final %patch200 -p1 -b .audit %patch201 -p1 -b .audit-race