From 9a4c887c4a7279acc4cae66b11540746244e9cc3 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 12 Feb 2009 20:48:40 +0000 Subject: [PATCH] Added support for Digest and NTLM authentication using GnuTLS. --- CHANGES | 3 ++ RELEASE-NOTES | 1 + configure.ac | 2 +- docs/FAQ | 2 +- docs/TODO | 8 ++-- lib/gtls.c | 26 ++++++++++++ lib/gtls.h | 1 + lib/http_ntlm.c | 102 +++++++++++++++++++++++++++++++++++++++++++----- lib/md5.c | 24 ++++++++++-- lib/setup.h | 2 +- 10 files changed, 151 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 3832faab6..17d4039ee 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ Changelog +Daniel Fandrich (12 Feb 2009) +- Added support for Digest and NTLM authentication using GnuTLS. + Daniel Stenberg (11 Feb 2009) - CURLINFO_CONDITION_UNMET was added to allow an application to get to know if the condition in the previous request was unmet. This is typically a time diff --git a/RELEASE-NOTES b/RELEASE-NOTES index b1d5263d9..0661de35c 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -20,6 +20,7 @@ This release includes the following changes: o Added CURLPROXY_HTTP_1_0 and --proxy1.0 o Added docs/libcurl/symbols-in-versions o Added CURLINFO_CONDITION_UNMET + o Added support for Digest and NTLM authentication using GnuTLS This release includes the following bugfixes: diff --git a/configure.ac b/configure.ac index 063025004..779e0e8b0 100644 --- a/configure.ac +++ b/configure.ac @@ -2508,7 +2508,7 @@ fi if test "x$USE_WINDOWS_SSPI" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSPI" fi -if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1"; then +if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" -o "x$USE_GNUTLS" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM" fi diff --git a/docs/FAQ b/docs/FAQ index 39fd3bdf7..e3814b343 100644 --- a/docs/FAQ +++ b/docs/FAQ @@ -788,7 +788,7 @@ FAQ This is supported in curl 7.10.6 or later. No earlier curl version knows of this magic. Later versions require the OpenSSL or Microsoft Windows - libraries to provide this functionality. Using GnuTLS or NSS libraries will + libraries to provide this functionality. Using the NSS library will not provide NTLM authentication functionality in curl. NTLM is a Microsoft proprietary protocol. Proprietary formats are evil. You diff --git a/docs/TODO b/docs/TODO index d38a4aa7d..a0d8b4c6a 100644 --- a/docs/TODO +++ b/docs/TODO @@ -328,10 +328,10 @@ to provide the data to send. 8.1 Make NTLM work without OpenSSL functions - Get NTLM working using the functions provided by libgcrypt, since GnuTLS - already depends on that to function. Not strictly SSL/TLS related, but - hey... Another option is to get available DES and MD4 source code from the - cryptopp library. They are fine license-wise, but are C++. + Get NTLM working using the functions provided by NSS. Not strictly + SSL/TLS related, but hey... Another option is to get available DES and + MD4 source code from the cryptopp library. They are fine license-wise, + but are C++. 8.2 SSL engine stuff diff --git a/lib/gtls.c b/lib/gtls.c index 53a7400a8..b37edd45f 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -33,6 +33,7 @@ #ifdef USE_GNUTLS #include #include +#include #include #include @@ -777,4 +778,29 @@ size_t Curl_gtls_version(char *buffer, size_t size) return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); } +static void gtls_seed(struct SessionHandle *data) +{ + /* TODO: to a good job seeding the RNG */ + /* This may involve the gcry_control function and these options: */ + /* GCRYCTL_SET_RANDOM_SEED_FILE */ + /* GCRYCTL_SET_RNDEGD_SOCKET */ +} + +int Curl_gtls_seed(struct SessionHandle *data) +{ + /* we have the "SSL is seeded" boolean static to prevent multiple + time-consuming seedings in vain */ + static bool ssl_seeded = FALSE; + + /* Quickly add a bit of entropy */ + gcry_fast_random_poll(); + + if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] || + data->set.str[STRING_SSL_EGDSOCKET]) { + gtls_seed(data); + ssl_seeded = TRUE; + } + return 0; +} + #endif /* USE_GNUTLS */ diff --git a/lib/gtls.h b/lib/gtls.h index 9d8c0723d..661cfef78 100644 --- a/lib/gtls.h +++ b/lib/gtls.h @@ -47,6 +47,7 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ void Curl_gtls_session_free(void *ptr); size_t Curl_gtls_version(char *buffer, size_t size); int Curl_gtls_shutdown(struct connectdata *conn, int sockindex); +int Curl_gtls_seed(struct SessionHandle *data); /* API setup for GnuTLS */ #define curlssl_init Curl_gtls_init diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 759647238..59a2d6924 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -60,7 +60,6 @@ #include "http_ntlm.h" #include "url.h" #include "memory.h" -#include "ssluse.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -68,9 +67,8 @@ /* "NTLMSSP" signature is always in ASCII regardless of the platform */ #define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" -#ifndef USE_WINDOWS_SSPI - -# ifdef USE_SSLEAY +#ifdef USE_SSLEAY +#include "ssluse.h" # ifdef USE_OPENSSL # include # include @@ -84,9 +82,6 @@ # include # include # endif -# else -# error "Can't compile NTLM support without OpenSSL." -# endif #if OPENSSL_VERSION_NUMBER < 0x00907001L #define DES_key_schedule des_key_schedule @@ -104,10 +99,20 @@ #define DESKEY(x) &x #endif -#else +#elif defined(USE_GNUTLS) + +#include "gtls.h" +#include + +#define MD5_DIGEST_LENGTH 16 +#define MD4_DIGEST_LENGTH 16 + +#elif defined(USE_WINDOWS_SSPI) #include "curl_sspi.h" +#else +# error "Can't compile NTLM support without a crypto library." #endif /* The last #include file should be: */ @@ -314,6 +319,7 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, #ifndef USE_WINDOWS_SSPI +#ifdef USE_SSLEAY /* * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * key schedule ks is also set. @@ -335,6 +341,28 @@ static void setup_des_key(const unsigned char *key_56, DES_set_odd_parity(&key); DES_set_key(&key, ks); } +#elif defined(USE_GNUTLS) + +/* + * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. + */ +static void setup_des_key(const unsigned char *key_56, + gcry_cipher_hd_t *des) +{ + char key[8]; + + key[0] = key_56[0]; + key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); + key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); + key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); + key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); + key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); + key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); + key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); + + gcry_cipher_setkey(*des, key, 8); +} +#endif /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The @@ -345,6 +373,7 @@ static void lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { +#ifdef USE_SSLEAY DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); @@ -358,6 +387,24 @@ static void lm_resp(const unsigned char *keys, setup_des_key(keys+14, DESKEY(ks)); DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16), DESKEY(ks), DES_ENCRYPT); +#elif USE_GNUTLS + gcry_cipher_hd_t des; + + gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + setup_des_key(keys, &des); + gcry_cipher_encrypt(des, results, 8, plaintext, 8); + gcry_cipher_close(des); + + gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + setup_des_key(keys+7, &des); + gcry_cipher_encrypt(des, results+8, 8, plaintext, 8); + gcry_cipher_close(des); + + gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + setup_des_key(keys+14, &des); + gcry_cipher_encrypt(des, results+16, 8, plaintext, 8); + gcry_cipher_close(des); +#endif } @@ -391,6 +438,7 @@ static void mk_lm_hash(struct SessionHandle *data, { /* Create LanManager hashed password. */ +#ifdef USE_SSLEAY DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); @@ -400,6 +448,19 @@ static void mk_lm_hash(struct SessionHandle *data, setup_des_key(pw+7, DESKEY(ks)); DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8), DESKEY(ks), DES_ENCRYPT); +#elif USE_GNUTLS + gcry_cipher_hd_t des; + + gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + setup_des_key(pw, &des); + gcry_cipher_encrypt(des, lmbuffer, 8, magic, 8); + gcry_cipher_close(des); + + gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + setup_des_key(pw+7, &des); + gcry_cipher_encrypt(des, lmbuffer+8, 8, magic, 8); + gcry_cipher_close(des); +#endif memset(lmbuffer + 16, 0, 21 - 16); } @@ -443,11 +504,18 @@ static CURLcode mk_nt_hash(struct SessionHandle *data, { /* Create NT hashed password. */ +#ifdef USE_SSLEAY MD4_CTX MD4pw; - MD4_Init(&MD4pw); MD4_Update(&MD4pw, pw, 2*len); MD4_Final(ntbuffer, &MD4pw); +#elif USE_GNUTLS + gcry_md_hd_t MD4pw; + gcry_md_open(&MD4pw, GCRY_MD_MD4, 0); + gcry_md_write(MD4pw, pw, 2*len); + memcpy (ntbuffer, gcry_md_read (MD4pw, 0), MD4_DIGEST_LENGTH); + gcry_md_close(MD4pw); +#endif memset(ntbuffer + 16, 0, 21 - 16); } @@ -837,12 +905,18 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, unsigned char ntbuffer[0x18]; unsigned char tmp[0x18]; unsigned char md5sum[MD5_DIGEST_LENGTH]; - MD5_CTX MD5pw; unsigned char entropy[8]; /* Need to create 8 bytes random data */ +#ifdef USE_SSLEAY + MD5_CTX MD5pw; Curl_ossl_seed(conn->data); /* Initiate the seed if not already done */ RAND_bytes(entropy,8); +#elif USE_GNUTLS + gcry_md_hd_t MD5pw; + Curl_gtls_seed(conn->data); /* Initiate the seed if not already done */ + gcry_randomize(entropy, 8, GCRY_STRONG_RANDOM); +#endif /* 8 bytes random data as challenge in lmresp */ memcpy(lmresp,entropy,8); @@ -853,9 +927,17 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, memcpy(tmp,&ntlm->nonce[0],8); memcpy(tmp+8,entropy,8); +#ifdef USE_SSLEAY MD5_Init(&MD5pw); MD5_Update(&MD5pw, tmp, 16); MD5_Final(md5sum, &MD5pw); +#elif USE_GNUTLS + gcry_md_open(&MD5pw, GCRY_MD_MD5, 0); + gcry_md_write(MD5pw, tmp, MD5_DIGEST_LENGTH); + memcpy(md5sum, gcry_md_read (MD5pw, 0), MD5_DIGEST_LENGTH); + gcry_md_close(MD5pw); +#endif + /* We shall only use the first 8 bytes of md5sum, but the des code in lm_resp only encrypt the first 8 bytes */ if(mk_nt_hash(conn->data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY) diff --git a/lib/md5.c b/lib/md5.c index 965578004..8f2a38dd1 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -27,6 +27,24 @@ #include +#include "curl_md5.h" + +#ifdef USE_GNUTLS + +#include + +void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ + const unsigned char *input) +{ + gcry_md_hd_t ctx; + gcry_md_open(&ctx, GCRY_MD_MD5, 0); + gcry_md_write(ctx, input, (unsigned int)strlen((char *)input)); + memcpy (outbuffer, gcry_md_read (ctx, 0), 16); + gcry_md_close(ctx); +} + +#else + #ifdef USE_SSLEAY /* When OpenSSL is available we use the MD5-function from OpenSSL */ @@ -341,8 +359,6 @@ static void Decode (UINT4 *output, #endif /* USE_SSLEAY */ -#include "curl_md5.h" - void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ const unsigned char *input) { @@ -352,4 +368,6 @@ void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ MD5_Final(outbuffer, &ctx); } -#endif +#endif /* USE_GNUTLS */ + +#endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/lib/setup.h b/lib/setup.h index 5b675edda..efab2ea6f 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -455,7 +455,7 @@ int netware_init(void); #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || defined(USE_GNUTLS) #define USE_NTLM #endif #endif