diff options
author | djm@openbsd.org <djm@openbsd.org> | 2018-12-27 03:25:24 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2018-12-27 14:38:22 +1100 |
commit | 0a843d9a0e805f14653a555f5c7a8ba99d62c12d (patch) | |
tree | 481f36e9fd1918be5449e369a97c086a1a8d2432 /sshconnect.c | |
parent | 434b587afe41c19391821e7392005068fda76248 (diff) |
upstream: move client/server SSH-* banners to buffers under
ssh->kex and factor out the banner exchange. This eliminates some common code
from the client and server.
Also be more strict about handling \r characters - these should only
be accepted immediately before \n (pointed out by Jann Horn).
Inspired by a patch from Markus Schmidt.
(lots of) feedback and ok markus@
OpenBSD-Commit-ID: 1cc7885487a6754f63641d7d3279b0941890275b
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 187 |
1 files changed, 8 insertions, 179 deletions
diff --git a/sshconnect.c b/sshconnect.c index 4862da5ed..884e33628 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.308 2018/11/18 22:43:29 dtucker Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.309 2018/12/27 03:25:25 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 |
@@ -68,9 +68,8 @@ | |||
68 | #include "authfile.h" | 68 | #include "authfile.h" |
69 | #include "ssherr.h" | 69 | #include "ssherr.h" |
70 | #include "authfd.h" | 70 | #include "authfd.h" |
71 | #include "kex.h" | ||
71 | 72 | ||
72 | char *client_version_string = NULL; | ||
73 | char *server_version_string = NULL; | ||
74 | struct sshkey *previous_host_key = NULL; | 73 | struct sshkey *previous_host_key = NULL; |
75 | 74 | ||
76 | static int matching_host_key_dns = 0; | 75 | static int matching_host_key_dns = 0; |
@@ -445,73 +444,6 @@ fail: | |||
445 | } | 444 | } |
446 | 445 | ||
447 | /* | 446 | /* |
448 | * Wait up to *timeoutp milliseconds for fd to be readable. Updates | ||
449 | * *timeoutp with time remaining. | ||
450 | * Returns 0 if fd ready or -1 on timeout or error (see errno). | ||
451 | */ | ||
452 | static int | ||
453 | waitrfd(int fd, int *timeoutp) | ||
454 | { | ||
455 | struct pollfd pfd; | ||
456 | struct timeval t_start; | ||
457 | int oerrno, r; | ||
458 | |||
459 | monotime_tv(&t_start); | ||
460 | pfd.fd = fd; | ||
461 | pfd.events = POLLIN; | ||
462 | for (; *timeoutp >= 0;) { | ||
463 | r = poll(&pfd, 1, *timeoutp); | ||
464 | oerrno = errno; | ||
465 | ms_subtract_diff(&t_start, timeoutp); | ||
466 | errno = oerrno; | ||
467 | if (r > 0) | ||
468 | return 0; | ||
469 | else if (r == -1 && errno != EAGAIN) | ||
470 | return -1; | ||
471 | else if (r == 0) | ||
472 | break; | ||
473 | } | ||
474 | /* timeout */ | ||
475 | errno = ETIMEDOUT; | ||
476 | return -1; | ||
477 | } | ||
478 | |||
479 | static int | ||
480 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | ||
481 | socklen_t addrlen, int *timeoutp) | ||
482 | { | ||
483 | int optval = 0; | ||
484 | socklen_t optlen = sizeof(optval); | ||
485 | |||
486 | /* No timeout: just do a blocking connect() */ | ||
487 | if (*timeoutp <= 0) | ||
488 | return connect(sockfd, serv_addr, addrlen); | ||
489 | |||
490 | set_nonblock(sockfd); | ||
491 | if (connect(sockfd, serv_addr, addrlen) == 0) { | ||
492 | /* Succeeded already? */ | ||
493 | unset_nonblock(sockfd); | ||
494 | return 0; | ||
495 | } else if (errno != EINPROGRESS) | ||
496 | return -1; | ||
497 | |||
498 | if (waitrfd(sockfd, timeoutp) == -1) | ||
499 | return -1; | ||
500 | |||
501 | /* Completed or failed */ | ||
502 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { | ||
503 | debug("getsockopt: %s", strerror(errno)); | ||
504 | return -1; | ||
505 | } | ||
506 | if (optval != 0) { | ||
507 | errno = optval; | ||
508 | return -1; | ||
509 | } | ||
510 | unset_nonblock(sockfd); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | /* | ||
515 | * Opens a TCP/IP connection to the remote server on the given host. | 447 | * Opens a TCP/IP connection to the remote server on the given host. |
516 | * The address of the remote host will be returned in hostaddr. | 448 | * The address of the remote host will be returned in hostaddr. |
517 | * If port is 0, the default port will be used. | 449 | * If port is 0, the default port will be used. |
@@ -629,110 +561,6 @@ ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, | |||
629 | return ssh_proxy_connect(ssh, host, port, options.proxy_command); | 561 | return ssh_proxy_connect(ssh, host, port, options.proxy_command); |
630 | } | 562 | } |
631 | 563 | ||
632 | static void | ||
633 | send_client_banner(int connection_out, int minor1) | ||
634 | { | ||
635 | /* Send our own protocol version identification. */ | ||
636 | xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n", | ||
637 | PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION); | ||
638 | if (atomicio(vwrite, connection_out, client_version_string, | ||
639 | strlen(client_version_string)) != strlen(client_version_string)) | ||
640 | fatal("write: %.100s", strerror(errno)); | ||
641 | chop(client_version_string); | ||
642 | debug("Local version string %.100s", client_version_string); | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | * Waits for the server identification string, and sends our own | ||
647 | * identification string. | ||
648 | */ | ||
649 | void | ||
650 | ssh_exchange_identification(int timeout_ms) | ||
651 | { | ||
652 | char buf[256], remote_version[256]; /* must be same size! */ | ||
653 | int remote_major, remote_minor, mismatch; | ||
654 | int connection_in = packet_get_connection_in(); | ||
655 | int connection_out = packet_get_connection_out(); | ||
656 | u_int i, n; | ||
657 | size_t len; | ||
658 | int rc; | ||
659 | |||
660 | send_client_banner(connection_out, 0); | ||
661 | |||
662 | /* Read other side's version identification. */ | ||
663 | for (n = 0;;) { | ||
664 | for (i = 0; i < sizeof(buf) - 1; i++) { | ||
665 | if (timeout_ms > 0) { | ||
666 | rc = waitrfd(connection_in, &timeout_ms); | ||
667 | if (rc == -1 && errno == ETIMEDOUT) { | ||
668 | fatal("Connection timed out during " | ||
669 | "banner exchange"); | ||
670 | } else if (rc == -1) { | ||
671 | fatal("%s: %s", | ||
672 | __func__, strerror(errno)); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | len = atomicio(read, connection_in, &buf[i], 1); | ||
677 | if (len != 1 && errno == EPIPE) | ||
678 | fatal("ssh_exchange_identification: " | ||
679 | "Connection closed by remote host"); | ||
680 | else if (len != 1) | ||
681 | fatal("ssh_exchange_identification: " | ||
682 | "read: %.100s", strerror(errno)); | ||
683 | if (buf[i] == '\r') { | ||
684 | buf[i] = '\n'; | ||
685 | buf[i + 1] = 0; | ||
686 | continue; /**XXX wait for \n */ | ||
687 | } | ||
688 | if (buf[i] == '\n') { | ||
689 | buf[i + 1] = 0; | ||
690 | break; | ||
691 | } | ||
692 | if (++n > 65536) | ||
693 | fatal("ssh_exchange_identification: " | ||
694 | "No banner received"); | ||
695 | } | ||
696 | buf[sizeof(buf) - 1] = 0; | ||
697 | if (strncmp(buf, "SSH-", 4) == 0) | ||
698 | break; | ||
699 | debug("ssh_exchange_identification: %s", buf); | ||
700 | } | ||
701 | server_version_string = xstrdup(buf); | ||
702 | |||
703 | /* | ||
704 | * Check that the versions match. In future this might accept | ||
705 | * several versions and set appropriate flags to handle them. | ||
706 | */ | ||
707 | if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", | ||
708 | &remote_major, &remote_minor, remote_version) != 3) | ||
709 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
710 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
711 | remote_major, remote_minor, remote_version); | ||
712 | |||
713 | active_state->compat = compat_datafellows(remote_version); | ||
714 | mismatch = 0; | ||
715 | |||
716 | switch (remote_major) { | ||
717 | case 2: | ||
718 | break; | ||
719 | case 1: | ||
720 | if (remote_minor != 99) | ||
721 | mismatch = 1; | ||
722 | break; | ||
723 | default: | ||
724 | mismatch = 1; | ||
725 | break; | ||
726 | } | ||
727 | if (mismatch) | ||
728 | fatal("Protocol major versions differ: %d vs. %d", | ||
729 | PROTOCOL_MAJOR_2, remote_major); | ||
730 | if ((datafellows & SSH_BUG_RSASIGMD5) != 0) | ||
731 | logit("Server version \"%.100s\" uses unsafe RSA signature " | ||
732 | "scheme; disabling use of RSA keys", remote_version); | ||
733 | chop(server_version_string); | ||
734 | } | ||
735 | |||
736 | /* defaults to 'no' */ | 564 | /* defaults to 'no' */ |
737 | static int | 565 | static int |
738 | confirm(const char *prompt) | 566 | confirm(const char *prompt) |
@@ -1426,7 +1254,7 @@ out: | |||
1426 | * This function does not require super-user privileges. | 1254 | * This function does not require super-user privileges. |
1427 | */ | 1255 | */ |
1428 | void | 1256 | void |
1429 | ssh_login(Sensitive *sensitive, const char *orighost, | 1257 | ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost, |
1430 | struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) | 1258 | struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) |
1431 | { | 1259 | { |
1432 | char *host; | 1260 | char *host; |
@@ -1440,16 +1268,17 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
1440 | lowercase(host); | 1268 | lowercase(host); |
1441 | 1269 | ||
1442 | /* Exchange protocol version identification strings with the server. */ | 1270 | /* Exchange protocol version identification strings with the server. */ |
1443 | ssh_exchange_identification(timeout_ms); | 1271 | if (kex_exchange_identification(ssh, timeout_ms, NULL) != 0) |
1272 | cleanup_exit(255); /* error already logged */ | ||
1444 | 1273 | ||
1445 | /* Put the connection into non-blocking mode. */ | 1274 | /* Put the connection into non-blocking mode. */ |
1446 | packet_set_nonblocking(); | 1275 | ssh_packet_set_nonblocking(ssh); |
1447 | 1276 | ||
1448 | /* key exchange */ | 1277 | /* key exchange */ |
1449 | /* authenticate user */ | 1278 | /* authenticate user */ |
1450 | debug("Authenticating to %s:%d as '%s'", host, port, server_user); | 1279 | debug("Authenticating to %s:%d as '%s'", host, port, server_user); |
1451 | ssh_kex2(host, hostaddr, port); | 1280 | ssh_kex2(ssh, host, hostaddr, port); |
1452 | ssh_userauth2(local_user, server_user, host, sensitive); | 1281 | ssh_userauth2(ssh, local_user, server_user, host, sensitive); |
1453 | free(local_user); | 1282 | free(local_user); |
1454 | } | 1283 | } |
1455 | 1284 | ||