summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-11-23 05:08:07 +0000
committerDamien Miller <djm@mindrot.org>2018-11-23 16:09:12 +1100
commit9e34e0c59ab04514f9de9934a772283f7f372afe (patch)
tree4306e1438b1efe0283b635d4d4ed1256cff0fe59
parent4da58d58736b065b1182b563d10ad6765d811c6d (diff)
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
-rw-r--r--readconf.c44
-rw-r--r--readconf.h6
-rw-r--r--ssh-keysign.c5
-rw-r--r--ssh.c31
-rw-r--r--ssh_config.526
5 files changed, 76 insertions, 36 deletions
diff --git a/readconf.c b/readconf.c
index 7850f2f59..7331ef5ad 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.301 2018/11/16 03:26:01 djm Exp $ */ 1/* $OpenBSD: readconf.c,v 1.302 2018/11/23 05:08:07 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -133,10 +133,11 @@
133 133
134static int read_config_file_depth(const char *filename, struct passwd *pw, 134static int read_config_file_depth(const char *filename, struct passwd *pw,
135 const char *host, const char *original_host, Options *options, 135 const char *host, const char *original_host, Options *options,
136 int flags, int *activep, int depth); 136 int flags, int *activep, int *want_final_pass, int depth);
137static int process_config_line_depth(Options *options, struct passwd *pw, 137static int process_config_line_depth(Options *options, struct passwd *pw,
138 const char *host, const char *original_host, char *line, 138 const char *host, const char *original_host, char *line,
139 const char *filename, int linenum, int *activep, int flags, int depth); 139 const char *filename, int linenum, int *activep, int flags,
140 int *want_final_pass, int depth);
140 141
141/* Keyword tokens. */ 142/* Keyword tokens. */
142 143
@@ -539,8 +540,8 @@ execute_in_shell(const char *cmd)
539 */ 540 */
540static int 541static int
541match_cfg_line(Options *options, char **condition, struct passwd *pw, 542match_cfg_line(Options *options, char **condition, struct passwd *pw,
542 const char *host_arg, const char *original_host, int post_canon, 543 const char *host_arg, const char *original_host, int final_pass,
543 const char *filename, int linenum) 544 int *want_final_pass, const char *filename, int linenum)
544{ 545{
545 char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; 546 char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria;
546 const char *ruser; 547 const char *ruser;
@@ -554,7 +555,7 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
554 */ 555 */
555 port = options->port <= 0 ? default_ssh_port() : options->port; 556 port = options->port <= 0 ? default_ssh_port() : options->port;
556 ruser = options->user == NULL ? pw->pw_name : options->user; 557 ruser = options->user == NULL ? pw->pw_name : options->user;
557 if (post_canon) { 558 if (final_pass) {
558 host = xstrdup(options->hostname); 559 host = xstrdup(options->hostname);
559 } else if (options->hostname != NULL) { 560 } else if (options->hostname != NULL) {
560 /* NB. Please keep in sync with ssh.c:main() */ 561 /* NB. Please keep in sync with ssh.c:main() */
@@ -586,8 +587,16 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
586 goto out; 587 goto out;
587 } 588 }
588 attributes++; 589 attributes++;
589 if (strcasecmp(attrib, "canonical") == 0) { 590 if (strcasecmp(attrib, "canonical") == 0 ||
590 r = !!post_canon; /* force bitmask member to boolean */ 591 strcasecmp(attrib, "final") == 0) {
592 /*
593 * If the config requests "Match final" then remember
594 * this so we can perform a second pass later.
595 */
596 if (strcasecmp(attrib, "final") == 0 &&
597 want_final_pass != NULL)
598 *want_final_pass = 1;
599 r = !!final_pass; /* force bitmask member to boolean */
591 if (r == (negate ? 1 : 0)) 600 if (r == (negate ? 1 : 0))
592 this_result = result = 0; 601 this_result = result = 0;
593 debug3("%.200s line %d: %smatched '%s'", 602 debug3("%.200s line %d: %smatched '%s'",
@@ -824,14 +833,14 @@ process_config_line(Options *options, struct passwd *pw, const char *host,
824 int linenum, int *activep, int flags) 833 int linenum, int *activep, int flags)
825{ 834{
826 return process_config_line_depth(options, pw, host, original_host, 835 return process_config_line_depth(options, pw, host, original_host,
827 line, filename, linenum, activep, flags, 0); 836 line, filename, linenum, activep, flags, NULL, 0);
828} 837}
829 838
830#define WHITESPACE " \t\r\n" 839#define WHITESPACE " \t\r\n"
831static int 840static int
832process_config_line_depth(Options *options, struct passwd *pw, const char *host, 841process_config_line_depth(Options *options, struct passwd *pw, const char *host,
833 const char *original_host, char *line, const char *filename, 842 const char *original_host, char *line, const char *filename,
834 int linenum, int *activep, int flags, int depth) 843 int linenum, int *activep, int flags, int *want_final_pass, int depth)
835{ 844{
836 char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; 845 char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
837 char **cpptr, fwdarg[256]; 846 char **cpptr, fwdarg[256];
@@ -1339,7 +1348,8 @@ parse_keytypes:
1339 fatal("Host directive not supported as a command-line " 1348 fatal("Host directive not supported as a command-line "
1340 "option"); 1349 "option");
1341 value = match_cfg_line(options, &s, pw, host, original_host, 1350 value = match_cfg_line(options, &s, pw, host, original_host,
1342 flags & SSHCONF_POSTCANON, filename, linenum); 1351 flags & SSHCONF_FINAL, want_final_pass,
1352 filename, linenum);
1343 if (value < 0) 1353 if (value < 0)
1344 fatal("%.200s line %d: Bad Match condition", filename, 1354 fatal("%.200s line %d: Bad Match condition", filename,
1345 linenum); 1355 linenum);
@@ -1548,7 +1558,7 @@ parse_keytypes:
1548 pw, host, original_host, options, 1558 pw, host, original_host, options,
1549 flags | SSHCONF_CHECKPERM | 1559 flags | SSHCONF_CHECKPERM |
1550 (oactive ? 0 : SSHCONF_NEVERMATCH), 1560 (oactive ? 0 : SSHCONF_NEVERMATCH),
1551 activep, depth + 1); 1561 activep, want_final_pass, depth + 1);
1552 if (r != 1 && errno != ENOENT) { 1562 if (r != 1 && errno != ENOENT) {
1553 fatal("Can't open user config file " 1563 fatal("Can't open user config file "
1554 "%.100s: %.100s", gl.gl_pathv[i], 1564 "%.100s: %.100s", gl.gl_pathv[i],
@@ -1751,19 +1761,20 @@ parse_keytypes:
1751 */ 1761 */
1752int 1762int
1753read_config_file(const char *filename, struct passwd *pw, const char *host, 1763read_config_file(const char *filename, struct passwd *pw, const char *host,
1754 const char *original_host, Options *options, int flags) 1764 const char *original_host, Options *options, int flags,
1765 int *want_final_pass)
1755{ 1766{
1756 int active = 1; 1767 int active = 1;
1757 1768
1758 return read_config_file_depth(filename, pw, host, original_host, 1769 return read_config_file_depth(filename, pw, host, original_host,
1759 options, flags, &active, 0); 1770 options, flags, &active, want_final_pass, 0);
1760} 1771}
1761 1772
1762#define READCONF_MAX_DEPTH 16 1773#define READCONF_MAX_DEPTH 16
1763static int 1774static int
1764read_config_file_depth(const char *filename, struct passwd *pw, 1775read_config_file_depth(const char *filename, struct passwd *pw,
1765 const char *host, const char *original_host, Options *options, 1776 const char *host, const char *original_host, Options *options,
1766 int flags, int *activep, int depth) 1777 int flags, int *activep, int *want_final_pass, int depth)
1767{ 1778{
1768 FILE *f; 1779 FILE *f;
1769 char *line = NULL; 1780 char *line = NULL;
@@ -1798,7 +1809,8 @@ read_config_file_depth(const char *filename, struct passwd *pw,
1798 /* Update line number counter. */ 1809 /* Update line number counter. */
1799 linenum++; 1810 linenum++;
1800 if (process_config_line_depth(options, pw, host, original_host, 1811 if (process_config_line_depth(options, pw, host, original_host,
1801 line, filename, linenum, activep, flags, depth) != 0) 1812 line, filename, linenum, activep, flags, want_final_pass,
1813 depth) != 0)
1802 bad_options++; 1814 bad_options++;
1803 } 1815 }
1804 free(line); 1816 free(line);
diff --git a/readconf.h b/readconf.h
index fc7e38251..8e36bf32a 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.128 2018/09/20 03:30:44 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.129 2018/11/23 05:08:07 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -185,7 +185,7 @@ typedef struct {
185 185
186#define SSHCONF_CHECKPERM 1 /* check permissions on config file */ 186#define SSHCONF_CHECKPERM 1 /* check permissions on config file */
187#define SSHCONF_USERCONF 2 /* user provided config file not system */ 187#define SSHCONF_USERCONF 2 /* user provided config file not system */
188#define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */ 188#define SSHCONF_FINAL 4 /* Final pass over config, after canon. */
189#define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ 189#define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */
190 190
191#define SSH_UPDATE_HOSTKEYS_NO 0 191#define SSH_UPDATE_HOSTKEYS_NO 0
@@ -203,7 +203,7 @@ void fill_default_options_for_canonicalization(Options *);
203int process_config_line(Options *, struct passwd *, const char *, 203int process_config_line(Options *, struct passwd *, const char *,
204 const char *, char *, const char *, int, int *, int); 204 const char *, char *, const char *, int, int *, int);
205int read_config_file(const char *, struct passwd *, const char *, 205int read_config_file(const char *, struct passwd *, const char *,
206 const char *, Options *, int); 206 const char *, Options *, int, int *);
207int parse_forward(struct Forward *, const char *, int, int); 207int parse_forward(struct Forward *, const char *, int, int);
208int parse_jump(const char *, Options *, int); 208int parse_jump(const char *, Options *, int);
209int parse_ssh_uri(const char *, char **, char **, int *); 209int parse_ssh_uri(const char *, char **, char **, int *);
diff --git a/ssh-keysign.c b/ssh-keysign.c
index 8f487b8c5..7ea5ad0e9 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keysign.c,v 1.55 2018/07/27 05:34:42 dtucker Exp $ */ 1/* $OpenBSD: ssh-keysign.c,v 1.56 2018/11/23 05:08:07 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2002 Markus Friedl. All rights reserved. 3 * Copyright (c) 2002 Markus Friedl. All rights reserved.
4 * 4 *
@@ -208,7 +208,8 @@ main(int argc, char **argv)
208 208
209 /* verify that ssh-keysign is enabled by the admin */ 209 /* verify that ssh-keysign is enabled by the admin */
210 initialize_options(&options); 210 initialize_options(&options);
211 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0); 211 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "",
212 &options, 0, NULL);
212 fill_default_options(&options); 213 fill_default_options(&options);
213 if (options.enable_ssh_keysign != 1) 214 if (options.enable_ssh_keysign != 1)
214 fatal("ssh-keysign not enabled in %s", 215 fatal("ssh-keysign not enabled in %s",
diff --git a/ssh.c b/ssh.c
index 1ac903d16..c6cb7847d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.495 2018/10/23 05:56:35 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.496 2018/11/23 05:08:07 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * 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)
527 * file if the user specifies a config file on the command line. 527 * file if the user specifies a config file on the command line.
528 */ 528 */
529static void 529static void
530process_config_files(const char *host_name, struct passwd *pw, int post_canon) 530process_config_files(const char *host_name, struct passwd *pw, int final_pass,
531 int *want_final_pass)
531{ 532{
532 char buf[PATH_MAX]; 533 char buf[PATH_MAX];
533 int r; 534 int r;
@@ -535,7 +536,8 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon)
535 if (config != NULL) { 536 if (config != NULL) {
536 if (strcasecmp(config, "none") != 0 && 537 if (strcasecmp(config, "none") != 0 &&
537 !read_config_file(config, pw, host, host_name, &options, 538 !read_config_file(config, pw, host, host_name, &options,
538 SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0))) 539 SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0),
540 want_final_pass))
539 fatal("Can't open user config file %.100s: " 541 fatal("Can't open user config file %.100s: "
540 "%.100s", config, strerror(errno)); 542 "%.100s", config, strerror(errno));
541 } else { 543 } else {
@@ -544,12 +546,12 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon)
544 if (r > 0 && (size_t)r < sizeof(buf)) 546 if (r > 0 && (size_t)r < sizeof(buf))
545 (void)read_config_file(buf, pw, host, host_name, 547 (void)read_config_file(buf, pw, host, host_name,
546 &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | 548 &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF |
547 (post_canon ? SSHCONF_POSTCANON : 0)); 549 (final_pass ? SSHCONF_FINAL : 0), want_final_pass);
548 550
549 /* Read systemwide configuration file after user config. */ 551 /* Read systemwide configuration file after user config. */
550 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, 552 (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw,
551 host, host_name, &options, 553 host, host_name, &options,
552 post_canon ? SSHCONF_POSTCANON : 0); 554 final_pass ? SSHCONF_FINAL : 0, want_final_pass);
553 } 555 }
554} 556}
555 557
@@ -581,7 +583,7 @@ main(int ac, char **av)
581{ 583{
582 struct ssh *ssh = NULL; 584 struct ssh *ssh = NULL;
583 int i, r, opt, exit_status, use_syslog, direct, timeout_ms; 585 int i, r, opt, exit_status, use_syslog, direct, timeout_ms;
584 int was_addr, config_test = 0, opt_terminated = 0; 586 int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0;
585 char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile; 587 char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile;
586 char cname[NI_MAXHOST]; 588 char cname[NI_MAXHOST];
587 struct stat st; 589 struct stat st;
@@ -1089,7 +1091,9 @@ main(int ac, char **av)
1089 ); 1091 );
1090 1092
1091 /* Parse the configuration files */ 1093 /* Parse the configuration files */
1092 process_config_files(host_arg, pw, 0); 1094 process_config_files(host_arg, pw, 0, &want_final_pass);
1095 if (want_final_pass)
1096 debug("configuration requests final Match pass");
1093 1097
1094 /* Hostname canonicalisation needs a few options filled. */ 1098 /* Hostname canonicalisation needs a few options filled. */
1095 fill_default_options_for_canonicalization(&options); 1099 fill_default_options_for_canonicalization(&options);
@@ -1146,12 +1150,17 @@ main(int ac, char **av)
1146 * If canonicalisation is enabled then re-parse the configuration 1150 * If canonicalisation is enabled then re-parse the configuration
1147 * files as new stanzas may match. 1151 * files as new stanzas may match.
1148 */ 1152 */
1149 if (options.canonicalize_hostname != 0) { 1153 if (options.canonicalize_hostname != 0 && !want_final_pass) {
1150 debug("Re-reading configuration after hostname " 1154 debug("hostname canonicalisation enabled, "
1151 "canonicalisation"); 1155 "will re-parse configuration");
1156 want_final_pass = 1;
1157 }
1158
1159 if (want_final_pass) {
1160 debug("re-parsing configuration");
1152 free(options.hostname); 1161 free(options.hostname);
1153 options.hostname = xstrdup(host); 1162 options.hostname = xstrdup(host);
1154 process_config_files(host_arg, pw, 1); 1163 process_config_files(host_arg, pw, 1, NULL);
1155 /* 1164 /*
1156 * Address resolution happens early with canonicalisation 1165 * Address resolution happens early with canonicalisation
1157 * enabled and the port number may have changed since, so 1166 * enabled and the port number may have changed since, so
diff --git a/ssh_config.5 b/ssh_config.5
index 4d5b01d3e..58a5fa1c8 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh_config.5,v 1.286 2018/10/03 06:38:35 djm Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.287 2018/11/23 05:08:07 djm Exp $
37.Dd $Mdocdate: October 3 2018 $ 37.Dd $Mdocdate: November 23 2018 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -139,6 +139,7 @@ or the single token
139which always matches. 139which always matches.
140The available criteria keywords are: 140The available criteria keywords are:
141.Cm canonical , 141.Cm canonical ,
142.Cm final ,
142.Cm exec , 143.Cm exec ,
143.Cm host , 144.Cm host ,
144.Cm originalhost , 145.Cm originalhost ,
@@ -148,12 +149,15 @@ and
148The 149The
149.Cm all 150.Cm all
150criteria must appear alone or immediately after 151criteria must appear alone or immediately after
151.Cm canonical . 152.Cm canonical
153or
154.Cm final .
152Other criteria may be combined arbitrarily. 155Other criteria may be combined arbitrarily.
153All criteria but 156All criteria but
154.Cm all 157.Cm all
155and
156.Cm canonical 158.Cm canonical
159and
160.Cm final
157require an argument. 161require an argument.
158Criteria may be negated by prepending an exclamation mark 162Criteria may be negated by prepending an exclamation mark
159.Pq Sq !\& . 163.Pq Sq !\& .
@@ -166,6 +170,20 @@ after hostname canonicalization (see the
166option.) 170option.)
167This may be useful to specify conditions that work with canonical host 171This may be useful to specify conditions that work with canonical host
168names only. 172names only.
173.Pp
174The
175.Cm final
176keyword requests that the configuration be re-parsed (regardless of whether
177.Cm CanonicalizeHostname
178is enabled), and matches only during this final pass.
179If
180.Cm CanonicalizeHostname
181is enabled, then
182.Cm canonical
183and
184.Cm final
185match during the same pass.
186.Pp
169The 187The
170.Cm exec 188.Cm exec
171keyword executes the specified command under the user's shell. 189keyword executes the specified command under the user's shell.