diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 05e02c9a8..baf04963e 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -10,6 +10,7 @@ Curl and libcurl 7.21.1 This release includes the following changes: o maketgz: produce CHANGES automatically + o added support for NTLM authentication when compiled with NSS This release includes the following bugfixes: diff --git a/configure.ac b/configure.ac index af30b8a69..18a364234 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 63117803d..a502e8f73 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 000000000..6b6c16e13 --- /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 36e538ea0..0e831ca33 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 000000000..700008375 --- /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 4058d7867..6d3f12c03 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; + curlerr = init_nss(conn->data); + if(CURLE_OK != curlerr) { + PR_Unlock(nss_initlock); + goto error; + } - /* 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; - } - } - - 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 || !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) { + 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); + } + } #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 6eaa17858..f9cc46ae2 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 43f98a868..90bb898a3 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