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.
This commit is contained in:
Daniel Stenberg 2007-10-03 08:00:42 +00:00
parent 15b8da1980
commit 51c6a5d43b
9 changed files with 76 additions and 8 deletions

View File

@ -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

View 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)

View 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

View 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.

View 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

View File

@ -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;

View File

@ -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

View 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 */

View 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) {