]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
Based on a patch brought by Johnny Luong, libcurl now offers
authorDaniel Stenberg <daniel@haxx.se>
Wed, 3 Oct 2007 08:00:42 +0000 (08:00 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 3 Oct 2007 08:00:42 +0000 (08:00 +0000)
CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 and the curl tool --hostpubmd5. They both make
the SCP or SFTP connection verify the remote host's md5 checksum of the public
key before doing a connect, to reduce the risk of a man-in-the-middle attack.

CHANGES
RELEASE-NOTES
docs/curl.1
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/ssh.c
lib/url.c
lib/urldata.h
src/main.c

diff --git a/CHANGES b/CHANGES
index 1fddc88f0150ffedeeb4f4c747e6cad79aad4c7c..bcfe39805a743041695b858fd52ba1a9961d5585 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,13 @@
 
                                   Changelog
 
+Daniel S (3 October 2007)
+- Based on a patch brought by Johnny Luong, libcurl now offers
+  CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 and the curl tool --hostpubmd5. They both
+  make the SCP or SFTP connection verify the remote host's md5 checksum of the
+  public key before doing a connect, to reduce the risk of a man-in-the-middle
+  attack.
+
 Daniel S (2 October 2007)
 - libcurl now handles chunked-encoded CONNECT responses
 
index 5d792cca14a606e7c0fc317030725d55eb409db0..9c4e063819d58b5f9e69221945ee2036b7bd211b 100644 (file)
@@ -2,8 +2,8 @@ Curl and libcurl 7.17.1
 
  Public curl release number:               102
  Releases counted from the very beginning: 128
- Available command line options:           120
- Available curl_easy_setopt() options:     144
+ Available command line options:           121
+ Available curl_easy_setopt() options:     145
  Number of public functions in libcurl:    55
  Amount of public web site mirrors:        42
  Number of known libcurl bindings:         36
@@ -16,6 +16,7 @@ This release includes the following changes:
  o added --proxy-negotiate
  o added --post301 and CURLOPT_POST301
  o builds with c-ares 1.5.0
+ o added CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 and --hostpubmd5
 
 This release includes the following bugfixes:
 
@@ -47,6 +48,6 @@ This release would not have looked like this without help, code, reports and
 advice from friends like these:
 
  Dan Fandrich, Michal Marek, Günter Knauf, Rob Crittenden, Immanuel Gregoire,
- Mark Davies, Max Katsev, Philip Langdale, Alex Fishman
+ Mark Davies, Max Katsev, Philip Langdale, Alex Fishman, Johnny Luong
  
         Thanks! (and sorry if I forgot to mention someone)
index f415d6f309fff294b6ab0a96eb93ad22e8fa3a4c..26d70b8d22697eb81c0e8ac831a569a189d58caa 100644 (file)
@@ -544,6 +544,11 @@ for you.
 See also the \fI-A/--user-agent\fP and \fI-e/--referer\fP options.
 
 This option can be used multiple times to add/replace/remove multiple headers.
+.IP "--hostpubmd5"
+Pass a string containing 32 hexadecimal digits. The string should be the 128
+bit MD5 cheksum of the remote host's public key, curl will refuse the
+connection with the host unless the md5sums match. This option is only for SCP
+and SFTP transfers. (Added in 7.17.1)
 .IP "--ignore-content-length"
 (HTTP)
 Ignore the Content-Length header. This is particularly useful for servers
index edf7473ffb7da0d529ca68f9d7768bc02558f86c..ac460ae095c144ac4b6a3f49bfedc979192134d2 100644 (file)
@@ -1411,6 +1411,11 @@ Pass a long set to a bitmask consisting of one or more of
 CURLSSH_AUTH_PUBLICKEY, CURLSSH_AUTH_PASSWORD, CURLSSH_AUTH_HOST,
 CURLSSH_AUTH_KEYBOARD. Set CURLSSH_AUTH_ANY to let libcurl pick one.
 (Added in 7.16.1)
+.IP CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
+Pass a char * pointing to a string containing 32 hexadecimal digits. The
+string should be the 128 bit MD5 cheksum of the remote host's public key, and
+libcurl will reject the connection to the host unless the md5sums match. This
+option is only for SCP and SFTP transfers. (Added in 7.17.1)
 .IP CURLOPT_SSH_PUBLIC_KEYFILE
 Pass a char * pointing to a file name for your public key. If not used,
 libcurl defaults to using \fB~/.ssh/id_dsa.pub\fP.
index 0df94334482e04d9a0c90ca64487725f9a29c70e..88d495fc9cc6154d5e3db37d9e8a04ca3aac44c1 100644 (file)
@@ -419,7 +419,7 @@ typedef enum {
 /* These are scheduled to disappear by 2009 */
 
 /* The following were added in 7.17.0 */
-#define CURLE_OBSOLETE CURLE_OBSOLETE50        /* noone should be using this! */
+#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* noone should be using this! */
 #define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46
 #define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44
 #define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10
@@ -438,7 +438,7 @@ typedef enum {
 #define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR
 #define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL
 #define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS
-#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR 
+#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR
 #define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED
 
 /* The following were added earlier */
@@ -1127,6 +1127,9 @@ typedef enum {
   /* Obey RFC 2616/10.3.2 and keep POSTs as POSTs after a 301 */
   CINIT(POST301, LONG, 161),
 
+  /* used by scp/sftp to verify the host's public key */
+  CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -1137,7 +1140,7 @@ typedef enum {
 /* These are scheduled to disappear by 2009 */
 
 /* The following were added in 7.17.0 */
-#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD 
+#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD
 #define CURLOPT_FTPAPPEND CURLOPT_APPEND
 #define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY
 #define CURLOPT_FTP_SSL CURLOPT_USE_SSL
index d56eb855c05b5f01902c406f96bce95dde9ae4d2..4a9d0373210af799e4dafd2e5239a95378327a07 100644 (file)
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -310,7 +310,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
 #ifdef CURL_LIBSSH2_DEBUG
   const char *fingerprint;
 #endif /* CURL_LIBSSH2_DEBUG */
-  int rc;
+  const char *host_public_key_md5;
+  int rc,i;
   long err;
 
   switch(sshc->state) {
@@ -351,6 +352,30 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
       infof(data, "\n");
 #endif /* CURL_LIBSSH2_DEBUG */
 
+      /* Before we authenticate we check the hostkey's MD5 fingerprint
+       * against a known fingerprint, if available.  This implementation pulls
+       * it from the curl option.
+       */
+      if (data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
+          strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32)
+      {
+        char buf[33];
+        host_public_key_md5 = libssh2_hostkey_hash(sftp_scp->ssh_session,
+                                                   LIBSSH2_HOSTKEY_HASH_MD5);
+        for (i = 0; i < 16; i++)
+          snprintf(&buf[i*2], 3, "%02x",
+                   (unsigned char) host_public_key_md5[i]);
+        if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
+          failf(data,
+                "Denied establishing ssh session: mismatch md5 fingerprint. "
+                "Remote %s is not equal to %s",
+                buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
+          state(conn, SSH_SESSION_FREE);
+          sshc->actualCode = CURLE_FAILED_INIT;
+          break;
+        }
+      }
+
       state(conn, SSH_AUTHLIST);
       break;
 
index afb0b3837facd019fa0775f858474d363ff2ebd9..c91d062fecf28f9d1e26ca13b020d8b4b9a3bc30 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1836,7 +1836,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
                             va_arg(param, char *));
     break;
-
+  case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
+    /*
+     * Option to allow for the MD5 of the host public key to be checked 
+     * for validation purposes.
+     */
+    result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
+                            va_arg(param, char *));
+    break;
   case CURLOPT_HTTP_TRANSFER_DECODING:
     /*
      * disable libcurl transfer encoding is used
index 4ed161a5f3248a295d15a847a0c9c0b1af4935fe..72bd0eb83af0878051c3c36c301f405ac69f7226 100644 (file)
@@ -1289,6 +1289,7 @@ enum dupstring {
   STRING_SSL_RANDOM_FILE, /* path to file containing "random" data */
   STRING_USERAGENT,       /* User-Agent string */
   STRING_USERPWD,         /* <user:password>, if used */
+  STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
 
   /* -- end of strings -- */
   STRING_LAST /* not used, just an end-of-list marker */
index cf68e845b822ec30cf0c962e055b03f8e247f3c7..4f9b3e4ece42e9b0f9ba8aa38a6e5dd89b07f38a 100644 (file)
@@ -407,6 +407,7 @@ struct Configurable {
   char *key_type;
   char *key_passwd;
   char *pubkey;
+  char *hostpubmd5;
   char *engine;
   bool list_engines;
   bool crlf;
@@ -639,6 +640,7 @@ static void help(void)
     "    --cacert <file> CA certificate to verify peer against (SSL)",
     "    --capath <directory> CA directory (made using c_rehash) to verify",
     "                    peer against (SSL)",
+    "    --hostpubmd5 <md5> Hex encoded MD5 string of the host public key. (SSH)",
     "    --ciphers <list> SSL ciphers to use (SSL)",
     "    --compressed    Request compressed response (using deflate or gzip)",
     "    --connect-timeout <seconds> Maximum time allowed for connection",
@@ -1541,6 +1543,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
     {"Ef","engine",      TRUE},
     {"Eg","capath ",     TRUE},
     {"Eh","pubkey",      TRUE},
+    {"Ei", "hostpubmd5", TRUE},
     {"f", "fail",        FALSE},
     {"F", "form",        TRUE},
     {"Fs","form-string", TRUE},
@@ -2159,6 +2162,11 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
       case 'h': /* --pubkey public key file */
         GetStr(&config->pubkey, nextarg);
         break;
+      case 'i': /* --hostpubmd5 md5 of the host public key */
+        GetStr(&config->hostpubmd5, nextarg);
+        if (!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
+           return PARAM_BAD_USE;
+        break;
       default: /* certificate file */
         {
           char *ptr = strchr(nextarg, ':');
@@ -4206,6 +4214,12 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
         my_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
         my_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
 
+        /* SSH host key md5 checking allows us to fail if we are
+         * not talking to who we think we should 
+         */
+        my_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, config->hostpubmd5);
+
+
         /* default to strict verifyhost */
         my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
         if(config->cacert || config->capath) {