diff options
-rw-r--r-- | misc.c | 63 | ||||
-rw-r--r-- | misc.h | 3 | ||||
-rw-r--r-- | readconf.c | 95 | ||||
-rw-r--r-- | readconf.h | 8 | ||||
-rw-r--r-- | ssh.1 | 24 | ||||
-rw-r--r-- | ssh.c | 77 | ||||
-rw-r--r-- | ssh_config.5 | 28 |
7 files changed, 271 insertions, 27 deletions
@@ -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 | */ | ||
462 | int | ||
463 | parse_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 */ |
455 | void | 516 | void |
456 | addargs(arglist *args, char *fmt, ...) | 517 | addargs(arglist *args, char *fmt, ...) |
@@ -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); | |||
49 | char *hpdelim(char **); | 49 | char *hpdelim(char **); |
50 | char *cleanhostname(char *); | 50 | char *cleanhostname(char *); |
51 | char *colon(char *); | 51 | char *colon(char *); |
52 | int parse_user_host_port(const char *, char **, char **, int *); | ||
52 | long convtime(const char *); | 53 | long convtime(const char *); |
53 | char *tilde_expand_filename(const char *, uid_t); | 54 | char *tilde_expand_filename(const char *, uid_t); |
54 | char *percent_expand(const char *, ...) __attribute__((__sentinel__)); | 55 | char *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 */ | ||
1124 | parse_command: | 1128 | parse_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; |
1134 | parse_int: | 1150 | parse_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 | ||
2284 | int | ||
2285 | parse_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 */ |
2265 | static const char * | 2323 | static const char * |
2266 | fmt_multistate_int(int val, const struct multistate *m) | 2324 | fmt_multistate_int(int val, const struct multistate *m) |
@@ -2412,7 +2470,7 @@ void | |||
2412 | dump_client_config(Options *o, const char *host) | 2470 | dump_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 *, | |||
198 | int read_config_file(const char *, struct passwd *, const char *, | 203 | int read_config_file(const char *, struct passwd *, const char *, |
199 | const char *, Options *, int); | 204 | const char *, Options *, int); |
200 | int parse_forward(struct Forward *, const char *, int, int); | 205 | int parse_forward(struct Forward *, const char *, int, int); |
206 | int parse_jump(const char *, Options *, int); | ||
201 | int default_ssh_port(void); | 207 | int default_ssh_port(void); |
202 | int option_clear_or_none(const char *); | 208 | int option_clear_or_none(const char *); |
203 | void dump_client_config(Options *o, const char *host); | 209 | void dump_client_config(Options *o, const char *host); |
@@ -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 |
313 | to identity filenames. | 314 | to 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 | ||
323 | Connect to the target host by first making a | ||
324 | .Nm | ||
325 | connection to | ||
326 | .Ar jump_host | ||
327 | and then establishing a TCP forward to the ultimate destination from | ||
328 | there. | ||
329 | Multiple jump hops may be specified separated by comma characters. | ||
330 | This is a shortcut to specify a | ||
331 | .Cm ProxyJump | ||
332 | configuration directive. | ||
333 | .Pp | ||
315 | .It Fl K | 334 | .It Fl K |
316 | Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI | 335 | Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI |
317 | credentials to the server. | 336 | credentials 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 |
@@ -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 | */ |
332 | static int | 332 | static int |
333 | check_follow_cname(char **namep, const char *cname) | 333 | check_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) | |||
371 | static struct addrinfo * | 371 | static struct addrinfo * |
372 | resolve_canonicalize(char **hostp, int port) | 372 | resolve_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 | |||
510 | main(int ac, char **av) | 512 | main(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 |
1359 | ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p | 1359 | ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p |
1360 | .Ed | 1360 | .Ed |
1361 | .It Cm ProxyJump | ||
1362 | Specifies 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 . | ||
1370 | Multiple proxies may be separated by comma characters. | ||
1371 | Setting this option will cause | ||
1372 | .Xr ssh 1 | ||
1373 | to connect to the target host by first making a | ||
1374 | .Xr ssh 1 | ||
1375 | connection to the specified | ||
1376 | .Cm ProxyJump | ||
1377 | host and then establishing a | ||
1378 | a TCP forwarding to the ultimate target from there. | ||
1379 | .Pp | ||
1380 | Note that this option will compete with the | ||
1381 | .Cm ProxyCommand | ||
1382 | option - whichever is specified first will prevent later instances of the | ||
1383 | other from taking effect. | ||
1384 | .Pp | ||
1361 | .It Cm ProxyUseFdpass | 1385 | .It Cm ProxyUseFdpass |
1362 | Specifies that | 1386 | Specifies that |
1363 | .Cm ProxyCommand | 1387 | .Cm ProxyCommand |