Added support for Digest and NTLM authentication using GnuTLS.

This commit is contained in:
Dan Fandrich 2009-02-12 20:48:40 +00:00
parent de41c6ef43
commit 9a4c887c4a
10 changed files with 151 additions and 20 deletions

View File

@ -6,6 +6,9 @@
Changelog Changelog
Daniel Fandrich (12 Feb 2009)
- Added support for Digest and NTLM authentication using GnuTLS.
Daniel Stenberg (11 Feb 2009) Daniel Stenberg (11 Feb 2009)
- CURLINFO_CONDITION_UNMET was added to allow an application to get to know if - 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 the condition in the previous request was unmet. This is typically a time

View File

@ -20,6 +20,7 @@ This release includes the following changes:
o Added CURLPROXY_HTTP_1_0 and --proxy1.0 o Added CURLPROXY_HTTP_1_0 and --proxy1.0
o Added docs/libcurl/symbols-in-versions o Added docs/libcurl/symbols-in-versions
o Added CURLINFO_CONDITION_UNMET o Added CURLINFO_CONDITION_UNMET
o Added support for Digest and NTLM authentication using GnuTLS
This release includes the following bugfixes: This release includes the following bugfixes:

View File

@ -2508,7 +2508,7 @@ fi
if test "x$USE_WINDOWS_SSPI" = "x1"; then if test "x$USE_WINDOWS_SSPI" = "x1"; then
SUPPORT_FEATURES="$SUPPORT_FEATURES SSPI" SUPPORT_FEATURES="$SUPPORT_FEATURES SSPI"
fi 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" SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM"
fi fi

View File

@ -788,7 +788,7 @@ FAQ
This is supported in curl 7.10.6 or later. No earlier curl version knows 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 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. not provide NTLM authentication functionality in curl.
NTLM is a Microsoft proprietary protocol. Proprietary formats are evil. You NTLM is a Microsoft proprietary protocol. Proprietary formats are evil. You

View File

@ -328,10 +328,10 @@ to provide the data to send.
8.1 Make NTLM work without OpenSSL functions 8.1 Make NTLM work without OpenSSL functions
Get NTLM working using the functions provided by libgcrypt, since GnuTLS Get NTLM working using the functions provided by NSS. Not strictly
already depends on that to function. Not strictly SSL/TLS related, but SSL/TLS related, but hey... Another option is to get available DES and
hey... Another option is to get available DES and MD4 source code from the MD4 source code from the cryptopp library. They are fine license-wise,
cryptopp library. They are fine license-wise, but are C++. but are C++.
8.2 SSL engine stuff 8.2 SSL engine stuff

View File

@ -33,6 +33,7 @@
#ifdef USE_GNUTLS #ifdef USE_GNUTLS
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <gnutls/x509.h> #include <gnutls/x509.h>
#include <gcrypt.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -777,4 +778,29 @@ size_t Curl_gtls_version(char *buffer, size_t size)
return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); 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 */ #endif /* USE_GNUTLS */

View File

@ -47,6 +47,7 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
void Curl_gtls_session_free(void *ptr); void Curl_gtls_session_free(void *ptr);
size_t Curl_gtls_version(char *buffer, size_t size); size_t Curl_gtls_version(char *buffer, size_t size);
int Curl_gtls_shutdown(struct connectdata *conn, int sockindex); int Curl_gtls_shutdown(struct connectdata *conn, int sockindex);
int Curl_gtls_seed(struct SessionHandle *data);
/* API setup for GnuTLS */ /* API setup for GnuTLS */
#define curlssl_init Curl_gtls_init #define curlssl_init Curl_gtls_init

View File

@ -60,7 +60,6 @@
#include "http_ntlm.h" #include "http_ntlm.h"
#include "url.h" #include "url.h"
#include "memory.h" #include "memory.h"
#include "ssluse.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -68,9 +67,8 @@
/* "NTLMSSP" signature is always in ASCII regardless of the platform */ /* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" #define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
#ifndef USE_WINDOWS_SSPI #ifdef USE_SSLEAY
#include "ssluse.h"
# ifdef USE_SSLEAY
# ifdef USE_OPENSSL # ifdef USE_OPENSSL
# include <openssl/des.h> # include <openssl/des.h>
# include <openssl/md4.h> # include <openssl/md4.h>
@ -84,9 +82,6 @@
# include <ssl.h> # include <ssl.h>
# include <rand.h> # include <rand.h>
# endif # endif
# else
# error "Can't compile NTLM support without OpenSSL."
# endif
#if OPENSSL_VERSION_NUMBER < 0x00907001L #if OPENSSL_VERSION_NUMBER < 0x00907001L
#define DES_key_schedule des_key_schedule #define DES_key_schedule des_key_schedule
@ -104,10 +99,20 @@
#define DESKEY(x) &x #define DESKEY(x) &x
#endif #endif
#else #elif defined(USE_GNUTLS)
#include "gtls.h"
#include <gcrypt.h>
#define MD5_DIGEST_LENGTH 16
#define MD4_DIGEST_LENGTH 16
#elif defined(USE_WINDOWS_SSPI)
#include "curl_sspi.h" #include "curl_sspi.h"
#else
# error "Can't compile NTLM support without a crypto library."
#endif #endif
/* The last #include file should be: */ /* The last #include file should be: */
@ -314,6 +319,7 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn,
#ifndef USE_WINDOWS_SSPI #ifndef USE_WINDOWS_SSPI
#ifdef USE_SSLEAY
/* /*
* Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The
* key schedule ks is also set. * 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_odd_parity(&key);
DES_set_key(&key, ks); 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 * 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, const unsigned char *plaintext,
unsigned char *results) unsigned char *results)
{ {
#ifdef USE_SSLEAY
DES_key_schedule ks; DES_key_schedule ks;
setup_des_key(keys, DESKEY(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)); setup_des_key(keys+14, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16), DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
DESKEY(ks), DES_ENCRYPT); 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. */ /* Create LanManager hashed password. */
#ifdef USE_SSLEAY
DES_key_schedule ks; DES_key_schedule ks;
setup_des_key(pw, DESKEY(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)); setup_des_key(pw+7, DESKEY(ks));
DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8), DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
DESKEY(ks), DES_ENCRYPT); 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); memset(lmbuffer + 16, 0, 21 - 16);
} }
@ -443,11 +504,18 @@ static CURLcode mk_nt_hash(struct SessionHandle *data,
{ {
/* Create NT hashed password. */ /* Create NT hashed password. */
#ifdef USE_SSLEAY
MD4_CTX MD4pw; MD4_CTX MD4pw;
MD4_Init(&MD4pw); MD4_Init(&MD4pw);
MD4_Update(&MD4pw, pw, 2*len); MD4_Update(&MD4pw, pw, 2*len);
MD4_Final(ntbuffer, &MD4pw); 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); memset(ntbuffer + 16, 0, 21 - 16);
} }
@ -837,12 +905,18 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
unsigned char ntbuffer[0x18]; unsigned char ntbuffer[0x18];
unsigned char tmp[0x18]; unsigned char tmp[0x18];
unsigned char md5sum[MD5_DIGEST_LENGTH]; unsigned char md5sum[MD5_DIGEST_LENGTH];
MD5_CTX MD5pw;
unsigned char entropy[8]; unsigned char entropy[8];
/* Need to create 8 bytes random data */ /* 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 */ Curl_ossl_seed(conn->data); /* Initiate the seed if not already done */
RAND_bytes(entropy,8); 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 */ /* 8 bytes random data as challenge in lmresp */
memcpy(lmresp,entropy,8); memcpy(lmresp,entropy,8);
@ -853,9 +927,17 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
memcpy(tmp,&ntlm->nonce[0],8); memcpy(tmp,&ntlm->nonce[0],8);
memcpy(tmp+8,entropy,8); memcpy(tmp+8,entropy,8);
#ifdef USE_SSLEAY
MD5_Init(&MD5pw); MD5_Init(&MD5pw);
MD5_Update(&MD5pw, tmp, 16); MD5_Update(&MD5pw, tmp, 16);
MD5_Final(md5sum, &MD5pw); 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, /* We shall only use the first 8 bytes of md5sum,
but the des code in lm_resp only encrypt the first 8 bytes */ 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) if(mk_nt_hash(conn->data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY)

View File

@ -27,6 +27,24 @@
#include <string.h> #include <string.h>
#include "curl_md5.h"
#ifdef USE_GNUTLS
#include <gcrypt.h>
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 #ifdef USE_SSLEAY
/* When OpenSSL is available we use the MD5-function from OpenSSL */ /* When OpenSSL is available we use the MD5-function from OpenSSL */
@ -341,8 +359,6 @@ static void Decode (UINT4 *output,
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
#include "curl_md5.h"
void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */
const unsigned char *input) const unsigned char *input)
{ {
@ -352,4 +368,6 @@ void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */
MD5_Final(outbuffer, &ctx); MD5_Final(outbuffer, &ctx);
} }
#endif #endif /* USE_GNUTLS */
#endif /* CURL_DISABLE_CRYPTO_AUTH */

View File

@ -455,7 +455,7 @@ int netware_init(void);
#endif #endif
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) #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 #define USE_NTLM
#endif #endif
#endif #endif