diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 248 |
1 files changed, 167 insertions, 81 deletions
diff --git a/sshconnect.c b/sshconnect.c index ad960fdbf..a2fbf9e65 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.238 2013/05/17 00:13:14 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.244 2014/01/09 23:26:48 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 |
@@ -59,6 +59,7 @@ | |||
59 | #include "misc.h" | 59 | #include "misc.h" |
60 | #include "dns.h" | 60 | #include "dns.h" |
61 | #include "roaming.h" | 61 | #include "roaming.h" |
62 | #include "monitor_fdpass.h" | ||
62 | #include "ssh2.h" | 63 | #include "ssh2.h" |
63 | #include "version.h" | 64 | #include "version.h" |
64 | 65 | ||
@@ -78,47 +79,122 @@ extern uid_t original_effective_uid; | |||
78 | static int show_other_keys(struct hostkeys *, Key *); | 79 | static int show_other_keys(struct hostkeys *, Key *); |
79 | static void warn_changed_key(Key *); | 80 | static void warn_changed_key(Key *); |
80 | 81 | ||
82 | /* Expand a proxy command */ | ||
83 | static char * | ||
84 | expand_proxy_command(const char *proxy_command, const char *user, | ||
85 | const char *host, int port) | ||
86 | { | ||
87 | char *tmp, *ret, strport[NI_MAXSERV]; | ||
88 | |||
89 | snprintf(strport, sizeof strport, "%d", port); | ||
90 | xasprintf(&tmp, "exec %s", proxy_command); | ||
91 | ret = percent_expand(tmp, "h", host, "p", strport, | ||
92 | "r", options.user, (char *)NULL); | ||
93 | free(tmp); | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Connect to the given ssh server using a proxy command that passes a | ||
99 | * a connected fd back to us. | ||
100 | */ | ||
101 | static int | ||
102 | ssh_proxy_fdpass_connect(const char *host, u_short port, | ||
103 | const char *proxy_command) | ||
104 | { | ||
105 | char *command_string; | ||
106 | int sp[2], sock; | ||
107 | pid_t pid; | ||
108 | char *shell; | ||
109 | |||
110 | if ((shell = getenv("SHELL")) == NULL) | ||
111 | shell = _PATH_BSHELL; | ||
112 | |||
113 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) | ||
114 | fatal("Could not create socketpair to communicate with " | ||
115 | "proxy dialer: %.100s", strerror(errno)); | ||
116 | |||
117 | command_string = expand_proxy_command(proxy_command, options.user, | ||
118 | host, port); | ||
119 | debug("Executing proxy dialer command: %.500s", command_string); | ||
120 | |||
121 | /* Fork and execute the proxy command. */ | ||
122 | if ((pid = fork()) == 0) { | ||
123 | char *argv[10]; | ||
124 | |||
125 | /* Child. Permanently give up superuser privileges. */ | ||
126 | permanently_drop_suid(original_real_uid); | ||
127 | |||
128 | close(sp[1]); | ||
129 | /* Redirect stdin and stdout. */ | ||
130 | if (sp[0] != 0) { | ||
131 | if (dup2(sp[0], 0) < 0) | ||
132 | perror("dup2 stdin"); | ||
133 | } | ||
134 | if (sp[0] != 1) { | ||
135 | if (dup2(sp[0], 1) < 0) | ||
136 | perror("dup2 stdout"); | ||
137 | } | ||
138 | if (sp[0] >= 2) | ||
139 | close(sp[0]); | ||
140 | |||
141 | /* | ||
142 | * Stderr is left as it is so that error messages get | ||
143 | * printed on the user's terminal. | ||
144 | */ | ||
145 | argv[0] = shell; | ||
146 | argv[1] = "-c"; | ||
147 | argv[2] = command_string; | ||
148 | argv[3] = NULL; | ||
149 | |||
150 | /* | ||
151 | * Execute the proxy command. | ||
152 | * Note that we gave up any extra privileges above. | ||
153 | */ | ||
154 | execv(argv[0], argv); | ||
155 | perror(argv[0]); | ||
156 | exit(1); | ||
157 | } | ||
158 | /* Parent. */ | ||
159 | if (pid < 0) | ||
160 | fatal("fork failed: %.100s", strerror(errno)); | ||
161 | close(sp[0]); | ||
162 | free(command_string); | ||
163 | |||
164 | if ((sock = mm_receive_fd(sp[1])) == -1) | ||
165 | fatal("proxy dialer did not pass back a connection"); | ||
166 | |||
167 | while (waitpid(pid, NULL, 0) == -1) | ||
168 | if (errno != EINTR) | ||
169 | fatal("Couldn't wait for child: %s", strerror(errno)); | ||
170 | |||
171 | /* Set the connection file descriptors. */ | ||
172 | packet_set_connection(sock, sock); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
81 | /* | 177 | /* |
82 | * Connect to the given ssh server using a proxy command. | 178 | * Connect to the given ssh server using a proxy command. |
83 | */ | 179 | */ |
84 | static int | 180 | static int |
85 | ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | 181 | ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) |
86 | { | 182 | { |
87 | char *command_string, *tmp; | 183 | char *command_string; |
88 | int pin[2], pout[2]; | 184 | int pin[2], pout[2]; |
89 | pid_t pid; | 185 | pid_t pid; |
90 | char *shell, strport[NI_MAXSERV]; | 186 | char *shell; |
91 | |||
92 | if (!strcmp(proxy_command, "-")) { | ||
93 | packet_set_connection(STDIN_FILENO, STDOUT_FILENO); | ||
94 | packet_set_timeout(options.server_alive_interval, | ||
95 | options.server_alive_count_max); | ||
96 | return 0; | ||
97 | } | ||
98 | 187 | ||
99 | if ((shell = getenv("SHELL")) == NULL || *shell == '\0') | 188 | if ((shell = getenv("SHELL")) == NULL || *shell == '\0') |
100 | shell = _PATH_BSHELL; | 189 | shell = _PATH_BSHELL; |
101 | 190 | ||
102 | /* Convert the port number into a string. */ | ||
103 | snprintf(strport, sizeof strport, "%hu", port); | ||
104 | |||
105 | /* | ||
106 | * Build the final command string in the buffer by making the | ||
107 | * appropriate substitutions to the given proxy command. | ||
108 | * | ||
109 | * Use "exec" to avoid "sh -c" processes on some platforms | ||
110 | * (e.g. Solaris) | ||
111 | */ | ||
112 | xasprintf(&tmp, "exec %s", proxy_command); | ||
113 | command_string = percent_expand(tmp, "h", host, "p", strport, | ||
114 | "r", options.user, (char *)NULL); | ||
115 | free(tmp); | ||
116 | |||
117 | /* Create pipes for communicating with the proxy. */ | 191 | /* Create pipes for communicating with the proxy. */ |
118 | if (pipe(pin) < 0 || pipe(pout) < 0) | 192 | if (pipe(pin) < 0 || pipe(pout) < 0) |
119 | fatal("Could not create pipes to communicate with the proxy: %.100s", | 193 | fatal("Could not create pipes to communicate with the proxy: %.100s", |
120 | strerror(errno)); | 194 | strerror(errno)); |
121 | 195 | ||
196 | command_string = expand_proxy_command(proxy_command, options.user, | ||
197 | host, port); | ||
122 | debug("Executing proxy command: %.500s", command_string); | 198 | debug("Executing proxy command: %.500s", command_string); |
123 | 199 | ||
124 | /* Fork and execute the proxy command. */ | 200 | /* Fork and execute the proxy command. */ |
@@ -170,8 +246,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
170 | 246 | ||
171 | /* Set the connection file descriptors. */ | 247 | /* Set the connection file descriptors. */ |
172 | packet_set_connection(pout[0], pin[1]); | 248 | packet_set_connection(pout[0], pin[1]); |
173 | packet_set_timeout(options.server_alive_interval, | ||
174 | options.server_alive_count_max); | ||
175 | 249 | ||
176 | /* Indicate OK return */ | 250 | /* Indicate OK return */ |
177 | return 0; | 251 | return 0; |
@@ -194,34 +268,18 @@ ssh_kill_proxy_command(void) | |||
194 | static int | 268 | static int |
195 | ssh_create_socket(int privileged, struct addrinfo *ai) | 269 | ssh_create_socket(int privileged, struct addrinfo *ai) |
196 | { | 270 | { |
197 | int sock, gaierr; | 271 | int sock, r, gaierr; |
198 | struct addrinfo hints, *res; | 272 | struct addrinfo hints, *res; |
199 | 273 | ||
200 | /* | ||
201 | * If we are running as root and want to connect to a privileged | ||
202 | * port, bind our own socket to a privileged port. | ||
203 | */ | ||
204 | if (privileged) { | ||
205 | int p = IPPORT_RESERVED - 1; | ||
206 | PRIV_START; | ||
207 | sock = rresvport_af(&p, ai->ai_family); | ||
208 | PRIV_END; | ||
209 | if (sock < 0) | ||
210 | error("rresvport: af=%d %.100s", ai->ai_family, | ||
211 | strerror(errno)); | ||
212 | else | ||
213 | debug("Allocated local port %d.", p); | ||
214 | return sock; | ||
215 | } | ||
216 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 274 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
217 | if (sock < 0) { | 275 | if (sock < 0) { |
218 | error("socket: %.100s", strerror(errno)); | 276 | error("socket: %s", strerror(errno)); |
219 | return -1; | 277 | return -1; |
220 | } | 278 | } |
221 | fcntl(sock, F_SETFD, FD_CLOEXEC); | 279 | fcntl(sock, F_SETFD, FD_CLOEXEC); |
222 | 280 | ||
223 | /* Bind the socket to an alternative local IP address */ | 281 | /* Bind the socket to an alternative local IP address */ |
224 | if (options.bind_address == NULL) | 282 | if (options.bind_address == NULL && !privileged) |
225 | return sock; | 283 | return sock; |
226 | 284 | ||
227 | memset(&hints, 0, sizeof(hints)); | 285 | memset(&hints, 0, sizeof(hints)); |
@@ -236,11 +294,28 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
236 | close(sock); | 294 | close(sock); |
237 | return -1; | 295 | return -1; |
238 | } | 296 | } |
239 | if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | 297 | /* |
240 | error("bind: %s: %s", options.bind_address, strerror(errno)); | 298 | * If we are running as root and want to connect to a privileged |
241 | close(sock); | 299 | * port, bind our own socket to a privileged port. |
242 | freeaddrinfo(res); | 300 | */ |
243 | return -1; | 301 | if (privileged) { |
302 | PRIV_START; | ||
303 | r = bindresvport_sa(sock, res->ai_addr); | ||
304 | PRIV_END; | ||
305 | if (r < 0) { | ||
306 | error("bindresvport_sa: af=%d %s", ai->ai_family, | ||
307 | strerror(errno)); | ||
308 | goto fail; | ||
309 | } | ||
310 | } else { | ||
311 | if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | ||
312 | error("bind: %s: %s", options.bind_address, | ||
313 | strerror(errno)); | ||
314 | fail: | ||
315 | close(sock); | ||
316 | freeaddrinfo(res); | ||
317 | return -1; | ||
318 | } | ||
244 | } | 319 | } |
245 | freeaddrinfo(res); | 320 | freeaddrinfo(res); |
246 | return sock; | 321 | return sock; |
@@ -340,33 +415,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
340 | * and %p substituted for host and port, respectively) to use to contact | 415 | * and %p substituted for host and port, respectively) to use to contact |
341 | * the daemon. | 416 | * the daemon. |
342 | */ | 417 | */ |
343 | int | 418 | static int |
344 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | 419 | ssh_connect_direct(const char *host, struct addrinfo *aitop, |
345 | u_short port, int family, int connection_attempts, int *timeout_ms, | 420 | struct sockaddr_storage *hostaddr, u_short port, int family, |
346 | int want_keepalive, int needpriv, const char *proxy_command) | 421 | int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) |
347 | { | 422 | { |
348 | int gaierr; | ||
349 | int on = 1; | 423 | int on = 1; |
350 | int sock = -1, attempt; | 424 | int sock = -1, attempt; |
351 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | 425 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
352 | struct addrinfo hints, *ai, *aitop; | 426 | struct addrinfo *ai; |
353 | 427 | ||
354 | debug2("ssh_connect: needpriv %d", needpriv); | 428 | debug2("ssh_connect: needpriv %d", needpriv); |
355 | 429 | ||
356 | /* If a proxy command is given, connect using it. */ | ||
357 | if (proxy_command != NULL) | ||
358 | return ssh_proxy_connect(host, port, proxy_command); | ||
359 | |||
360 | /* No proxy command. */ | ||
361 | |||
362 | memset(&hints, 0, sizeof(hints)); | ||
363 | hints.ai_family = family; | ||
364 | hints.ai_socktype = SOCK_STREAM; | ||
365 | snprintf(strport, sizeof strport, "%u", port); | ||
366 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) | ||
367 | fatal("%s: Could not resolve hostname %.100s: %s", __progname, | ||
368 | host, ssh_gai_strerror(gaierr)); | ||
369 | |||
370 | for (attempt = 0; attempt < connection_attempts; attempt++) { | 430 | for (attempt = 0; attempt < connection_attempts; attempt++) { |
371 | if (attempt > 0) { | 431 | if (attempt > 0) { |
372 | /* Sleep a moment before retrying. */ | 432 | /* Sleep a moment before retrying. */ |
@@ -378,7 +438,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
378 | * sequence until the connection succeeds. | 438 | * sequence until the connection succeeds. |
379 | */ | 439 | */ |
380 | for (ai = aitop; ai; ai = ai->ai_next) { | 440 | for (ai = aitop; ai; ai = ai->ai_next) { |
381 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | 441 | if (ai->ai_family != AF_INET && |
442 | ai->ai_family != AF_INET6) | ||
382 | continue; | 443 | continue; |
383 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, | 444 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, |
384 | ntop, sizeof(ntop), strport, sizeof(strport), | 445 | ntop, sizeof(ntop), strport, sizeof(strport), |
@@ -411,8 +472,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
411 | break; /* Successful connection. */ | 472 | break; /* Successful connection. */ |
412 | } | 473 | } |
413 | 474 | ||
414 | freeaddrinfo(aitop); | ||
415 | |||
416 | /* Return failure if we didn't get a successful connection. */ | 475 | /* Return failure if we didn't get a successful connection. */ |
417 | if (sock == -1) { | 476 | if (sock == -1) { |
418 | error("ssh: connect to host %s port %s: %s", | 477 | error("ssh: connect to host %s port %s: %s", |
@@ -430,12 +489,28 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
430 | 489 | ||
431 | /* Set the connection. */ | 490 | /* Set the connection. */ |
432 | packet_set_connection(sock, sock); | 491 | packet_set_connection(sock, sock); |
433 | packet_set_timeout(options.server_alive_interval, | ||
434 | options.server_alive_count_max); | ||
435 | 492 | ||
436 | return 0; | 493 | return 0; |
437 | } | 494 | } |
438 | 495 | ||
496 | int | ||
497 | ssh_connect(const char *host, struct addrinfo *addrs, | ||
498 | struct sockaddr_storage *hostaddr, u_short port, int family, | ||
499 | int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) | ||
500 | { | ||
501 | if (options.proxy_command == NULL) { | ||
502 | return ssh_connect_direct(host, addrs, hostaddr, port, family, | ||
503 | connection_attempts, timeout_ms, want_keepalive, needpriv); | ||
504 | } else if (strcmp(options.proxy_command, "-") == 0) { | ||
505 | packet_set_connection(STDIN_FILENO, STDOUT_FILENO); | ||
506 | return 0; /* Always succeeds */ | ||
507 | } else if (options.proxy_use_fdpass) { | ||
508 | return ssh_proxy_fdpass_connect(host, port, | ||
509 | options.proxy_command); | ||
510 | } | ||
511 | return ssh_proxy_connect(host, port, options.proxy_command); | ||
512 | } | ||
513 | |||
439 | static void | 514 | static void |
440 | send_client_banner(int connection_out, int minor1) | 515 | send_client_banner(int connection_out, int minor1) |
441 | { | 516 | { |
@@ -587,6 +662,12 @@ ssh_exchange_identification(int timeout_ms) | |||
587 | fatal("Protocol major versions differ: %d vs. %d", | 662 | fatal("Protocol major versions differ: %d vs. %d", |
588 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 663 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
589 | remote_major); | 664 | remote_major); |
665 | if ((datafellows & SSH_BUG_DERIVEKEY) != 0) | ||
666 | fatal("Server version \"%.100s\" uses unsafe key agreement; " | ||
667 | "refusing connection", remote_version); | ||
668 | if ((datafellows & SSH_BUG_RSASIGMD5) != 0) | ||
669 | logit("Server version \"%.100s\" uses unsafe RSA signature " | ||
670 | "scheme; disabling use of RSA keys", remote_version); | ||
590 | if (!client_banner_sent) | 671 | if (!client_banner_sent) |
591 | send_client_banner(connection_out, minor1); | 672 | send_client_banner(connection_out, minor1); |
592 | chop(server_version_string); | 673 | chop(server_version_string); |
@@ -1181,7 +1262,7 @@ void | |||
1181 | ssh_login(Sensitive *sensitive, const char *orighost, | 1262 | ssh_login(Sensitive *sensitive, const char *orighost, |
1182 | struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) | 1263 | struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) |
1183 | { | 1264 | { |
1184 | char *host, *cp; | 1265 | char *host; |
1185 | char *server_user, *local_user; | 1266 | char *server_user, *local_user; |
1186 | 1267 | ||
1187 | local_user = xstrdup(pw->pw_name); | 1268 | local_user = xstrdup(pw->pw_name); |
@@ -1189,9 +1270,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
1189 | 1270 | ||
1190 | /* Convert the user-supplied hostname into all lowercase. */ | 1271 | /* Convert the user-supplied hostname into all lowercase. */ |
1191 | host = xstrdup(orighost); | 1272 | host = xstrdup(orighost); |
1192 | for (cp = host; *cp; cp++) | 1273 | lowercase(host); |
1193 | if (isupper(*cp)) | ||
1194 | *cp = (char)tolower(*cp); | ||
1195 | 1274 | ||
1196 | /* Exchange protocol version identification strings with the server. */ | 1275 | /* Exchange protocol version identification strings with the server. */ |
1197 | ssh_exchange_identification(timeout_ms); | 1276 | ssh_exchange_identification(timeout_ms); |
@@ -1233,7 +1312,14 @@ ssh_put_password(char *password) | |||
1233 | static int | 1312 | static int |
1234 | show_other_keys(struct hostkeys *hostkeys, Key *key) | 1313 | show_other_keys(struct hostkeys *hostkeys, Key *key) |
1235 | { | 1314 | { |
1236 | int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; | 1315 | int type[] = { |
1316 | KEY_RSA1, | ||
1317 | KEY_RSA, | ||
1318 | KEY_DSA, | ||
1319 | KEY_ECDSA, | ||
1320 | KEY_ED25519, | ||
1321 | -1 | ||
1322 | }; | ||
1237 | int i, ret = 0; | 1323 | int i, ret = 0; |
1238 | char *fp, *ra; | 1324 | char *fp, *ra; |
1239 | const struct hostkey_entry *found; | 1325 | const struct hostkey_entry *found; |