]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: add a ssh_config "Match final" predicate
authordjm@openbsd.org <djm@openbsd.org>
Fri, 23 Nov 2018 05:08:07 +0000 (05:08 +0000)
committerDamien Miller <djm@mindrot.org>
Fri, 23 Nov 2018 05:09:12 +0000 (16:09 +1100)
Matches in same pass as "Match canonical" but doesn't require
hostname canonicalisation be enabled. bz#2906 ok markus

OpenBSD-Commit-ID: fba1dfe9f6e0cabcd0e2b3be13f7a434199beffa

readconf.c
readconf.h
ssh-keysign.c
ssh.c
ssh_config.5

index 7850f2f59747c42da0e4ca9512111b9fed95c3ae..7331ef5ad6cae3f566785c8584f73dd75e3db007 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.301 2018/11/16 03:26:01 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.302 2018/11/23 05:08:07 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 
 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);
index fc7e3825101d0539bc36f0f9f7f088331ecfa599..8e36bf32adb34293c2a9d2d2a64f63ee64db44bb 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.128 2018/09/20 03:30:44 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.129 2018/11/23 05:08:07 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -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 *);
index 8f487b8c5436ef25dda57e41f988bc1eb8d6b409..7ea5ad0e99708642afcf5e3620a218217cb90ba6 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keysign.c,v 1.55 2018/07/27 05:34:42 dtucker Exp $ */
+/* $OpenBSD: ssh-keysign.c,v 1.56 2018/11/23 05:08:07 djm Exp $ */
 /*
  * Copyright (c) 2002 Markus Friedl.  All rights reserved.
  *
@@ -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 1ac903d16c9c6575bc7b14284c74ec9db528658a..c6cb7847ded30ea74d5d04901467def21981c07b 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.495 2018/10/23 05:56:35 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.496 2018/11/23 05:08:07 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -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
index 4d5b01d3eaceea637d478cc6dfbe09f52bd162a5..58a5fa1c8fba9d766fc08c6edb7559dcce5cbb3a 100644 (file)
@@ -33,8 +33,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh_config.5,v 1.286 2018/10/03 06:38:35 djm Exp $
-.Dd $Mdocdate: October 3 2018 $
+.\" $OpenBSD: ssh_config.5,v 1.287 2018/11/23 05:08:07 djm Exp $
+.Dd $Mdocdate: November 23 2018 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -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.