From d616d7a60a6cf6b6f72208d70d4ea97a0e075f33 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 28 Jun 2010 11:29:02 +0000 Subject: [PATCH] - add support for NTLM authentication (#603783) --- 0001-curl-7.21.0-ntlm.patch | 855 ++++++++++++++++++++++++++++++++++++ curl.spec | 11 +- 2 files changed, 865 insertions(+), 1 deletion(-) create mode 100644 0001-curl-7.21.0-ntlm.patch diff --git a/0001-curl-7.21.0-ntlm.patch b/0001-curl-7.21.0-ntlm.patch new file mode 100644 index 0000000..617813f --- /dev/null +++ b/0001-curl-7.21.0-ntlm.patch @@ -0,0 +1,855 @@ + CHANGES | 20 ++++ + configure.ac | 3 +- + lib/Makefile.inc | 4 +- + lib/curl_md4.h | 33 +++++++ + lib/http_ntlm.c | 126 +++++++++++++++++++++++-- + lib/md4.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + lib/nss.c | 162 ++++++++++++++++++------------- + lib/nssg.h | 3 + + lib/setup.h | 2 +- + 9 files changed, 556 insertions(+), 78 deletions(-) + +diff --git a/CHANGES b/CHANGES +index f561aad..30bb18b 100644 +--- a/CHANGES ++++ b/CHANGES +@@ -6,6 +6,26 @@ + + Changelog + ++Kamil Dudka (28 Jun 2010) ++- http_ntlm: add support for NSS ++ ++ When configured with '--without-ssl --with-nss', NTLM authentication ++ now uses NSS crypto library for MD5 and DES. For MD4 we have a local ++ implementation in that case. More details are available at ++ https://bugzilla.redhat.com/603783 ++ ++ In order to get it working, curl_global_init() must be called with ++ CURL_GLOBAL_SSL or CURL_GLOBAL_ALL. That's necessary because NSS needs ++ to be initialized globally and we do so only when the NSS library is ++ actually required by protocol. The mentioned call of curl_global_init() ++ is responsible for creating of the initialization mutex. ++ ++ There was also slightly changed the NSS initialization scenario, in ++ particular, loading of the NSS PEM module. It used to be loaded always ++ right after the NSS library was initialized. Now the library is ++ initialized as soon as any SSL or NTLM is required, while the PEM module ++ is prevented from being loaded until the SSL is actually required. ++ + Version 7.21.0 (16 June 2010) + + Daniel Stenberg (5 June 2010) +diff --git a/configure.ac b/configure.ac +index af30b8a..18a3642 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2659,7 +2659,8 @@ 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" -o "x$GNUTLS_ENABLED" = "x1"; then ++if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \ ++ -o "x$GNUTLS_ENABLED" = "x1" -o "x$NSS_ENABLED" = "x1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM" + fi + +diff --git a/lib/Makefile.inc b/lib/Makefile.inc +index 6311780..a502e8f 100644 +--- a/lib/Makefile.inc ++++ b/lib/Makefile.inc +@@ -6,7 +6,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ + netrc.c getinfo.c transfer.c strequal.c easy.c security.c krb4.c \ + curl_fnmatch.c fileinfo.c ftplistparser.c wildcard.c \ + krb5.c memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c \ +- multi.c content_encoding.c share.c http_digest.c md5.c curl_rand.c \ ++ multi.c content_encoding.c share.c http_digest.c md4.c md5.c curl_rand.c \ + http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \ + hostares.c hostasyn.c hostip4.c hostip6.c hostsyn.c hostthre.c \ + inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c \ +@@ -20,7 +20,7 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ + if2ip.h speedcheck.h urldata.h curl_ldap.h ssluse.h escape.h telnet.h \ + getinfo.h strequal.h krb4.h memdebug.h http_chunks.h curl_rand.h \ + curl_fnmatch.h wildcard.h fileinfo.h ftplistparser.h \ +- strtok.h connect.h llist.h hash.h content_encoding.h share.h \ ++ strtok.h connect.h llist.h hash.h content_encoding.h share.h curl_md4.h \ + curl_md5.h http_digest.h http_negotiate.h http_ntlm.h inet_pton.h \ + strtoofft.h strerror.h inet_ntop.h curlx.h curl_memory.h setup.h \ + transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h gtls.h \ +diff --git a/lib/curl_md4.h b/lib/curl_md4.h +new file mode 100644 +index 0000000..6b6c16e +--- /dev/null ++++ b/lib/curl_md4.h +@@ -0,0 +1,33 @@ ++#ifndef HEADER_CURL_MD4_H ++#define HEADER_CURL_MD4_H ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. ++ * ++ * This software is licensed as described in the file COPYING, which ++ * you should have received as part of this distribution. The terms ++ * are also available at http://curl.haxx.se/docs/copyright.html. ++ * ++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell ++ * copies of the Software, and permit persons to whom the Software is ++ * furnished to do so, under the terms of the COPYING file. ++ * ++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ++ * KIND, either express or implied. ++ * ++ ***************************************************************************/ ++ ++#include "setup.h" ++ ++/* NSS crypto library does not provide the MD4 hash algorithm, so that we have ++ * a local implementation of it */ ++#ifdef USE_NSS ++void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len); ++#endif /* USE_NSS */ ++ ++#endif /* HEADER_CURL_MD4_H */ +diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c +index 36e538e..0e831ca 100644 +--- a/lib/http_ntlm.c ++++ b/lib/http_ntlm.c +@@ -116,6 +116,15 @@ + #define MD5_DIGEST_LENGTH 16 + #define MD4_DIGEST_LENGTH 16 + ++#elif defined(USE_NSS) ++ ++#include "curl_md4.h" ++#include "nssg.h" ++#include ++#include ++#include ++#define MD5_DIGEST_LENGTH MD5_LENGTH ++ + #elif defined(USE_WINDOWS_SSPI) + + #include "curl_sspi.h" +@@ -250,6 +259,11 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, + static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; + #endif + ++#ifdef USE_NSS ++ if(CURLE_OK != Curl_nss_force_init(conn->data)) ++ return CURLNTLM_BAD; ++#endif ++ + ntlm = proxy?&conn->proxyntlm:&conn->ntlm; + + /* skip initial whitespaces */ +@@ -351,16 +365,14 @@ static void setup_des_key(const unsigned char *key_56, + DES_set_odd_parity(&key); + DES_set_key(&key, ks); + } +-#elif defined(USE_GNUTLS) ++ ++#else /* defined(USE_SSLEAY) */ + + /* +- * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. ++ * Turns a 56 bit key into the 64 bit, odd parity key. Used by GnuTLS and NSS. + */ +-static void setup_des_key(const unsigned char *key_56, +- gcry_cipher_hd_t *des) ++static void extend_key_56_to_64(const unsigned char *key_56, char *key) + { +- 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)); +@@ -369,10 +381,84 @@ static void setup_des_key(const unsigned char *key_56, + 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); ++} ++ ++#if 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]; ++ extend_key_56_to_64(key_56, key); + gcry_cipher_setkey(*des, key, 8); + } +-#endif ++ ++#elif defined(USE_NSS) ++ ++/* ++ * Expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of data, using ++ * the expanded key. The caller is responsible for giving 64 bit of valid ++ * data is IN and (at least) 64 bit large buffer as OUT. ++ */ ++static bool encrypt_des(const unsigned char *in, unsigned char *out, ++ const unsigned char *key_56) ++{ ++ const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */ ++ PK11SlotInfo *slot = NULL; ++ char key[8]; /* expanded 64 bit key */ ++ SECItem key_item; ++ PK11SymKey *symkey = NULL; ++ SECItem *param = NULL; ++ PK11Context *ctx = NULL; ++ int out_len; /* not used, required by NSS */ ++ bool rv = FALSE; ++ ++ /* use internal slot for DES encryption (requires NSS to be initialized) */ ++ slot = PK11_GetInternalKeySlot(); ++ if(!slot) ++ return FALSE; ++ ++ /* expand the 56 bit key to 64 bit and wrap by NSS */ ++ extend_key_56_to_64(key_56, key); ++ key_item.data = (unsigned char *)key; ++ key_item.len = /* hard-wired */ 8; ++ symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT, ++ &key_item, NULL); ++ if(!symkey) ++ goto fail; ++ ++ /* create DES encryption context */ ++ param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL); ++ if(!param) ++ goto fail; ++ ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param); ++ if(!ctx) ++ goto fail; ++ ++ /* perform the encryption */ ++ if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8, ++ (unsigned char *)in, /* inbuflen */ 8) ++ && SECSuccess == PK11_Finalize(ctx)) ++ rv = /* all OK */ TRUE; ++ ++fail: ++ /* cleanup */ ++ if(ctx) ++ PK11_DestroyContext(ctx, PR_TRUE); ++ if(symkey) ++ PK11_FreeSymKey(symkey); ++ if(param) ++ SECITEM_FreeItem(param, PR_TRUE); ++ PK11_FreeSlot(slot); ++ return rv; ++} ++ ++#endif /* defined(USE_NSS) */ ++ ++#endif /* defined(USE_SSLEAY) */ + + /* + * takes a 21 byte array and treats it as 3 56-bit DES keys. The +@@ -414,6 +500,10 @@ static void lm_resp(const unsigned char *keys, + setup_des_key(keys+14, &des); + gcry_cipher_encrypt(des, results+16, 8, plaintext, 8); + gcry_cipher_close(des); ++#elif defined(USE_NSS) ++ encrypt_des(plaintext, results, keys); ++ encrypt_des(plaintext, results+8, keys+7); ++ encrypt_des(plaintext, results+16, keys+14); + #endif + } + +@@ -470,11 +560,14 @@ static void mk_lm_hash(struct SessionHandle *data, + setup_des_key(pw+7, &des); + gcry_cipher_encrypt(des, lmbuffer+8, 8, magic, 8); + gcry_cipher_close(des); ++#elif defined(USE_NSS) ++ encrypt_des(magic, lmbuffer, pw); ++ encrypt_des(magic, lmbuffer+8, pw+7); + #endif + + memset(lmbuffer + 16, 0, 21 - 16); + } +- } ++} + + #if USE_NTRESPONSES + static void ascii_to_unicode_le(unsigned char *dest, const char *src, +@@ -525,6 +618,8 @@ static CURLcode mk_nt_hash(struct SessionHandle *data, + gcry_md_write(MD4pw, pw, 2*len); + memcpy (ntbuffer, gcry_md_read (MD4pw, 0), MD4_DIGEST_LENGTH); + gcry_md_close(MD4pw); ++#elif defined(USE_NSS) ++ Curl_md4it(ntbuffer, pw, 2*len); + #endif + + memset(ntbuffer + 16, 0, 21 - 16); +@@ -599,6 +694,11 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, + DEBUGASSERT(conn); + DEBUGASSERT(conn->data); + ++#ifdef USE_NSS ++ if(CURLE_OK != Curl_nss_force_init(conn->data)) ++ return CURLE_OUT_OF_MEMORY; ++#endif ++ + if(proxy) { + allocuserpwd = &conn->allocptr.proxyuserpwd; + userp = conn->proxyuser; +@@ -926,6 +1026,11 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, + gcry_md_hd_t MD5pw; + Curl_gtls_seed(conn->data); /* Initiate the seed if not already done */ + gcry_randomize(entropy, 8, GCRY_STRONG_RANDOM); ++#elif defined(USE_NSS) ++ PK11Context *MD5pw; ++ unsigned int outlen; ++ Curl_nss_seed(conn->data); /* Initiate the seed if not already done */ ++ PK11_GenerateRandom(entropy, 8); + #endif + + /* 8 bytes random data as challenge in lmresp */ +@@ -946,6 +1051,11 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, + gcry_md_write(MD5pw, tmp, MD5_DIGEST_LENGTH); + memcpy(md5sum, gcry_md_read (MD5pw, 0), MD5_DIGEST_LENGTH); + gcry_md_close(MD5pw); ++#elif defined(USE_NSS) ++ MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); ++ PK11_DigestOp(MD5pw, tmp, 16); ++ PK11_DigestFinal(MD5pw, md5sum, &outlen, MD5_DIGEST_LENGTH); ++ PK11_DestroyContext(MD5pw, PR_TRUE); + #endif + + /* We shall only use the first 8 bytes of md5sum, +diff --git a/lib/md4.c b/lib/md4.c +new file mode 100644 +index 0000000..7000083 +--- /dev/null ++++ b/lib/md4.c +@@ -0,0 +1,281 @@ ++/*- ++ Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. ++ ++ License to copy and use this software is granted provided that it ++ is identified as the "RSA Data Security, Inc. MD4 Message-Digest ++ Algorithm" in all material mentioning or referencing this software ++ or this function. ++ ++ License is also granted to make and use derivative works provided ++ that such works are identified as "derived from the RSA Data ++ Security, Inc. MD4 Message-Digest Algorithm" in all material ++ mentioning or referencing the derived work. ++ ++ RSA Data Security, Inc. makes no representations concerning either ++ the merchantability of this software or the suitability of this ++ software for any particular purpose. It is provided "as is" ++ without express or implied warranty of any kind. ++ ++ These notices must be retained in any copies of any part of this ++ documentation and/or software. ++ */ ++ ++#include "setup.h" ++ ++/* NSS crypto library does not provide the MD4 hash algorithm, so that we have ++ * a local implementation of it */ ++#ifdef USE_NSS ++ ++#include "curl_md4.h" ++#include ++ ++typedef unsigned int UINT4; ++ ++typedef struct MD4Context { ++ UINT4 state[4]; /* state (ABCD) */ ++ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ ++ unsigned char buffer[64]; /* input buffer */ ++} MD4_CTX; ++ ++/* Constants for MD4Transform routine. ++ */ ++#define S11 3 ++#define S12 7 ++#define S13 11 ++#define S14 19 ++#define S21 3 ++#define S22 5 ++#define S23 9 ++#define S24 13 ++#define S31 3 ++#define S32 9 ++#define S33 11 ++#define S34 15 ++ ++static void MD4Transform(UINT4 [4], const unsigned char [64]); ++static void Encode(unsigned char *, UINT4 *, unsigned int); ++static void Decode(UINT4 *, const unsigned char *, unsigned int); ++ ++static unsigned char PADDING[64] = { ++ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* F, G and H are basic MD4 functions. ++ */ ++#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) ++#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) ++#define H(x, y, z) ((x) ^ (y) ^ (z)) ++ ++/* ROTATE_LEFT rotates x left n bits. ++ */ ++#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) ++ ++/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ ++/* Rotation is separate from addition to prevent recomputation */ ++#define FF(a, b, c, d, x, s) { \ ++ (a) += F ((b), (c), (d)) + (x); \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ } ++#define GG(a, b, c, d, x, s) { \ ++ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ } ++#define HH(a, b, c, d, x, s) { \ ++ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ } ++ ++/* MD4 initialization. Begins an MD4 operation, writing a new context. ++ */ ++static void MD4Init(MD4_CTX *context) ++{ ++ context->count[0] = context->count[1] = 0; ++ ++ /* Load magic initialization constants. ++ */ ++ context->state[0] = 0x67452301; ++ context->state[1] = 0xefcdab89; ++ context->state[2] = 0x98badcfe; ++ context->state[3] = 0x10325476; ++} ++ ++/* MD4 block update operation. Continues an MD4 message-digest ++ operation, processing another message block, and updating the ++ context. ++ */ ++static void MD4Update(MD4_CTX *context, const unsigned char *input, ++ unsigned int inputLen) ++{ ++ unsigned int i, bufindex, partLen; ++ ++ /* Compute number of bytes mod 64 */ ++ bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F); ++ /* Update number of bits */ ++ if ((context->count[0] += ((UINT4)inputLen << 3)) ++ < ((UINT4)inputLen << 3)) ++ context->count[1]++; ++ context->count[1] += ((UINT4)inputLen >> 29); ++ ++ partLen = 64 - bufindex; ++ /* Transform as many times as possible. ++ */ ++ if (inputLen >= partLen) { ++ bcopy(input, &context->buffer[bufindex], partLen); ++ MD4Transform (context->state, context->buffer); ++ ++ for (i = partLen; i + 63 < inputLen; i += 64) ++ MD4Transform (context->state, &input[i]); ++ ++ bufindex = 0; ++ } ++ else ++ i = 0; ++ ++ /* Buffer remaining input */ ++ bcopy(&input[i], &context->buffer[bufindex], inputLen-i); ++} ++ ++/* MD4 padding. */ ++static void MD4Pad(MD4_CTX *context) ++{ ++ unsigned char bits[8]; ++ unsigned int bufindex, padLen; ++ ++ /* Save number of bits */ ++ Encode (bits, context->count, 8); ++ ++ /* Pad out to 56 mod 64. ++ */ ++ bufindex = (unsigned int)((context->count[0] >> 3) & 0x3f); ++ padLen = (bufindex < 56) ? (56 - bufindex) : (120 - bufindex); ++ MD4Update (context, PADDING, padLen); ++ ++ /* Append length (before padding) */ ++ MD4Update (context, bits, 8); ++} ++ ++/* MD4 finalization. Ends an MD4 message-digest operation, writing the ++ the message digest and zeroizing the context. ++ */ ++static void MD4Final (unsigned char digest[16], MD4_CTX *context) ++{ ++ /* Do padding */ ++ MD4Pad (context); ++ ++ /* Store state in digest */ ++ Encode (digest, context->state, 16); ++ ++ /* Zeroize sensitive information. ++ */ ++ memset(context, 0, sizeof(*context)); ++} ++ ++/* MD4 basic transformation. Transforms state based on block. ++ */ ++static void MD4Transform (UINT4 state[4], const unsigned char block[64]) ++{ ++ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; ++ ++ Decode (x, block, 64); ++ ++ /* Round 1 */ ++ FF (a, b, c, d, x[ 0], S11); /* 1 */ ++ FF (d, a, b, c, x[ 1], S12); /* 2 */ ++ FF (c, d, a, b, x[ 2], S13); /* 3 */ ++ FF (b, c, d, a, x[ 3], S14); /* 4 */ ++ FF (a, b, c, d, x[ 4], S11); /* 5 */ ++ FF (d, a, b, c, x[ 5], S12); /* 6 */ ++ FF (c, d, a, b, x[ 6], S13); /* 7 */ ++ FF (b, c, d, a, x[ 7], S14); /* 8 */ ++ FF (a, b, c, d, x[ 8], S11); /* 9 */ ++ FF (d, a, b, c, x[ 9], S12); /* 10 */ ++ FF (c, d, a, b, x[10], S13); /* 11 */ ++ FF (b, c, d, a, x[11], S14); /* 12 */ ++ FF (a, b, c, d, x[12], S11); /* 13 */ ++ FF (d, a, b, c, x[13], S12); /* 14 */ ++ FF (c, d, a, b, x[14], S13); /* 15 */ ++ FF (b, c, d, a, x[15], S14); /* 16 */ ++ ++ /* Round 2 */ ++ GG (a, b, c, d, x[ 0], S21); /* 17 */ ++ GG (d, a, b, c, x[ 4], S22); /* 18 */ ++ GG (c, d, a, b, x[ 8], S23); /* 19 */ ++ GG (b, c, d, a, x[12], S24); /* 20 */ ++ GG (a, b, c, d, x[ 1], S21); /* 21 */ ++ GG (d, a, b, c, x[ 5], S22); /* 22 */ ++ GG (c, d, a, b, x[ 9], S23); /* 23 */ ++ GG (b, c, d, a, x[13], S24); /* 24 */ ++ GG (a, b, c, d, x[ 2], S21); /* 25 */ ++ GG (d, a, b, c, x[ 6], S22); /* 26 */ ++ GG (c, d, a, b, x[10], S23); /* 27 */ ++ GG (b, c, d, a, x[14], S24); /* 28 */ ++ GG (a, b, c, d, x[ 3], S21); /* 29 */ ++ GG (d, a, b, c, x[ 7], S22); /* 30 */ ++ GG (c, d, a, b, x[11], S23); /* 31 */ ++ GG (b, c, d, a, x[15], S24); /* 32 */ ++ ++ /* Round 3 */ ++ HH (a, b, c, d, x[ 0], S31); /* 33 */ ++ HH (d, a, b, c, x[ 8], S32); /* 34 */ ++ HH (c, d, a, b, x[ 4], S33); /* 35 */ ++ HH (b, c, d, a, x[12], S34); /* 36 */ ++ HH (a, b, c, d, x[ 2], S31); /* 37 */ ++ HH (d, a, b, c, x[10], S32); /* 38 */ ++ HH (c, d, a, b, x[ 6], S33); /* 39 */ ++ HH (b, c, d, a, x[14], S34); /* 40 */ ++ HH (a, b, c, d, x[ 1], S31); /* 41 */ ++ HH (d, a, b, c, x[ 9], S32); /* 42 */ ++ HH (c, d, a, b, x[ 5], S33); /* 43 */ ++ HH (b, c, d, a, x[13], S34); /* 44 */ ++ HH (a, b, c, d, x[ 3], S31); /* 45 */ ++ HH (d, a, b, c, x[11], S32); /* 46 */ ++ HH (c, d, a, b, x[ 7], S33); /* 47 */ ++ HH (b, c, d, a, x[15], S34); /* 48 */ ++ ++ state[0] += a; ++ state[1] += b; ++ state[2] += c; ++ state[3] += d; ++ ++ /* Zeroize sensitive information. ++ */ ++ memset(x, 0, sizeof(x)); ++} ++ ++/* Encodes input (UINT4) into output (unsigned char). Assumes len is ++ a multiple of 4. ++ */ ++static void Encode(unsigned char *output, UINT4 *input, unsigned int len) ++{ ++ unsigned int i, j; ++ ++ for (i = 0, j = 0; j < len; i++, j += 4) { ++ output[j] = (unsigned char)(input[i] & 0xff); ++ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); ++ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); ++ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); ++ } ++} ++ ++/* Decodes input (unsigned char) into output (UINT4). Assumes len is ++ a multiple of 4. ++ */ ++static void Decode (UINT4 *output, const unsigned char *input, unsigned int len) ++{ ++ unsigned int i, j; ++ ++ for (i = 0, j = 0; j < len; i++, j += 4) ++ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | ++ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); ++} ++ ++void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len) ++{ ++ MD4_CTX ctx; ++ MD4Init(&ctx); ++ MD4Update(&ctx, input, (unsigned int)len); ++ MD4Final(output, &ctx); ++} ++#endif /* USE_NSS */ +diff --git a/lib/nss.c b/lib/nss.c +index 4058d78..6d3f12c 100644 +--- a/lib/nss.c ++++ b/lib/nss.c +@@ -891,6 +891,57 @@ isTLSIntoleranceError(PRInt32 err) + } + } + ++static CURLcode init_nss(struct SessionHandle *data) ++{ ++ char *cert_dir; ++ struct_stat st; ++ if(initialized) ++ return CURLE_OK; ++ ++ /* First we check if $SSL_DIR points to a valid dir */ ++ cert_dir = getenv("SSL_DIR"); ++ if(cert_dir) { ++ if((stat(cert_dir, &st) != 0) || ++ (!S_ISDIR(st.st_mode))) { ++ cert_dir = NULL; ++ } ++ } ++ ++ /* Now we check if the default location is a valid dir */ ++ if(!cert_dir) { ++ if((stat(SSL_DIR, &st) == 0) && ++ (S_ISDIR(st.st_mode))) { ++ cert_dir = (char *)SSL_DIR; ++ } ++ } ++ ++ if(!NSS_IsInitialized()) { ++ SECStatus rv; ++ initialized = 1; ++ infof(data, "Initializing NSS with certpath: %s\n", ++ cert_dir ? cert_dir : "none"); ++ if(!cert_dir) { ++ rv = NSS_NoDB_Init(NULL); ++ } ++ else { ++ char *certpath = ++ PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", cert_dir); ++ rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); ++ PR_smprintf_free(certpath); ++ } ++ if(rv != SECSuccess) { ++ infof(data, "Unable to initialize NSS database\n"); ++ initialized = 0; ++ return CURLE_SSL_CACERT_BADFILE; ++ } ++ } ++ ++ if(num_enabled_ciphers() == 0) ++ NSS_SetDomesticPolicy(); ++ ++ return CURLE_OK; ++} ++ + /** + * Global SSL init + * +@@ -911,6 +962,21 @@ int Curl_nss_init(void) + return 1; + } + ++CURLcode Curl_nss_force_init(struct SessionHandle *data) ++{ ++ CURLcode rv; ++ if(!nss_initlock) { ++ failf(data, "unable to initialize NSS, curl_global_init() should have been " ++ "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); ++ return CURLE_OUT_OF_MEMORY; ++ } ++ ++ PR_Lock(nss_initlock); ++ rv = init_nss(data); ++ PR_Unlock(nss_initlock); ++ return rv; ++} ++ + /* Global cleanup */ + void Curl_nss_cleanup(void) + { +@@ -1039,16 +1105,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) + struct SessionHandle *data = conn->data; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +- SECStatus rv; +- char *certDir = NULL; + int curlerr; + const int *cipher_to_enable; + PRSocketOptionData sock_opt; + long time_left; + PRUint32 timeout; + +- curlerr = CURLE_SSL_CONNECT_ERROR; +- + if (connssl->state == ssl_connection_complete) + return CURLE_OK; + +@@ -1062,76 +1124,36 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) + + /* FIXME. NSS doesn't support multiple databases open at the same time. */ + PR_Lock(nss_initlock); +- if(!initialized) { +- struct_stat st; +- +- /* First we check if $SSL_DIR points to a valid dir */ +- certDir = getenv("SSL_DIR"); +- if(certDir) { +- if((stat(certDir, &st) != 0) || +- (!S_ISDIR(st.st_mode))) { +- certDir = NULL; +- } +- } +- +- /* Now we check if the default location is a valid dir */ +- if(!certDir) { +- if((stat(SSL_DIR, &st) == 0) && +- (S_ISDIR(st.st_mode))) { +- certDir = (char *)SSL_DIR; +- } +- } +- +- if (!NSS_IsInitialized()) { +- initialized = 1; +- infof(conn->data, "Initializing NSS with certpath: %s\n", +- certDir ? certDir : "none"); +- if(!certDir) { +- rv = NSS_NoDB_Init(NULL); +- } +- else { +- char *certpath = PR_smprintf("%s%s", +- NSS_VersionCheck("3.12.0") ? "sql:" : "", +- certDir); +- rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); +- PR_smprintf_free(certpath); +- } +- if(rv != SECSuccess) { +- infof(conn->data, "Unable to initialize NSS database\n"); +- curlerr = CURLE_SSL_CACERT_BADFILE; +- initialized = 0; +- PR_Unlock(nss_initlock); +- goto error; +- } +- } ++ curlerr = init_nss(conn->data); ++ if(CURLE_OK != curlerr) { ++ PR_Unlock(nss_initlock); ++ goto error; ++ } + +- if(num_enabled_ciphers() == 0) +- NSS_SetDomesticPolicy(); ++ curlerr = CURLE_SSL_CONNECT_ERROR; + + #ifdef HAVE_PK11_CREATEGENERICOBJECT +- if(!mod) { +- char *configstring = aprintf("library=%s name=PEM", pem_library); +- if(!configstring) { +- PR_Unlock(nss_initlock); +- goto error; +- } +- mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); +- free(configstring); ++ if(!mod) { ++ char *configstring = aprintf("library=%s name=PEM", pem_library); ++ if(!configstring) { ++ PR_Unlock(nss_initlock); ++ goto error; ++ } ++ mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); ++ free(configstring); + +- if(!mod || !mod->loaded) { +- if(mod) { +- SECMOD_DestroyModule(mod); +- mod = NULL; +- } +- infof(data, "WARNING: failed to load NSS PEM library %s. Using " +- "OpenSSL PEM certificates will not work.\n", pem_library); ++ if(!mod || !mod->loaded) { ++ if(mod) { ++ SECMOD_DestroyModule(mod); ++ mod = NULL; + } ++ infof(data, "WARNING: failed to load NSS PEM library %s. Using " ++ "OpenSSL PEM certificates will not work.\n", pem_library); + } ++ } + #endif + +- PK11_SetPasswordFunc(nss_get_password); +- +- } ++ PK11_SetPasswordFunc(nss_get_password); + PR_Unlock(nss_initlock); + + model = PR_NewTCPSocket(); +@@ -1448,4 +1470,12 @@ size_t Curl_nss_version(char *buffer, size_t size) + { + return snprintf(buffer, size, "NSS/%s", NSS_VERSION); + } ++ ++int Curl_nss_seed(struct SessionHandle *data) ++{ ++ /* TODO: implement? */ ++ (void) data; ++ return 0; ++} ++ + #endif /* USE_NSS */ +diff --git a/lib/nssg.h b/lib/nssg.h +index 6eaa178..f9cc46a 100644 +--- a/lib/nssg.h ++++ b/lib/nssg.h +@@ -46,6 +46,9 @@ size_t Curl_nss_version(char *buffer, size_t size); + int Curl_nss_check_cxn(struct connectdata *cxn); + int Curl_nss_seed(struct SessionHandle *data); + ++/* initialize NSS library if not already */ ++CURLcode Curl_nss_force_init(struct SessionHandle *data); ++ + /* API setup for NSS */ + #define curlssl_init Curl_nss_init + #define curlssl_cleanup Curl_nss_cleanup +diff --git a/lib/setup.h b/lib/setup.h +index 43f98a8..90bb898 100644 +--- a/lib/setup.h ++++ b/lib/setup.h +@@ -530,7 +530,7 @@ int netware_init(void); + #endif + + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) +-#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || defined(USE_GNUTLS) ++#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || defined(USE_GNUTLS) || defined(USE_NSS) + #define USE_NTLM + #endif + #endif diff --git a/curl.spec b/curl.spec index 5fd8561..576eb71 100644 --- a/curl.spec +++ b/curl.spec @@ -1,13 +1,16 @@ Summary: A utility for getting files from remote servers (FTP, HTTP, and others) Name: curl Version: 7.21.0 -Release: 1%{?dist} +Release: 2%{?dist} License: MIT Group: Applications/Internet Source: http://curl.haxx.se/download/%{name}-%{version}.tar.lzma Source2: curlbuild.h Source3: hide_selinux.c +# patch adding support for NTLM authentication (#603783) +Patch1: 0001-curl-7.21.0-ntlm.patch + # patch making libcurl multilib ready Patch101: 0101-curl-7.20.0-multilib.patch @@ -94,6 +97,9 @@ for f in CHANGES README; do mv -f ${f}.utf8 ${f} done +# upstream patches (not yet applied) +%patch1 -p1 + # Fedora patches %patch101 -p1 %patch102 -p1 @@ -207,6 +213,9 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/aclocal/libcurl.m4 %changelog +* Mon Jun 28 2010 Kamil Dudka 7.21.0-2 +- add support for NTLM authentication (#603783) + * Wed Jun 16 2010 Kamil Dudka 7.21.0-1 - new upstream release, drop applied patches - update of %%description