summaryrefslogtreecommitdiff
path: root/ssh.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2016-07-15 00:24:30 +0000
committerDamien Miller <djm@mindrot.org>2016-07-15 14:20:10 +1000
commited877ef653847d056bb433975d731b7a1132a979 (patch)
tree855230b944a0fc2eebdaa4c037f911e28ff21e17 /ssh.c
parent5c02dd126206a26785379e80f2d3848e4470b711 (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.c77
1 files changed, 63 insertions, 14 deletions
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);