diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 197 |
1 files changed, 158 insertions, 39 deletions
diff --git a/sshconnect.c b/sshconnect.c index 3280b310d..15d8b807e 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.297 2018/02/23 15:58:38 markus 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 |
@@ -23,6 +23,7 @@ | |||
23 | # include <sys/time.h> | 23 | # include <sys/time.h> |
24 | #endif | 24 | #endif |
25 | 25 | ||
26 | #include <net/if.h> | ||
26 | #include <netinet/in.h> | 27 | #include <netinet/in.h> |
27 | #include <arpa/inet.h> | 28 | #include <arpa/inet.h> |
28 | 29 | ||
@@ -43,6 +44,9 @@ | |||
43 | #include <stdlib.h> | 44 | #include <stdlib.h> |
44 | #include <string.h> | 45 | #include <string.h> |
45 | #include <unistd.h> | 46 | #include <unistd.h> |
47 | #ifdef HAVE_IFADDRS_H | ||
48 | # include <ifaddrs.h> | ||
49 | #endif | ||
46 | 50 | ||
47 | #include "xmalloc.h" | 51 | #include "xmalloc.h" |
48 | #include "key.h" | 52 | #include "key.h" |
@@ -270,14 +274,83 @@ ssh_kill_proxy_command(void) | |||
270 | kill(proxy_command_pid, SIGHUP); | 274 | kill(proxy_command_pid, SIGHUP); |
271 | } | 275 | } |
272 | 276 | ||
277 | #ifdef HAVE_IFADDRS_H | ||
278 | /* | ||
279 | * Search a interface address list (returned from getifaddrs(3)) for an | ||
280 | * address that matches the desired address family on the specifed interface. | ||
281 | * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. | ||
282 | */ | ||
283 | static int | ||
284 | check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, | ||
285 | struct sockaddr_storage *resultp, socklen_t *rlenp) | ||
286 | { | ||
287 | struct sockaddr_in6 *sa6; | ||
288 | struct sockaddr_in *sa; | ||
289 | struct in6_addr *v6addr; | ||
290 | const struct ifaddrs *ifa; | ||
291 | int allow_local; | ||
292 | |||
293 | /* | ||
294 | * Prefer addresses that are not loopback or linklocal, but use them | ||
295 | * if nothing else matches. | ||
296 | */ | ||
297 | for (allow_local = 0; allow_local < 2; allow_local++) { | ||
298 | for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { | ||
299 | if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || | ||
300 | (ifa->ifa_flags & IFF_UP) == 0 || | ||
301 | ifa->ifa_addr->sa_family != af || | ||
302 | strcmp(ifa->ifa_name, options.bind_interface) != 0) | ||
303 | continue; | ||
304 | switch (ifa->ifa_addr->sa_family) { | ||
305 | case AF_INET: | ||
306 | sa = (struct sockaddr_in *)ifa->ifa_addr; | ||
307 | if (!allow_local && sa->sin_addr.s_addr == | ||
308 | htonl(INADDR_LOOPBACK)) | ||
309 | continue; | ||
310 | if (*rlenp < sizeof(struct sockaddr_in)) { | ||
311 | error("%s: v4 addr doesn't fit", | ||
312 | __func__); | ||
313 | return -1; | ||
314 | } | ||
315 | *rlenp = sizeof(struct sockaddr_in); | ||
316 | memcpy(resultp, sa, *rlenp); | ||
317 | return 0; | ||
318 | case AF_INET6: | ||
319 | sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; | ||
320 | v6addr = &sa6->sin6_addr; | ||
321 | if (!allow_local && | ||
322 | (IN6_IS_ADDR_LINKLOCAL(v6addr) || | ||
323 | IN6_IS_ADDR_LOOPBACK(v6addr))) | ||
324 | continue; | ||
325 | if (*rlenp < sizeof(struct sockaddr_in6)) { | ||
326 | error("%s: v6 addr doesn't fit", | ||
327 | __func__); | ||
328 | return -1; | ||
329 | } | ||
330 | *rlenp = sizeof(struct sockaddr_in6); | ||
331 | memcpy(resultp, sa6, *rlenp); | ||
332 | return 0; | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | return -1; | ||
337 | } | ||
338 | #endif | ||
339 | |||
273 | /* | 340 | /* |
274 | * Creates a (possibly privileged) socket for use as the ssh connection. | 341 | * Creates a (possibly privileged) socket for use as the ssh connection. |
275 | */ | 342 | */ |
276 | static int | 343 | static int |
277 | ssh_create_socket(int privileged, struct addrinfo *ai) | 344 | ssh_create_socket(int privileged, struct addrinfo *ai) |
278 | { | 345 | { |
279 | int sock, r, gaierr; | 346 | int sock, r, oerrno; |
347 | struct sockaddr_storage bindaddr; | ||
348 | socklen_t bindaddrlen = 0; | ||
280 | struct addrinfo hints, *res = NULL; | 349 | struct addrinfo hints, *res = NULL; |
350 | #ifdef HAVE_IFADDRS_H | ||
351 | struct ifaddrs *ifaddrs = NULL; | ||
352 | #endif | ||
353 | char ntop[NI_MAXHOST]; | ||
281 | 354 | ||
282 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 355 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
283 | if (sock < 0) { | 356 | if (sock < 0) { |
@@ -287,22 +360,55 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
287 | fcntl(sock, F_SETFD, FD_CLOEXEC); | 360 | fcntl(sock, F_SETFD, FD_CLOEXEC); |
288 | 361 | ||
289 | /* Bind the socket to an alternative local IP address */ | 362 | /* Bind the socket to an alternative local IP address */ |
290 | if (options.bind_address == NULL && !privileged) | 363 | if (options.bind_address == NULL && options.bind_interface == NULL && |
364 | !privileged) | ||
291 | return sock; | 365 | return sock; |
292 | 366 | ||
293 | if (options.bind_address) { | 367 | if (options.bind_address != NULL) { |
294 | memset(&hints, 0, sizeof(hints)); | 368 | memset(&hints, 0, sizeof(hints)); |
295 | hints.ai_family = ai->ai_family; | 369 | hints.ai_family = ai->ai_family; |
296 | hints.ai_socktype = ai->ai_socktype; | 370 | hints.ai_socktype = ai->ai_socktype; |
297 | hints.ai_protocol = ai->ai_protocol; | 371 | hints.ai_protocol = ai->ai_protocol; |
298 | hints.ai_flags = AI_PASSIVE; | 372 | hints.ai_flags = AI_PASSIVE; |
299 | gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); | 373 | if ((r = getaddrinfo(options.bind_address, NULL, |
300 | if (gaierr) { | 374 | &hints, &res)) != 0) { |
301 | error("getaddrinfo: %s: %s", options.bind_address, | 375 | error("getaddrinfo: %s: %s", options.bind_address, |
302 | ssh_gai_strerror(gaierr)); | 376 | ssh_gai_strerror(r)); |
303 | close(sock); | 377 | goto fail; |
304 | return -1; | 378 | } |
379 | if (res == NULL) { | ||
380 | error("getaddrinfo: no addrs"); | ||
381 | goto fail; | ||
305 | } | 382 | } |
383 | if (res->ai_addrlen > sizeof(bindaddr)) { | ||
384 | error("%s: addr doesn't fit", __func__); | ||
385 | goto fail; | ||
386 | } | ||
387 | memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); | ||
388 | bindaddrlen = res->ai_addrlen; | ||
389 | } else if (options.bind_interface != NULL) { | ||
390 | #ifdef HAVE_IFADDRS_H | ||
391 | if ((r = getifaddrs(&ifaddrs)) != 0) { | ||
392 | error("getifaddrs: %s: %s", options.bind_interface, | ||
393 | strerror(errno)); | ||
394 | goto fail; | ||
395 | } | ||
396 | bindaddrlen = sizeof(bindaddr); | ||
397 | if (check_ifaddrs(options.bind_interface, ai->ai_family, | ||
398 | ifaddrs, &bindaddr, &bindaddrlen) != 0) { | ||
399 | logit("getifaddrs: %s: no suitable addresses", | ||
400 | options.bind_interface); | ||
401 | goto fail; | ||
402 | } | ||
403 | #else | ||
404 | error("BindInterface not supported on this platform."); | ||
405 | #endif | ||
406 | } | ||
407 | if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, | ||
408 | ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { | ||
409 | error("%s: getnameinfo failed: %s", __func__, | ||
410 | ssh_gai_strerror(r)); | ||
411 | goto fail; | ||
306 | } | 412 | } |
307 | /* | 413 | /* |
308 | * If we are running as root and want to connect to a privileged | 414 | * If we are running as root and want to connect to a privileged |
@@ -310,25 +416,32 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
310 | */ | 416 | */ |
311 | if (privileged) { | 417 | if (privileged) { |
312 | PRIV_START; | 418 | PRIV_START; |
313 | r = bindresvport_sa(sock, res ? res->ai_addr : NULL); | 419 | r = bindresvport_sa(sock, |
420 | bindaddrlen == 0 ? NULL : (struct sockaddr *)&bindaddr); | ||
421 | oerrno = errno; | ||
314 | PRIV_END; | 422 | PRIV_END; |
315 | if (r < 0) { | 423 | if (r < 0) { |
316 | error("bindresvport_sa: af=%d %s", ai->ai_family, | 424 | error("bindresvport_sa %s: %s", ntop, |
317 | strerror(errno)); | 425 | strerror(oerrno)); |
318 | goto fail; | 426 | goto fail; |
319 | } | 427 | } |
320 | } else { | 428 | } else if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { |
321 | if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | 429 | error("bind %s: %s", ntop, strerror(errno)); |
322 | error("bind: %s: %s", options.bind_address, | 430 | goto fail; |
323 | strerror(errno)); | ||
324 | fail: | ||
325 | close(sock); | ||
326 | freeaddrinfo(res); | ||
327 | return -1; | ||
328 | } | ||
329 | } | 431 | } |
432 | debug("%s: bound to %s", __func__, ntop); | ||
433 | /* success */ | ||
434 | goto out; | ||
435 | fail: | ||
436 | close(sock); | ||
437 | sock = -1; | ||
438 | out: | ||
330 | if (res != NULL) | 439 | if (res != NULL) |
331 | freeaddrinfo(res); | 440 | freeaddrinfo(res); |
441 | #ifdef HAVE_IFADDRS_H | ||
442 | if (ifaddrs != NULL) | ||
443 | freeifaddrs(ifaddrs); | ||
444 | #endif | ||
332 | return sock; | 445 | return sock; |
333 | } | 446 | } |
334 | 447 | ||
@@ -344,7 +457,7 @@ waitrfd(int fd, int *timeoutp) | |||
344 | struct timeval t_start; | 457 | struct timeval t_start; |
345 | int oerrno, r; | 458 | int oerrno, r; |
346 | 459 | ||
347 | gettimeofday(&t_start, NULL); | 460 | monotime_tv(&t_start); |
348 | pfd.fd = fd; | 461 | pfd.fd = fd; |
349 | pfd.events = POLLIN; | 462 | pfd.events = POLLIN; |
350 | for (; *timeoutp >= 0;) { | 463 | for (; *timeoutp >= 0;) { |
@@ -416,7 +529,7 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, | |||
416 | int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) | 529 | int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) |
417 | { | 530 | { |
418 | int on = 1; | 531 | int on = 1; |
419 | int sock = -1, attempt; | 532 | int oerrno, sock = -1, attempt; |
420 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | 533 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
421 | struct addrinfo *ai; | 534 | struct addrinfo *ai; |
422 | 535 | ||
@@ -436,12 +549,16 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, | |||
436 | */ | 549 | */ |
437 | for (ai = aitop; ai; ai = ai->ai_next) { | 550 | for (ai = aitop; ai; ai = ai->ai_next) { |
438 | if (ai->ai_family != AF_INET && | 551 | if (ai->ai_family != AF_INET && |
439 | ai->ai_family != AF_INET6) | 552 | ai->ai_family != AF_INET6) { |
553 | errno = EAFNOSUPPORT; | ||
440 | continue; | 554 | continue; |
555 | } | ||
441 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, | 556 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, |
442 | ntop, sizeof(ntop), strport, sizeof(strport), | 557 | ntop, sizeof(ntop), strport, sizeof(strport), |
443 | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { | 558 | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { |
559 | oerrno = errno; | ||
444 | error("%s: getnameinfo failed", __func__); | 560 | error("%s: getnameinfo failed", __func__); |
561 | errno = oerrno; | ||
445 | continue; | 562 | continue; |
446 | } | 563 | } |
447 | debug("Connecting to %.200s [%.100s] port %s.", | 564 | debug("Connecting to %.200s [%.100s] port %s.", |
@@ -449,9 +566,11 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, | |||
449 | 566 | ||
450 | /* Create a socket for connecting. */ | 567 | /* Create a socket for connecting. */ |
451 | sock = ssh_create_socket(needpriv, ai); | 568 | sock = ssh_create_socket(needpriv, ai); |
452 | if (sock < 0) | 569 | if (sock < 0) { |
453 | /* Any error is already output */ | 570 | /* Any error is already output */ |
571 | errno = 0; | ||
454 | continue; | 572 | continue; |
573 | } | ||
455 | 574 | ||
456 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, | 575 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, |
457 | timeout_ms) >= 0) { | 576 | timeout_ms) >= 0) { |
@@ -459,10 +578,12 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, | |||
459 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | 578 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); |
460 | break; | 579 | break; |
461 | } else { | 580 | } else { |
581 | oerrno = errno; | ||
462 | debug("connect to address %s port %s: %s", | 582 | debug("connect to address %s port %s: %s", |
463 | ntop, strport, strerror(errno)); | 583 | ntop, strport, strerror(errno)); |
464 | close(sock); | 584 | close(sock); |
465 | sock = -1; | 585 | sock = -1; |
586 | errno = oerrno; | ||
466 | } | 587 | } |
467 | } | 588 | } |
468 | if (sock != -1) | 589 | if (sock != -1) |
@@ -472,8 +593,8 @@ ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, | |||
472 | /* Return failure if we didn't get a successful connection. */ | 593 | /* Return failure if we didn't get a successful connection. */ |
473 | if (sock == -1) { | 594 | if (sock == -1) { |
474 | error("ssh: connect to host %s port %s: %s", | 595 | error("ssh: connect to host %s port %s: %s", |
475 | host, strport, strerror(errno)); | 596 | host, strport, errno == 0 ? "failure" : strerror(errno)); |
476 | return (-1); | 597 | return -1; |
477 | } | 598 | } |
478 | 599 | ||
479 | debug("Connection established."); | 600 | debug("Connection established."); |
@@ -610,9 +731,6 @@ ssh_exchange_identification(int timeout_ms) | |||
610 | if (mismatch) | 731 | if (mismatch) |
611 | fatal("Protocol major versions differ: %d vs. %d", | 732 | fatal("Protocol major versions differ: %d vs. %d", |
612 | PROTOCOL_MAJOR_2, remote_major); | 733 | PROTOCOL_MAJOR_2, remote_major); |
613 | if ((datafellows & SSH_BUG_DERIVEKEY) != 0) | ||
614 | fatal("Server version \"%.100s\" uses unsafe key agreement; " | ||
615 | "refusing connection", remote_version); | ||
616 | if ((datafellows & SSH_BUG_RSASIGMD5) != 0) | 734 | if ((datafellows & SSH_BUG_RSASIGMD5) != 0) |
617 | logit("Server version \"%.100s\" uses unsafe RSA signature " | 735 | logit("Server version \"%.100s\" uses unsafe RSA signature " |
618 | "scheme; disabling use of RSA keys", remote_version); | 736 | "scheme; disabling use of RSA keys", remote_version); |
@@ -631,11 +749,12 @@ confirm(const char *prompt) | |||
631 | return 0; | 749 | return 0; |
632 | for (msg = prompt;;msg = again) { | 750 | for (msg = prompt;;msg = again) { |
633 | p = read_passphrase(msg, RP_ECHO); | 751 | p = read_passphrase(msg, RP_ECHO); |
634 | if (p == NULL || | 752 | if (p == NULL) |
635 | (p[0] == '\0') || (p[0] == '\n') || | 753 | return 0; |
636 | strncasecmp(p, "no", 2) == 0) | 754 | p[strcspn(p, "\n")] = '\0'; |
755 | if (p[0] == '\0' || strcasecmp(p, "no") == 0) | ||
637 | ret = 0; | 756 | ret = 0; |
638 | if (p && strncasecmp(p, "yes", 3) == 0) | 757 | else if (strcasecmp(p, "yes") == 0) |
639 | ret = 1; | 758 | ret = 1; |
640 | free(p); | 759 | free(p); |
641 | if (ret != -1) | 760 | if (ret != -1) |
@@ -1178,8 +1297,7 @@ fail: | |||
1178 | host_key = raw_key; | 1297 | host_key = raw_key; |
1179 | goto retry; | 1298 | goto retry; |
1180 | } | 1299 | } |
1181 | if (raw_key != NULL) | 1300 | sshkey_free(raw_key); |
1182 | sshkey_free(raw_key); | ||
1183 | free(ip); | 1301 | free(ip); |
1184 | free(host); | 1302 | free(host); |
1185 | if (host_hostkeys != NULL) | 1303 | if (host_hostkeys != NULL) |
@@ -1364,6 +1482,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) | |||
1364 | KEY_DSA, | 1482 | KEY_DSA, |
1365 | KEY_ECDSA, | 1483 | KEY_ECDSA, |
1366 | KEY_ED25519, | 1484 | KEY_ED25519, |
1485 | KEY_XMSS, | ||
1367 | -1 | 1486 | -1 |
1368 | }; | 1487 | }; |
1369 | int i, ret = 0; | 1488 | int i, ret = 0; |
@@ -1460,8 +1579,8 @@ ssh_local_cmd(const char *args) | |||
1460 | } | 1579 | } |
1461 | 1580 | ||
1462 | void | 1581 | void |
1463 | maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, | 1582 | maybe_add_key_to_agent(char *authfile, const struct sshkey *private, |
1464 | char *passphrase) | 1583 | char *comment, char *passphrase) |
1465 | { | 1584 | { |
1466 | int auth_sock = -1, r; | 1585 | int auth_sock = -1, r; |
1467 | 1586 | ||
@@ -1481,7 +1600,7 @@ maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, | |||
1481 | } | 1600 | } |
1482 | 1601 | ||
1483 | if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, | 1602 | if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, |
1484 | (options.add_keys_to_agent == 3))) == 0) | 1603 | (options.add_keys_to_agent == 3), 0)) == 0) |
1485 | debug("identity added to agent: %s", authfile); | 1604 | debug("identity added to agent: %s", authfile); |
1486 | else | 1605 | else |
1487 | debug("could not add identity to agent: %s (%d)", authfile, r); | 1606 | debug("could not add identity to agent: %s (%d)", authfile, r); |