summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--misc.c63
-rw-r--r--misc.h3
-rw-r--r--readconf.c95
-rw-r--r--readconf.h8
-rw-r--r--ssh.124
-rw-r--r--ssh.c77
-rw-r--r--ssh_config.528
7 files changed, 271 insertions, 27 deletions
diff --git a/misc.c b/misc.c
index 9d59ca6b2..9421b4d39 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.104 2016/04/06 06:42:17 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.105 2016/07/15 00:24:30 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -451,6 +451,67 @@ colon(char *cp)
451 return NULL; 451 return NULL;
452} 452}
453 453
454/*
455 * Parse a [user@]host[:port] string.
456 * Caller must free returned user and host.
457 * Any of the pointer return arguments may be NULL (useful for syntax checking).
458 * If user was not specified then *userp will be set to NULL.
459 * If port was not specified then *portp will be -1.
460 * Returns 0 on success, -1 on failure.
461 */
462int
463parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
464{
465 char *sdup, *cp, *tmp;
466 char *user = NULL, *host = NULL;
467 int port = -1, ret = -1;
468
469 if (userp != NULL)
470 *userp = NULL;
471 if (hostp != NULL)
472 *hostp = NULL;
473 if (portp != NULL)
474 *portp = -1;
475
476 if ((sdup = tmp = strdup(s)) == NULL)
477 return -1;
478 /* Extract optional username */
479 if ((cp = strchr(tmp, '@')) != NULL) {
480 *cp = '\0';
481 if (*tmp == '\0')
482 goto out;
483 if ((user = strdup(tmp)) == NULL)
484 goto out;
485 tmp = cp + 1;
486 }
487 /* Extract mandatory hostname */
488 if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0')
489 goto out;
490 host = xstrdup(cleanhostname(cp));
491 /* Convert and verify optional port */
492 if (tmp != NULL && *tmp != '\0') {
493 if ((port = a2port(tmp)) <= 0)
494 goto out;
495 }
496 /* Success */
497 if (userp != NULL) {
498 *userp = user;
499 user = NULL;
500 }
501 if (hostp != NULL) {
502 *hostp = host;
503 host = NULL;
504 }
505 if (portp != NULL)
506 *portp = port;
507 ret = 0;
508 out:
509 free(sdup);
510 free(user);
511 free(host);
512 return ret;
513}
514
454/* function to assist building execv() arguments */ 515/* function to assist building execv() arguments */
455void 516void
456addargs(arglist *args, char *fmt, ...) 517addargs(arglist *args, char *fmt, ...)
diff --git a/misc.h b/misc.h
index 01432ba8b..7c76a6a72 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.h,v 1.56 2016/04/06 06:42:17 djm Exp $ */ 1/* $OpenBSD: misc.h,v 1.57 2016/07/15 00:24:30 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -49,6 +49,7 @@ char *put_host_port(const char *, u_short);
49char *hpdelim(char **); 49char *hpdelim(char **);
50char *cleanhostname(char *); 50char *cleanhostname(char *);
51char *colon(char *); 51char *colon(char *);
52int parse_user_host_port(const char *, char **, char **, int *);
52long convtime(const char *); 53long convtime(const char *);
53char *tilde_expand_filename(const char *, uid_t); 54char *tilde_expand_filename(const char *, uid_t);
54char *percent_expand(const char *, ...) __attribute__((__sentinel__)); 55char *percent_expand(const char *, ...) __attribute__((__sentinel__));
diff --git a/readconf.c b/readconf.c
index 9dcc383da..cb2999d82 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.256 2016/06/03 04:09:38 dtucker Exp $ */ 1/* $OpenBSD: readconf.c,v 1.257 2016/07/15 00:24:30 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
@@ -170,7 +170,7 @@ typedef enum {
170 oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, 170 oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
171 oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, 171 oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
172 oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, 172 oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
173 oPubkeyAcceptedKeyTypes, 173 oPubkeyAcceptedKeyTypes, oProxyJump,
174 oIgnoredUnknownOption, oDeprecated, oUnsupported 174 oIgnoredUnknownOption, oDeprecated, oUnsupported
175} OpCodes; 175} OpCodes;
176 176
@@ -295,6 +295,7 @@ static struct {
295 { "hostbasedkeytypes", oHostbasedKeyTypes }, 295 { "hostbasedkeytypes", oHostbasedKeyTypes },
296 { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, 296 { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
297 { "ignoreunknown", oIgnoreUnknown }, 297 { "ignoreunknown", oIgnoreUnknown },
298 { "proxyjump", oProxyJump },
298 299
299 { NULL, oBadOption } 300 { NULL, oBadOption }
300}; 301};
@@ -1121,6 +1122,9 @@ parse_char_array:
1121 1122
1122 case oProxyCommand: 1123 case oProxyCommand:
1123 charptr = &options->proxy_command; 1124 charptr = &options->proxy_command;
1125 /* Ignore ProxyCommand if ProxyJump already specified */
1126 if (options->jump_host != NULL)
1127 charptr = &options->jump_host; /* Skip below */
1124parse_command: 1128parse_command:
1125 if (s == NULL) 1129 if (s == NULL)
1126 fatal("%.200s line %d: Missing argument.", filename, linenum); 1130 fatal("%.200s line %d: Missing argument.", filename, linenum);
@@ -1129,6 +1133,18 @@ parse_command:
1129 *charptr = xstrdup(s + len); 1133 *charptr = xstrdup(s + len);
1130 return 0; 1134 return 0;
1131 1135
1136 case oProxyJump:
1137 if (s == NULL) {
1138 fatal("%.200s line %d: Missing argument.",
1139 filename, linenum);
1140 }
1141 len = strspn(s, WHITESPACE "=");
1142 if (parse_jump(s + len, options, *activep) == -1) {
1143 fatal("%.200s line %d: Invalid ProxyJump \"%s\"",
1144 filename, linenum, s + len);
1145 }
1146 return 0;
1147
1132 case oPort: 1148 case oPort:
1133 intptr = &options->port; 1149 intptr = &options->port;
1134parse_int: 1150parse_int:
@@ -1789,6 +1805,10 @@ initialize_options(Options * options)
1789 options->hostname = NULL; 1805 options->hostname = NULL;
1790 options->host_key_alias = NULL; 1806 options->host_key_alias = NULL;
1791 options->proxy_command = NULL; 1807 options->proxy_command = NULL;
1808 options->jump_user = NULL;
1809 options->jump_host = NULL;
1810 options->jump_port = -1;
1811 options->jump_extra = NULL;
1792 options->user = NULL; 1812 options->user = NULL;
1793 options->escape_char = -1; 1813 options->escape_char = -1;
1794 options->num_system_hostfiles = 0; 1814 options->num_system_hostfiles = 0;
@@ -2261,6 +2281,44 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo
2261 return (0); 2281 return (0);
2262} 2282}
2263 2283
2284int
2285parse_jump(const char *s, Options *o, int active)
2286{
2287 char *orig, *sdup, *cp;
2288 char *host = NULL, *user = NULL;
2289 int ret = -1, port = -1;
2290
2291 active &= o->proxy_command == NULL && o->jump_host == NULL;
2292
2293 orig = sdup = xstrdup(s);
2294 while ((cp = strsep(&sdup, ",")) && cp != NULL) {
2295 if (active) {
2296 /* First argument and configuration is active */
2297 if (parse_user_host_port(cp, &user, &host, &port) != 0)
2298 goto out;
2299 } else {
2300 /* Subsequent argument or inactive configuration */
2301 if (parse_user_host_port(cp, NULL, NULL, NULL) != 0)
2302 goto out;
2303 }
2304 active = 0; /* only check syntax for subsequent hosts */
2305 }
2306 /* success */
2307 free(orig);
2308 o->jump_user = user;
2309 o->jump_host = host;
2310 o->jump_port = port;
2311 o->proxy_command = xstrdup("none");
2312 user = host = NULL;
2313 if ((cp = strchr(s, ',')) != NULL && cp[1] != '\0')
2314 o->jump_extra = xstrdup(cp + 1);
2315 ret = 0;
2316 out:
2317 free(user);
2318 free(host);
2319 return ret;
2320}
2321
2264/* XXX the following is a near-vebatim copy from servconf.c; refactor */ 2322/* XXX the following is a near-vebatim copy from servconf.c; refactor */
2265static const char * 2323static const char *
2266fmt_multistate_int(int val, const struct multistate *m) 2324fmt_multistate_int(int val, const struct multistate *m)
@@ -2412,7 +2470,7 @@ void
2412dump_client_config(Options *o, const char *host) 2470dump_client_config(Options *o, const char *host)
2413{ 2471{
2414 int i; 2472 int i;
2415 char vbuf[5]; 2473 char buf[8];
2416 2474
2417 /* This is normally prepared in ssh_kex2 */ 2475 /* This is normally prepared in ssh_kex2 */
2418 if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0) 2476 if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0)
@@ -2490,7 +2548,6 @@ dump_client_config(Options *o, const char *host)
2490 dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC); 2548 dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC);
2491 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); 2549 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
2492 dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); 2550 dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
2493 dump_cfg_string(oProxyCommand, o->proxy_command);
2494 dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types); 2551 dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types);
2495 dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); 2552 dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
2496 dump_cfg_string(oXAuthLocation, o->xauth_location); 2553 dump_cfg_string(oXAuthLocation, o->xauth_location);
@@ -2551,8 +2608,8 @@ dump_client_config(Options *o, const char *host)
2551 if (o->escape_char == SSH_ESCAPECHAR_NONE) 2608 if (o->escape_char == SSH_ESCAPECHAR_NONE)
2552 printf("escapechar none\n"); 2609 printf("escapechar none\n");
2553 else { 2610 else {
2554 vis(vbuf, o->escape_char, VIS_WHITE, 0); 2611 vis(buf, o->escape_char, VIS_WHITE, 0);
2555 printf("escapechar %s\n", vbuf); 2612 printf("escapechar %s\n", buf);
2556 } 2613 }
2557 2614
2558 /* oIPQoS */ 2615 /* oIPQoS */
@@ -2566,4 +2623,30 @@ dump_client_config(Options *o, const char *host)
2566 /* oStreamLocalBindMask */ 2623 /* oStreamLocalBindMask */
2567 printf("streamlocalbindmask 0%o\n", 2624 printf("streamlocalbindmask 0%o\n",
2568 o->fwd_opts.streamlocal_bind_mask); 2625 o->fwd_opts.streamlocal_bind_mask);
2626
2627 /* oProxyCommand / oProxyJump */
2628 if (o->jump_host == NULL)
2629 dump_cfg_string(oProxyCommand, o->proxy_command);
2630 else {
2631 /* Check for numeric addresses */
2632 i = strchr(o->jump_host, ':') != NULL ||
2633 strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
2634 snprintf(buf, sizeof(buf), "%d", o->jump_port);
2635 printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
2636 /* optional user */
2637 o->jump_user == NULL ? "" : o->jump_user,
2638 o->jump_user == NULL ? "" : "@",
2639 /* opening [ if hostname is numeric */
2640 i ? "[" : "",
2641 /* mandatory hostname */
2642 o->jump_host,
2643 /* closing ] if hostname is numeric */
2644 i ? "]" : "",
2645 /* optional port number */
2646 o->jump_port <= 0 ? "" : ":",
2647 o->jump_port <= 0 ? "" : buf,
2648 /* optional additional jump spec */
2649 o->jump_extra == NULL ? "" : ",",
2650 o->jump_extra == NULL ? "" : o->jump_extra);
2651 }
2569} 2652}
diff --git a/readconf.h b/readconf.h
index a8b0b9188..cef55f71c 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.116 2016/06/03 03:14:41 dtucker Exp $ */ 1/* $OpenBSD: readconf.h,v 1.117 2016/07/15 00:24:30 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -163,6 +163,11 @@ typedef struct {
163 char *hostbased_key_types; 163 char *hostbased_key_types;
164 char *pubkey_key_types; 164 char *pubkey_key_types;
165 165
166 char *jump_user;
167 char *jump_host;
168 int jump_port;
169 char *jump_extra;
170
166 char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ 171 char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
167} Options; 172} Options;
168 173
@@ -198,6 +203,7 @@ int process_config_line(Options *, struct passwd *, const char *,
198int read_config_file(const char *, struct passwd *, const char *, 203int read_config_file(const char *, struct passwd *, const char *,
199 const char *, Options *, int); 204 const char *, Options *, int);
200int parse_forward(struct Forward *, const char *, int, int); 205int parse_forward(struct Forward *, const char *, int, int);
206int parse_jump(const char *, Options *, int);
201int default_ssh_port(void); 207int default_ssh_port(void);
202int option_clear_or_none(const char *); 208int option_clear_or_none(const char *);
203void dump_client_config(Options *o, const char *host); 209void dump_client_config(Options *o, const char *host);
diff --git a/ssh.1 b/ssh.1
index 32949b056..f3492b4d6 100644
--- a/ssh.1
+++ b/ssh.1
@@ -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.1,v 1.374 2016/06/29 17:14:28 jmc Exp $ 36.\" $OpenBSD: ssh.1,v 1.375 2016/07/15 00:24:30 djm Exp $
37.Dd $Mdocdate: June 29 2016 $ 37.Dd $Mdocdate: July 15 2016 $
38.Dt SSH 1 38.Dt SSH 1
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -52,6 +52,7 @@
52.Op Fl F Ar configfile 52.Op Fl F Ar configfile
53.Op Fl I Ar pkcs11 53.Op Fl I Ar pkcs11
54.Op Fl i Ar identity_file 54.Op Fl i Ar identity_file
55.Oo Fl J Ar user Ns @ Oc Ns Ar host Ns Op : Ns Ar port
55.Op Fl L Ar address 56.Op Fl L Ar address
56.Op Fl l Ar login_name 57.Op Fl l Ar login_name
57.Op Fl m Ar mac_spec 58.Op Fl m Ar mac_spec
@@ -312,6 +313,24 @@ by appending
312.Pa -cert.pub 313.Pa -cert.pub
313to identity filenames. 314to identity filenames.
314.Pp 315.Pp
316.It Fl J Xo
317.Sm off
318.Oo Ar jump_user @ Oc
319.Ar jump_host
320.Ns Op : Ns Ar jump_port
321.Sm on
322.Xc
323Connect to the target host by first making a
324.Nm
325connection to
326.Ar jump_host
327and then establishing a TCP forward to the ultimate destination from
328there.
329Multiple jump hops may be specified separated by comma characters.
330This is a shortcut to specify a
331.Cm ProxyJump
332configuration directive.
333.Pp
315.It Fl K 334.It Fl K
316Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI 335Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI
317credentials to the server. 336credentials to the server.
@@ -523,6 +542,7 @@ For full details of the options listed below, and their possible values, see
523.It PreferredAuthentications 542.It PreferredAuthentications
524.It Protocol 543.It Protocol
525.It ProxyCommand 544.It ProxyCommand
545.It ProxyJump
526.It ProxyUseFdpass 546.It ProxyUseFdpass
527.It PubkeyAcceptedKeyTypes 547.It PubkeyAcceptedKeyTypes
528.It PubkeyAuthentication 548.It PubkeyAuthentication
diff --git a/ssh.c b/ssh.c
index e7d4fd915..a9b685349 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.442 2016/06/03 04:09:39 dtucker Exp $ */ 1/* $OpenBSD: ssh.c,v 1.443 2016/07/15 00:24:30 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
@@ -330,7 +330,7 @@ resolve_addr(const char *name, int port, char *caddr, size_t clen)
330 * NB. this function must operate with a options having undefined members. 330 * NB. this function must operate with a options having undefined members.
331 */ 331 */
332static int 332static int
333check_follow_cname(char **namep, const char *cname) 333check_follow_cname(int direct, char **namep, const char *cname)
334{ 334{
335 int i; 335 int i;
336 struct allowed_cname *rule; 336 struct allowed_cname *rule;
@@ -342,9 +342,9 @@ check_follow_cname(char **namep, const char *cname)
342 return 0; 342 return 0;
343 /* 343 /*
344 * Don't attempt to canonicalize names that will be interpreted by 344 * Don't attempt to canonicalize names that will be interpreted by
345 * a proxy unless the user specifically requests so. 345 * a proxy or jump host unless the user specifically requests so.
346 */ 346 */
347 if (!option_clear_or_none(options.proxy_command) && 347 if (!direct &&
348 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) 348 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
349 return 0; 349 return 0;
350 debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); 350 debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname);
@@ -371,7 +371,7 @@ check_follow_cname(char **namep, const char *cname)
371static struct addrinfo * 371static struct addrinfo *
372resolve_canonicalize(char **hostp, int port) 372resolve_canonicalize(char **hostp, int port)
373{ 373{
374 int i, ndots; 374 int i, direct, ndots;
375 char *cp, *fullhost, newname[NI_MAXHOST]; 375 char *cp, *fullhost, newname[NI_MAXHOST];
376 struct addrinfo *addrs; 376 struct addrinfo *addrs;
377 377
@@ -382,7 +382,9 @@ resolve_canonicalize(char **hostp, int port)
382 * Don't attempt to canonicalize names that will be interpreted by 382 * Don't attempt to canonicalize names that will be interpreted by
383 * a proxy unless the user specifically requests so. 383 * a proxy unless the user specifically requests so.
384 */ 384 */
385 if (!option_clear_or_none(options.proxy_command) && 385 direct = option_clear_or_none(options.proxy_command) &&
386 options.jump_host == NULL;
387 if (!direct &&
386 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) 388 options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
387 return NULL; 389 return NULL;
388 390
@@ -437,7 +439,7 @@ resolve_canonicalize(char **hostp, int port)
437 /* Remove trailing '.' */ 439 /* Remove trailing '.' */
438 fullhost[strlen(fullhost) - 1] = '\0'; 440 fullhost[strlen(fullhost) - 1] = '\0';
439 /* Follow CNAME if requested */ 441 /* Follow CNAME if requested */
440 if (!check_follow_cname(&fullhost, newname)) { 442 if (!check_follow_cname(direct, &fullhost, newname)) {
441 debug("Canonicalized hostname \"%s\" => \"%s\"", 443 debug("Canonicalized hostname \"%s\" => \"%s\"",
442 *hostp, fullhost); 444 *hostp, fullhost);
443 } 445 }
@@ -510,7 +512,7 @@ int
510main(int ac, char **av) 512main(int ac, char **av)
511{ 513{
512 struct ssh *ssh = NULL; 514 struct ssh *ssh = NULL;
513 int i, r, opt, exit_status, use_syslog, config_test = 0; 515 int i, r, opt, exit_status, use_syslog, direct, config_test = 0;
514 char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile; 516 char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile;
515 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; 517 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
516 char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex; 518 char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex;
@@ -603,7 +605,7 @@ main(int ac, char **av)
603 605
604 again: 606 again:
605 while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" 607 while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
606 "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { 608 "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
607 switch (opt) { 609 switch (opt) {
608 case '1': 610 case '1':
609 options.protocol = SSH_PROTO_1; 611 options.protocol = SSH_PROTO_1;
@@ -728,6 +730,15 @@ main(int ac, char **av)
728 fprintf(stderr, "no support for PKCS#11.\n"); 730 fprintf(stderr, "no support for PKCS#11.\n");
729#endif 731#endif
730 break; 732 break;
733 case 'J':
734 if (options.jump_host != NULL)
735 fatal("Only a single -J option permitted");
736 if (options.proxy_command != NULL)
737 fatal("Cannot specify -J with ProxyCommand");
738 if (parse_jump(optarg, &options, 1) == -1)
739 fatal("Invalid -J argument");
740 options.proxy_command = xstrdup("none");
741 break;
731 case 't': 742 case 't':
732 if (options.request_tty == REQUEST_TTY_YES) 743 if (options.request_tty == REQUEST_TTY_YES)
733 options.request_tty = REQUEST_TTY_FORCE; 744 options.request_tty = REQUEST_TTY_FORCE;
@@ -739,8 +750,10 @@ main(int ac, char **av)
739 debug_flag = 1; 750 debug_flag = 1;
740 options.log_level = SYSLOG_LEVEL_DEBUG1; 751 options.log_level = SYSLOG_LEVEL_DEBUG1;
741 } else { 752 } else {
742 if (options.log_level < SYSLOG_LEVEL_DEBUG3) 753 if (options.log_level < SYSLOG_LEVEL_DEBUG3) {
754 debug_flag++;
743 options.log_level++; 755 options.log_level++;
756 }
744 } 757 }
745 break; 758 break;
746 case 'V': 759 case 'V':
@@ -1038,9 +1051,10 @@ main(int ac, char **av)
1038 * has specifically requested canonicalisation for this case via 1051 * has specifically requested canonicalisation for this case via
1039 * CanonicalizeHostname=always 1052 * CanonicalizeHostname=always
1040 */ 1053 */
1041 if (addrs == NULL && options.num_permitted_cnames != 0 && 1054 direct = option_clear_or_none(options.proxy_command) &&
1042 (option_clear_or_none(options.proxy_command) || 1055 options.jump_host == NULL;
1043 options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { 1056 if (addrs == NULL && options.num_permitted_cnames != 0 && (direct ||
1057 options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) {
1044 if ((addrs = resolve_host(host, options.port, 1058 if ((addrs = resolve_host(host, options.port,
1045 option_clear_or_none(options.proxy_command), 1059 option_clear_or_none(options.proxy_command),
1046 cname, sizeof(cname))) == NULL) { 1060 cname, sizeof(cname))) == NULL) {
@@ -1048,7 +1062,7 @@ main(int ac, char **av)
1048 if (option_clear_or_none(options.proxy_command)) 1062 if (option_clear_or_none(options.proxy_command))
1049 cleanup_exit(255); /* logged in resolve_host */ 1063 cleanup_exit(255); /* logged in resolve_host */
1050 } else 1064 } else
1051 check_follow_cname(&host, cname); 1065 check_follow_cname(direct, &host, cname);
1052 } 1066 }
1053 1067
1054 /* 1068 /*
@@ -1073,6 +1087,41 @@ main(int ac, char **av)
1073 /* Fill configuration defaults. */ 1087 /* Fill configuration defaults. */
1074 fill_default_options(&options); 1088 fill_default_options(&options);
1075 1089
1090 /*
1091 * If ProxyJump option specified, then construct a ProxyCommand now.
1092 */
1093 if (options.jump_host != NULL) {
1094 char port_s[8];
1095
1096 /* Consistency check */
1097 if (options.proxy_command != NULL)
1098 fatal("inconsistent options: ProxyCommand+ProxyJump");
1099 /* Never use FD passing for ProxyJump */
1100 options.proxy_use_fdpass = 0;
1101 snprintf(port_s, sizeof(port_s), "%d", options.jump_port);
1102 xasprintf(&options.proxy_command,
1103 "ssh%s%s%s%s%s%s%s%s%s%.*s -W %%h:%%p %s",
1104 /* Optional "-l user" argument if jump_user set */
1105 options.jump_user == NULL ? "" : " -l ",
1106 options.jump_user == NULL ? "" : options.jump_user,
1107 /* Optional "-p port" argument if jump_port set */
1108 options.jump_port <= 0 ? "" : " -p ",
1109 options.jump_port <= 0 ? "" : port_s,
1110 /* Optional additional jump hosts ",..." */
1111 options.jump_extra == NULL ? "" : " -J ",
1112 options.jump_extra == NULL ? "" : options.jump_extra,
1113 /* Optional "-F" argumment if -F specified */
1114 config == NULL ? "" : " -F ",
1115 config == NULL ? "" : config,
1116 /* Optional "-v" arguments if -v set */
1117 debug_flag ? " -" : "",
1118 debug_flag, "vvv",
1119 /* Mandatory hostname */
1120 options.jump_host);
1121 debug("Setting implicit ProxyCommand from ProxyJump: %s",
1122 options.proxy_command);
1123 }
1124
1076 if (options.port == 0) 1125 if (options.port == 0)
1077 options.port = default_ssh_port(); 1126 options.port = default_ssh_port();
1078 channel_set_af(options.address_family); 1127 channel_set_af(options.address_family);
diff --git a/ssh_config.5 b/ssh_config.5
index 45fe89202..860577023 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.232 2016/05/04 14:29:58 markus Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.233 2016/07/15 00:24:30 djm Exp $
37.Dd $Mdocdate: May 4 2016 $ 37.Dd $Mdocdate: July 15 2016 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -1358,6 +1358,30 @@ For example, the following directive would connect via an HTTP proxy at
1358.Bd -literal -offset 3n 1358.Bd -literal -offset 3n
1359ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p 1359ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p
1360.Ed 1360.Ed
1361.It Cm ProxyJump
1362Specifies one or more jump proxies as
1363.Xo
1364.Sm off
1365.Oo Ar user @ Oc
1366.Ar host
1367.Ns Op : Ns Ar port
1368.Sm on
1369.Xc .
1370Multiple proxies may be separated by comma characters.
1371Setting this option will cause
1372.Xr ssh 1
1373to connect to the target host by first making a
1374.Xr ssh 1
1375connection to the specified
1376.Cm ProxyJump
1377host and then establishing a
1378a TCP forwarding to the ultimate target from there.
1379.Pp
1380Note that this option will compete with the
1381.Cm ProxyCommand
1382option - whichever is specified first will prevent later instances of the
1383other from taking effect.
1384.Pp
1361.It Cm ProxyUseFdpass 1385.It Cm ProxyUseFdpass
1362Specifies that 1386Specifies that
1363.Cm ProxyCommand 1387.Cm ProxyCommand