]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
libssh: Fix matching user-specified MD5 hex key
authorJay Satiro <raysatiro@yahoo.com>
Sun, 23 Feb 2020 23:37:09 +0000 (18:37 -0500)
committerJay Satiro <raysatiro@yahoo.com>
Sat, 7 Mar 2020 08:06:11 +0000 (03:06 -0500)
Prior to this change a match would never be successful because it
was mistakenly coded to compare binary data from libssh to a
user-specified hex string (ie CURLOPT_SSH_HOST_PUBLIC_KEY_MD5).

Reported-by: fds242@users.noreply.github.com
Fixes https://github.com/curl/curl/issues/4971
Closes https://github.com/curl/curl/pull/4974

lib/vssh/libssh.c
tests/.gitignore
tests/FILEFORMAT
tests/data/Makefile.inc
tests/data/test664 [new file with mode: 0644]
tests/data/test665 [new file with mode: 0644]
tests/runtests.pl
tests/sshhelp.pm
tests/sshserver.pl

index 647b4d4913a6a8302264a44c7e1e55414e53b730..08d9f9e0fb26f42eb1d823d198a956946bd4131e 100644 (file)
@@ -345,13 +345,27 @@ static int myssh_is_known(struct connectdata *conn)
     return rc;
 
   if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+    int i;
+    char md5buffer[33];
+    const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
+
     rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
                                 &hash, &hlen);
-    if(rc != SSH_OK)
+    if(rc != SSH_OK || hlen != 16) {
+      failf(data,
+            "Denied establishing ssh session: md5 fingerprint not available");
       goto cleanup;
+    }
+
+    for(i = 0; i < 16; i++)
+      msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
+
+    infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
 
-    if(hlen != strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) ||
-       memcmp(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], hash, hlen)) {
+    if(!strcasecompare(md5buffer, pubkey_md5)) {
+      failf(data,
+            "Denied establishing ssh session: mismatch md5 fingerprint. "
+            "Remote %s is not equal to %s", md5buffer, pubkey_md5);
       rc = SSH_ERROR;
       goto cleanup;
     }
index fbbc164857286c470b993246b19d70efe3b4ffa1..abf9aed8c20d2431e14525d0441b26da3e0b39ef 100644 (file)
@@ -6,6 +6,7 @@ curl_client_key.pub
 curl_client_knownhosts
 curl_host_rsa_key
 curl_host_rsa_key.pub
+curl_host_rsa_key.pub_md5
 curl_sftp_cmds
 curl_sftp_config
 curl_ssh_config
index 42ebe287168f9f88e9d2efeb4e2f899bdcf08ccd..16e7d30925ade2e55ad9553787d8869501ae81c1 100644 (file)
@@ -366,6 +366,7 @@ Available substitute variables include:
 %FILE_PWD  - Current directory, on windows prefixed with a slash
 %RTSP6PORT - IPv6 port number of the RTSP server
 %RTSPPORT  - Port number of the RTSP server
+%SSHSRVMD5 - MD5 of SSH server's public key
 %SMTP6PORT - IPv6 port number of the SMTP server
 %SMTPPORT  - Port number of the SMTP server
 %SOCKSPORT - Port number of the SOCKS4/5 server
index dfc74320e1a4a5cec27588be41401753c1df339c..9afbf74372dbd3bb4adeae7a229571a883a4601e 100644 (file)
@@ -85,7 +85,7 @@ test626 test627 test628 test629 test630 test631 test632 test633 test634 \
 test635 test636 test637 test638 test639 test640 test641 test642 \
 test643 test644 test645 test646 test647 test648 test649 test650 test651 \
 test652 test653 test654 test655 test656 test658 test659 test660 test661 \
-test662 test663 \
+test662 test663 test664 test665 \
 \
 test700 test701 test702 test703 test704 test705 test706 test707 test708 \
 test709 test710 test711 test712 test713 test714 test715 test716 test717 \
diff --git a/tests/data/test664 b/tests/data/test664
new file mode 100644 (file)
index 0000000..cb73b24
--- /dev/null
@@ -0,0 +1,44 @@
+<testcase>
+<info>
+<keywords>
+SFTP
+server key check
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+test
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+sftp
+</server>
+ <name>
+SFTP correct host key
+ </name>
+ <command>
+--hostpubmd5 %SSHSRVMD5 --key curl_client_key --pubkey curl_client_key.pub -u %USER: sftp://%HOSTIP:%SSHPORT%POSIX_PWD/log/file664.txt
+</command>
+<file name="log/file664.txt">
+test
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+<valgrind>
+disable
+</valgrind>
+</verify>
+</testcase>
diff --git a/tests/data/test665 b/tests/data/test665
new file mode 100644 (file)
index 0000000..830adb8
--- /dev/null
@@ -0,0 +1,44 @@
+<testcase>
+<info>
+<keywords>
+SCP
+server key check
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+test
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+scp
+</server>
+ <name>
+SCP correct host key
+ </name>
+ <command>
+--hostpubmd5 %SSHSRVMD5 --key curl_client_key --pubkey curl_client_key.pub -u %USER: scp://%HOSTIP:%SSHPORT%POSIX_PWD/log/file665.txt
+</command>
+<file name="log/file665.txt">
+test
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+<valgrind>
+disable
+</valgrind>
+</verify>
+</testcase>
index 827fdec50863d3272aee7e776668a1b276704ffd..08d9f9f3a5ceaf1842851bc8477345e66b273fef 100755 (executable)
@@ -152,6 +152,8 @@ my $SMBPORT;             # SMB server port
 my $SMBSPORT;            # SMBS server port
 my $NEGTELNETPORT;       # TELNET server port with negotiation
 
+my $SSHSRVMD5;           # MD5 of ssh server public key
+
 my $srcdir = $ENV{'srcdir'} || '.';
 my $CURL="../src/curl".exe_ext('TOOL'); # what curl executable to run on the tests
 my $VCURL=$CURL;   # what curl binary to use to verify the servers with
@@ -2139,6 +2141,18 @@ sub runsshserver {
         return (0,0);
     }
 
+    my $hstpubmd5f = "curl_host_rsa_key.pub_md5";
+    if(!open(PUBMD5FILE, "<", $hstpubmd5f) ||
+       (read(PUBMD5FILE, $SSHSRVMD5, 32) != 32) ||
+       !close(PUBMD5FILE) ||
+       ($SSHSRVMD5 !~ /^[a-f0-9]{32}$/i))
+    {
+        my $msg = "Fatal: $srvrname pubkey md5 missing : \"$hstpubmd5f\" : $!";
+        logmsg "$msg\n";
+        stopservers($verbose);
+        die $msg;
+    }
+
     if($verbose) {
         logmsg "RUN: $srvrname server is now running PID $pid2\n";
     }
@@ -3158,6 +3172,16 @@ sub subVariables {
   $$thing =~ s/%SRCDIR/$srcdir/g;
   $$thing =~ s/%USER/$USER/g;
 
+  if($$thing =~ /%SSHSRVMD5/) {
+      if(!$SSHSRVMD5) {
+          my $msg = "Fatal: Missing SSH server pubkey MD5. Is server running?";
+          logmsg "$msg\n";
+          stopservers($verbose);
+          die $msg;
+      }
+      $$thing =~ s/%SSHSRVMD5/$SSHSRVMD5/g;
+  }
+
   # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
   # used for time-out tests and that would work on most hosts as these
   # adjust for the startup/check time for this particular host. We needed
index 248be67a2368a4a6fa95c16443a1584b9bb34ba7..e43f86532790fcfe0896e3317b939181b093b478 100644 (file)
@@ -50,6 +50,7 @@ use vars qw(
     $sftpcmds
     $hstprvkeyf
     $hstpubkeyf
+    $hstpubmd5f
     $cliprvkeyf
     $clipubkeyf
     @sftppath
@@ -82,6 +83,7 @@ use vars qw(
     $sftpcmds
     $hstprvkeyf
     $hstpubkeyf
+    $hstpubmd5f
     $cliprvkeyf
     $clipubkeyf
     display_sshdconfig
@@ -122,6 +124,7 @@ $sftpcmds        = 'curl_sftp_cmds';         # sftp client commands batch file
 $knownhosts      = 'curl_client_knownhosts'; # ssh knownhosts file
 $hstprvkeyf      = 'curl_host_rsa_key';      # host private key file
 $hstpubkeyf      = 'curl_host_rsa_key.pub';  # host public key file
+$hstpubmd5f      = 'curl_host_rsa_key.pub_md5';  # md5 hash of host public key
 $cliprvkeyf      = 'curl_client_key';        # client private key file
 $clipubkeyf      = 'curl_client_key.pub';    # client public key file
 
index 197e8b872ee9ce4f52a71499a8e5c4bccc9ebc77..4414ca51bd9b9df3762af02d18c7bbd1afde9fd0 100644 (file)
@@ -28,6 +28,9 @@ use strict;
 use warnings;
 use Cwd;
 use Cwd 'abs_path';
+use Digest::MD5;
+use Digest::MD5 'md5_hex';
+use MIME::Base64;
 
 #***************************************************************************
 # Variables and subs imported from sshhelp module
@@ -48,6 +51,7 @@ use sshhelp qw(
     $sftpcmds
     $hstprvkeyf
     $hstpubkeyf
+    $hstpubmd5f
     $cliprvkeyf
     $clipubkeyf
     display_sshdconfig
@@ -357,10 +361,11 @@ if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) ||
 #
 if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) ||
    (! -e $hstpubkeyf) || (! -s $hstpubkeyf) ||
+   (! -e $hstpubmd5f) || (! -s $hstpubmd5f) ||
    (! -e $cliprvkeyf) || (! -s $cliprvkeyf) ||
    (! -e $clipubkeyf) || (! -s $clipubkeyf)) {
     # Make sure all files are gone so ssh-keygen doesn't complain
-    unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf);
+    unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $cliprvkeyf, $clipubkeyf);
     logmsg 'generating host keys...' if($verbose);
     if(system "\"$sshkeygen\" -q -t rsa -f $hstprvkeyf -C 'curl test server' -N ''") {
         logmsg 'Could not generate host key';
@@ -374,6 +379,21 @@ if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) ||
     # Make sure that permissions are restricted so openssh doesn't complain
     system "chmod 600 $hstprvkeyf";
     system "chmod 600 $cliprvkeyf";
+    # Save md5 hash of public host key
+    open(RSAKEYFILE, "<$hstpubkeyf");
+    my @rsahostkey = do { local $/ = ' '; <RSAKEYFILE> };
+    close(RSAKEYFILE);
+    if(!$rsahostkey[1]) {
+        logmsg 'Failed parsing base64 encoded RSA host key';
+        exit 1;
+    }
+    open(PUBMD5FILE, ">$hstpubmd5f");
+    print PUBMD5FILE md5_hex(decode_base64($rsahostkey[1]));
+    close(PUBMD5FILE);
+    if((! -e $hstpubmd5f) || (! -s $hstpubmd5f)) {
+        logmsg 'Failed writing md5 hash of RSA host key';
+        exit 1;
+    }
 }
 
 
@@ -1099,8 +1119,8 @@ elsif($verbose && ($rc >> 8)) {
 #***************************************************************************
 # Clean up once the server has stopped
 #
-unlink($hstprvkeyf, $hstpubkeyf, $cliprvkeyf, $clipubkeyf, $knownhosts);
-unlink($sshdconfig, $sshconfig, $sftpconfig);
-
+unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f,
+       $cliprvkeyf, $clipubkeyf, $knownhosts,
+       $sshdconfig, $sshconfig, $sftpconfig);
 
 exit 0;