diff options
| author | Daniel Stenberg <daniel@haxx.se> | 2007-10-03 08:00:42 +0000 | 
|---|---|---|
| committer | Daniel Stenberg <daniel@haxx.se> | 2007-10-03 08:00:42 +0000 | 
| commit | 51c6a5d43b09835289a469165aa7a2bfb79dbdc6 (patch) | |
| tree | 49b4c738d465d09c72fd048bb77b91514058b40c | |
| parent | 15b8da1980538f5c56115777610867230b0ac9d1 (diff) | |
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.
| -rw-r--r-- | CHANGES | 7 | ||||
| -rw-r--r-- | RELEASE-NOTES | 7 | ||||
| -rw-r--r-- | docs/curl.1 | 5 | ||||
| -rw-r--r-- | docs/libcurl/curl_easy_setopt.3 | 5 | ||||
| -rw-r--r-- | include/curl/curl.h | 9 | ||||
| -rw-r--r-- | lib/ssh.c | 27 | ||||
| -rw-r--r-- | lib/url.c | 9 | ||||
| -rw-r--r-- | lib/urldata.h | 1 | ||||
| -rw-r--r-- | src/main.c | 14 | 
9 files changed, 76 insertions, 8 deletions
@@ -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 diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 5d792cca1..9c4e06381 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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) diff --git a/docs/curl.1 b/docs/curl.1 index f415d6f30..26d70b8d2 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -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 diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index edf7473ff..ac460ae09 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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. diff --git a/include/curl/curl.h b/include/curl/curl.h index 0df943344..88d495fc9 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -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 @@ -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; @@ -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 diff --git a/lib/urldata.h b/lib/urldata.h index 4ed161a5f..72bd0eb83 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 */ diff --git a/src/main.c b/src/main.c index cf68e845b..4f9b3e4ec 100644 --- a/src/main.c +++ b/src/main.c @@ -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) {  | 
