summaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-12-27 03:25:24 +0000
committerDamien Miller <djm@mindrot.org>2018-12-27 14:38:22 +1100
commit0a843d9a0e805f14653a555f5c7a8ba99d62c12d (patch)
tree481f36e9fd1918be5449e369a97c086a1a8d2432 /sshconnect.c
parent434b587afe41c19391821e7392005068fda76248 (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.c187
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
72char *client_version_string = NULL;
73char *server_version_string = NULL;
74struct sshkey *previous_host_key = NULL; 73struct sshkey *previous_host_key = NULL;
75 74
76static int matching_host_key_dns = 0; 75static 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 */
452static int
453waitrfd(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
479static int
480timeout_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
632static void
633send_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 */
649void
650ssh_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' */
737static int 565static int
738confirm(const char *prompt) 566confirm(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 */
1428void 1256void
1429ssh_login(Sensitive *sensitive, const char *orighost, 1257ssh_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