aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2007-10-03 08:00:42 +0000
committerDaniel Stenberg <daniel@haxx.se>2007-10-03 08:00:42 +0000
commit51c6a5d43b09835289a469165aa7a2bfb79dbdc6 (patch)
tree49b4c738d465d09c72fd048bb77b91514058b40c
parent15b8da1980538f5c56115777610867230b0ac9d1 (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--CHANGES7
-rw-r--r--RELEASE-NOTES7
-rw-r--r--docs/curl.15
-rw-r--r--docs/libcurl/curl_easy_setopt.35
-rw-r--r--include/curl/curl.h9
-rw-r--r--lib/ssh.c27
-rw-r--r--lib/url.c9
-rw-r--r--lib/urldata.h1
-rw-r--r--src/main.c14
9 files changed, 76 insertions, 8 deletions
diff --git a/CHANGES b/CHANGES
index 1fddc88f0..bcfe39805 100644
--- 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
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
diff --git a/lib/ssh.c b/lib/ssh.c
index d56eb855c..4a9d03732 100644
--- 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;
diff --git a/lib/url.c b/lib/url.c
index afb0b3837..c91d062fe 100644
--- 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
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) {