diff options
author | djm@openbsd.org <djm@openbsd.org> | 2016-07-15 00:24:30 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2016-07-15 14:20:10 +1000 |
commit | ed877ef653847d056bb433975d731b7a1132a979 (patch) | |
tree | 855230b944a0fc2eebdaa4c037f911e28ff21e17 /ssh.c | |
parent | 5c02dd126206a26785379e80f2d3848e4470b711 (diff) |
upstream commit
Add a ProxyJump ssh_config(5) option and corresponding -J
ssh(1) command-line flag to allow simplified indirection through a SSH
bastion or "jump host".
These options construct a proxy command that connects to the
specified jump host(s) (more than one may be specified) and uses
port-forwarding to establish a connection to the next destination.
This codifies the safest way of indirecting connections through SSH
servers and makes it easy to use.
ok markus@
Upstream-ID: fa899cb8b26d889da8f142eb9774c1ea36b04397
Diffstat (limited to 'ssh.c')
-rw-r--r-- | ssh.c | 77 |
1 files changed, 63 insertions, 14 deletions
@@ -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); |