Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/vtls/nss.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 9 * | |
| 10 * This software is licensed as described in the file COPYING, which | |
| 11 * you should have received as part of this distribution. The terms | |
| 12 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 13 * | |
| 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 15 * copies of the Software, and permit persons to whom the Software is | |
| 16 * furnished to do so, under the terms of the COPYING file. | |
| 17 * | |
| 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 19 * KIND, either express or implied. | |
| 20 * | |
| 21 ***************************************************************************/ | |
| 22 | |
| 23 /* | |
| 24 * Source file for all NSS-specific code for the TLS/SSL layer. No code | |
| 25 * but vtls.c should ever call or use these functions. | |
| 26 */ | |
| 27 | |
| 28 #include "curl_setup.h" | |
| 29 | |
| 30 #ifdef USE_NSS | |
| 31 | |
| 32 #include "urldata.h" | |
| 33 #include "sendf.h" | |
| 34 #include "formdata.h" /* for the boundary function */ | |
| 35 #include "url.h" /* for the ssl config check function */ | |
| 36 #include "connect.h" | |
| 37 #include "strcase.h" | |
| 38 #include "select.h" | |
| 39 #include "vtls.h" | |
| 40 #include "llist.h" | |
| 41 #include "multiif.h" | |
| 42 #include "curl_printf.h" | |
| 43 #include "nssg.h" | |
| 44 #include <nspr.h> | |
| 45 #include <nss.h> | |
| 46 #include <ssl.h> | |
| 47 #include <sslerr.h> | |
| 48 #include <secerr.h> | |
| 49 #include <secmod.h> | |
| 50 #include <sslproto.h> | |
| 51 #include <prtypes.h> | |
| 52 #include <pk11pub.h> | |
| 53 #include <prio.h> | |
| 54 #include <secitem.h> | |
| 55 #include <secport.h> | |
| 56 #include <certdb.h> | |
| 57 #include <base64.h> | |
| 58 #include <cert.h> | |
| 59 #include <prerror.h> | |
| 60 #include <keyhi.h> /* for SECKEY_DestroyPublicKey() */ | |
| 61 #include <private/pprio.h> /* for PR_ImportTCPSocket */ | |
| 62 | |
| 63 #define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) | |
| 64 | |
| 65 #if NSSVERNUM >= 0x030f00 /* 3.15.0 */ | |
| 66 #include <ocsp.h> | |
| 67 #endif | |
| 68 | |
| 69 #include "strcase.h" | |
| 70 #include "warnless.h" | |
| 71 #include "x509asn1.h" | |
| 72 | |
| 73 /* The last #include files should be: */ | |
| 74 #include "curl_memory.h" | |
| 75 #include "memdebug.h" | |
| 76 | |
| 77 #define SSL_DIR "/etc/pki/nssdb" | |
| 78 | |
| 79 /* enough to fit the string "PEM Token #[0|1]" */ | |
| 80 #define SLOTSIZE 13 | |
| 81 | |
| 82 struct ssl_backend_data { | |
| 83 PRFileDesc *handle; | |
| 84 char *client_nickname; | |
| 85 struct Curl_easy *data; | |
| 86 struct curl_llist obj_list; | |
| 87 PK11GenericObject *obj_clicert; | |
| 88 }; | |
| 89 | |
| 90 #define BACKEND connssl->backend | |
| 91 | |
| 92 static PRLock *nss_initlock = NULL; | |
| 93 static PRLock *nss_crllock = NULL; | |
| 94 static PRLock *nss_findslot_lock = NULL; | |
| 95 static PRLock *nss_trustload_lock = NULL; | |
| 96 static struct curl_llist nss_crl_list; | |
| 97 static NSSInitContext *nss_context = NULL; | |
| 98 static volatile int initialized = 0; | |
| 99 | |
| 100 /* type used to wrap pointers as list nodes */ | |
| 101 struct ptr_list_wrap { | |
| 102 void *ptr; | |
| 103 struct curl_llist_element node; | |
| 104 }; | |
| 105 | |
| 106 typedef struct { | |
| 107 const char *name; | |
| 108 int num; | |
| 109 } cipher_s; | |
| 110 | |
| 111 #define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ | |
| 112 CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ | |
| 113 ptr->type = (_type); \ | |
| 114 ptr->pValue = (_val); \ | |
| 115 ptr->ulValueLen = (_len); \ | |
| 116 } WHILE_FALSE | |
| 117 | |
| 118 #define CERT_NewTempCertificate __CERT_NewTempCertificate | |
| 119 | |
| 120 #define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) | |
| 121 static const cipher_s cipherlist[] = { | |
| 122 /* SSL2 cipher suites */ | |
| 123 {"rc4", SSL_EN_RC4_128_WITH_MD5}, | |
| 124 {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, | |
| 125 {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, | |
| 126 {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, | |
| 127 {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, | |
| 128 {"des", SSL_EN_DES_64_CBC_WITH_MD5}, | |
| 129 {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, | |
| 130 /* SSL3/TLS cipher suites */ | |
| 131 {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, | |
| 132 {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, | |
| 133 {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, | |
| 134 {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, | |
| 135 {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, | |
| 136 {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, | |
| 137 {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, | |
| 138 {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, | |
| 139 {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, | |
| 140 {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, | |
| 141 {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, | |
| 142 {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, | |
| 143 {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, | |
| 144 /* TLS 1.0: Exportable 56-bit Cipher Suites. */ | |
| 145 {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, | |
| 146 {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, | |
| 147 /* AES ciphers. */ | |
| 148 {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA}, | |
| 149 {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA}, | |
| 150 {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, | |
| 151 {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, | |
| 152 {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, | |
| 153 {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, | |
| 154 /* ECC ciphers. */ | |
| 155 {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, | |
| 156 {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, | |
| 157 {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, | |
| 158 {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, | |
| 159 {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, | |
| 160 {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, | |
| 161 {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, | |
| 162 {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, | |
| 163 {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, | |
| 164 {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, | |
| 165 {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, | |
| 166 {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, | |
| 167 {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, | |
| 168 {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, | |
| 169 {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, | |
| 170 {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, | |
| 171 {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, | |
| 172 {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, | |
| 173 {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, | |
| 174 {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, | |
| 175 {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, | |
| 176 {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, | |
| 177 {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, | |
| 178 {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, | |
| 179 {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, | |
| 180 #ifdef TLS_RSA_WITH_NULL_SHA256 | |
| 181 /* new HMAC-SHA256 cipher suites specified in RFC */ | |
| 182 {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256}, | |
| 183 {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256}, | |
| 184 {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256}, | |
| 185 {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, | |
| 186 {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, | |
| 187 {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, | |
| 188 {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, | |
| 189 #endif | |
| 190 #ifdef TLS_RSA_WITH_AES_128_GCM_SHA256 | |
| 191 /* AES GCM cipher suites in RFC 5288 and RFC 5289 */ | |
| 192 {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256}, | |
| 193 {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| 194 {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256}, | |
| 195 {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, | |
| 196 {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256}, | |
| 197 {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| 198 {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256}, | |
| 199 #endif | |
| 200 #ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 | |
| 201 /* cipher suites using SHA384 */ | |
| 202 {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384}, | |
| 203 {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, | |
| 204 {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384}, | |
| 205 {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, | |
| 206 {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, | |
| 207 {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, | |
| 208 {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, | |
| 209 #endif | |
| 210 #ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 | |
| 211 /* chacha20-poly1305 cipher suites */ | |
| 212 {"ecdhe_rsa_chacha20_poly1305_sha_256", | |
| 213 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| 214 {"ecdhe_ecdsa_chacha20_poly1305_sha_256", | |
| 215 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| 216 {"dhe_rsa_chacha20_poly1305_sha_256", | |
| 217 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| 218 #endif | |
| 219 #ifdef TLS_AES_256_GCM_SHA384 | |
| 220 {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, | |
| 221 {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, | |
| 222 {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, | |
| 223 #endif | |
| 224 }; | |
| 225 | |
| 226 #if defined(WIN32) | |
| 227 static const char *pem_library = "nsspem.dll"; | |
| 228 static const char *trust_library = "nssckbi.dll"; | |
| 229 #elif defined(__APPLE__) | |
| 230 static const char *pem_library = "libnsspem.dylib"; | |
| 231 static const char *trust_library = "libnssckbi.dylib"; | |
| 232 #else | |
| 233 static const char *pem_library = "libnsspem.so"; | |
| 234 static const char *trust_library = "libnssckbi.so"; | |
| 235 #endif | |
| 236 | |
| 237 static SECMODModule *pem_module = NULL; | |
| 238 static SECMODModule *trust_module = NULL; | |
| 239 | |
| 240 /* NSPR I/O layer we use to detect blocking direction during SSL handshake */ | |
| 241 static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; | |
| 242 static PRIOMethods nspr_io_methods; | |
| 243 | |
| 244 static const char *nss_error_to_name(PRErrorCode code) | |
| 245 { | |
| 246 const char *name = PR_ErrorToName(code); | |
| 247 if(name) | |
| 248 return name; | |
| 249 | |
| 250 return "unknown error"; | |
| 251 } | |
| 252 | |
| 253 static void nss_print_error_message(struct Curl_easy *data, PRUint32 err) | |
| 254 { | |
| 255 failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); | |
| 256 } | |
| 257 | |
| 258 static char *nss_sslver_to_name(PRUint16 nssver) | |
| 259 { | |
| 260 switch(nssver) { | |
| 261 case SSL_LIBRARY_VERSION_2: | |
| 262 return strdup("SSLv2"); | |
| 263 case SSL_LIBRARY_VERSION_3_0: | |
| 264 return strdup("SSLv3"); | |
| 265 case SSL_LIBRARY_VERSION_TLS_1_0: | |
| 266 return strdup("TLSv1.0"); | |
| 267 #ifdef SSL_LIBRARY_VERSION_TLS_1_1 | |
| 268 case SSL_LIBRARY_VERSION_TLS_1_1: | |
| 269 return strdup("TLSv1.1"); | |
| 270 #endif | |
| 271 #ifdef SSL_LIBRARY_VERSION_TLS_1_2 | |
| 272 case SSL_LIBRARY_VERSION_TLS_1_2: | |
| 273 return strdup("TLSv1.2"); | |
| 274 #endif | |
| 275 #ifdef SSL_LIBRARY_VERSION_TLS_1_3 | |
| 276 case SSL_LIBRARY_VERSION_TLS_1_3: | |
| 277 return strdup("TLSv1.3"); | |
| 278 #endif | |
| 279 default: | |
| 280 return curl_maprintf("0x%04x", nssver); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model, | |
| 285 char *cipher_list) | |
| 286 { | |
| 287 unsigned int i; | |
| 288 PRBool cipher_state[NUM_OF_CIPHERS]; | |
| 289 PRBool found; | |
| 290 char *cipher; | |
| 291 | |
| 292 /* use accessors to avoid dynamic linking issues after an update of NSS */ | |
| 293 const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); | |
| 294 const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers(); | |
| 295 if(!implemented_ciphers) | |
| 296 return SECFailure; | |
| 297 | |
| 298 /* First disable all ciphers. This uses a different max value in case | |
| 299 * NSS adds more ciphers later we don't want them available by | |
| 300 * accident | |
| 301 */ | |
| 302 for(i = 0; i < num_implemented_ciphers; i++) { | |
| 303 SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); | |
| 304 } | |
| 305 | |
| 306 /* Set every entry in our list to false */ | |
| 307 for(i = 0; i < NUM_OF_CIPHERS; i++) { | |
| 308 cipher_state[i] = PR_FALSE; | |
| 309 } | |
| 310 | |
| 311 cipher = cipher_list; | |
| 312 | |
| 313 while(cipher_list && (cipher_list[0])) { | |
| 314 while((*cipher) && (ISSPACE(*cipher))) | |
| 315 ++cipher; | |
| 316 | |
| 317 cipher_list = strchr(cipher, ','); | |
| 318 if(cipher_list) { | |
| 319 *cipher_list++ = '\0'; | |
| 320 } | |
| 321 | |
| 322 found = PR_FALSE; | |
| 323 | |
| 324 for(i = 0; i<NUM_OF_CIPHERS; i++) { | |
| 325 if(strcasecompare(cipher, cipherlist[i].name)) { | |
| 326 cipher_state[i] = PR_TRUE; | |
| 327 found = PR_TRUE; | |
| 328 break; | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 if(found == PR_FALSE) { | |
| 333 failf(data, "Unknown cipher in list: %s", cipher); | |
| 334 return SECFailure; | |
| 335 } | |
| 336 | |
| 337 if(cipher_list) { | |
| 338 cipher = cipher_list; | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 /* Finally actually enable the selected ciphers */ | |
| 343 for(i = 0; i<NUM_OF_CIPHERS; i++) { | |
| 344 if(!cipher_state[i]) | |
| 345 continue; | |
| 346 | |
| 347 if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != SECSuccess) { | |
| 348 failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name); | |
| 349 return SECFailure; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 return SECSuccess; | |
| 354 } | |
| 355 | |
| 356 /* | |
| 357 * Return true if at least one cipher-suite is enabled. Used to determine | |
| 358 * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. | |
| 359 */ | |
| 360 static bool any_cipher_enabled(void) | |
| 361 { | |
| 362 unsigned int i; | |
| 363 | |
| 364 for(i = 0; i<NUM_OF_CIPHERS; i++) { | |
| 365 PRInt32 policy = 0; | |
| 366 SSL_CipherPolicyGet(cipherlist[i].num, &policy); | |
| 367 if(policy) | |
| 368 return TRUE; | |
| 369 } | |
| 370 | |
| 371 return FALSE; | |
| 372 } | |
| 373 | |
| 374 /* | |
| 375 * Determine whether the nickname passed in is a filename that needs to | |
| 376 * be loaded as a PEM or a regular NSS nickname. | |
| 377 * | |
| 378 * returns 1 for a file | |
| 379 * returns 0 for not a file (NSS nickname) | |
| 380 */ | |
| 381 static int is_file(const char *filename) | |
| 382 { | |
| 383 struct_stat st; | |
| 384 | |
| 385 if(filename == NULL) | |
| 386 return 0; | |
| 387 | |
| 388 if(stat(filename, &st) == 0) | |
| 389 if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode)) | |
| 390 return 1; | |
| 391 | |
| 392 return 0; | |
| 393 } | |
| 394 | |
| 395 /* Check if the given string is filename or nickname of a certificate. If the | |
| 396 * given string is recognized as filename, return NULL. If the given string is | |
| 397 * recognized as nickname, return a duplicated string. The returned string | |
| 398 * should be later deallocated using free(). If the OOM failure occurs, we | |
| 399 * return NULL, too. | |
| 400 */ | |
| 401 static char *dup_nickname(struct Curl_easy *data, const char *str) | |
| 402 { | |
| 403 const char *n; | |
| 404 | |
| 405 if(!is_file(str)) | |
| 406 /* no such file exists, use the string as nickname */ | |
| 407 return strdup(str); | |
| 408 | |
| 409 /* search the first slash; we require at least one slash in a file name */ | |
| 410 n = strchr(str, '/'); | |
| 411 if(!n) { | |
| 412 infof(data, "warning: certificate file name \"%s\" handled as nickname; " | |
| 413 "please use \"./%s\" to force file name\n", str, str); | |
| 414 return strdup(str); | |
| 415 } | |
| 416 | |
| 417 /* we'll use the PEM reader to read the certificate from file */ | |
| 418 return NULL; | |
| 419 } | |
| 420 | |
| 421 /* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition | |
| 422 * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN. For more | |
| 423 * details, go to <https://bugzilla.mozilla.org/1297397>. | |
| 424 */ | |
| 425 static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name) | |
| 426 { | |
| 427 PK11SlotInfo *slot; | |
| 428 PR_Lock(nss_findslot_lock); | |
| 429 slot = PK11_FindSlotByName(slot_name); | |
| 430 PR_Unlock(nss_findslot_lock); | |
| 431 return slot; | |
| 432 } | |
| 433 | |
| 434 /* wrap 'ptr' as list node and tail-insert into 'list' */ | |
| 435 static CURLcode insert_wrapped_ptr(struct curl_llist *list, void *ptr) | |
| 436 { | |
| 437 struct ptr_list_wrap *wrap = malloc(sizeof(*wrap)); | |
| 438 if(!wrap) | |
| 439 return CURLE_OUT_OF_MEMORY; | |
| 440 | |
| 441 wrap->ptr = ptr; | |
| 442 Curl_llist_insert_next(list, list->tail, wrap, &wrap->node); | |
| 443 return CURLE_OK; | |
| 444 } | |
| 445 | |
| 446 /* Call PK11_CreateGenericObject() with the given obj_class and filename. If | |
| 447 * the call succeeds, append the object handle to the list of objects so that | |
| 448 * the object can be destroyed in Curl_nss_close(). */ | |
| 449 static CURLcode nss_create_object(struct ssl_connect_data *connssl, | |
| 450 CK_OBJECT_CLASS obj_class, | |
| 451 const char *filename, bool cacert) | |
| 452 { | |
| 453 PK11SlotInfo *slot; | |
| 454 PK11GenericObject *obj; | |
| 455 CK_BBOOL cktrue = CK_TRUE; | |
| 456 CK_BBOOL ckfalse = CK_FALSE; | |
| 457 CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; | |
| 458 int attr_cnt = 0; | |
| 459 CURLcode result = (cacert) | |
| 460 ? CURLE_SSL_CACERT_BADFILE | |
| 461 : CURLE_SSL_CERTPROBLEM; | |
| 462 | |
| 463 const int slot_id = (cacert) ? 0 : 1; | |
| 464 char *slot_name = aprintf("PEM Token #%d", slot_id); | |
| 465 if(!slot_name) | |
| 466 return CURLE_OUT_OF_MEMORY; | |
| 467 | |
| 468 slot = nss_find_slot_by_name(slot_name); | |
| 469 free(slot_name); | |
| 470 if(!slot) | |
| 471 return result; | |
| 472 | |
| 473 PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); | |
| 474 PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); | |
| 475 PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, | |
| 476 (CK_ULONG)strlen(filename) + 1); | |
| 477 | |
| 478 if(CKO_CERTIFICATE == obj_class) { | |
| 479 CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); | |
| 480 PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); | |
| 481 } | |
| 482 | |
| 483 /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because | |
| 484 * PK11_DestroyGenericObject() does not release resources allocated by | |
| 485 * PK11_CreateGenericObject() early enough. */ | |
| 486 obj = | |
| 487 #ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT | |
| 488 PK11_CreateManagedGenericObject | |
| 489 #else | |
| 490 PK11_CreateGenericObject | |
| 491 #endif | |
| 492 (slot, attrs, attr_cnt, PR_FALSE); | |
| 493 | |
| 494 PK11_FreeSlot(slot); | |
| 495 if(!obj) | |
| 496 return result; | |
| 497 | |
| 498 if(insert_wrapped_ptr(&BACKEND->obj_list, obj) != CURLE_OK) { | |
| 499 PK11_DestroyGenericObject(obj); | |
| 500 return CURLE_OUT_OF_MEMORY; | |
| 501 } | |
| 502 | |
| 503 if(!cacert && CKO_CERTIFICATE == obj_class) | |
| 504 /* store reference to a client certificate */ | |
| 505 BACKEND->obj_clicert = obj; | |
| 506 | |
| 507 return CURLE_OK; | |
| 508 } | |
| 509 | |
| 510 /* Destroy the NSS object whose handle is given by ptr. This function is | |
| 511 * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy | |
| 512 * NSS objects in Curl_nss_close() */ | |
| 513 static void nss_destroy_object(void *user, void *ptr) | |
| 514 { | |
| 515 struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; | |
| 516 PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr; | |
| 517 (void) user; | |
| 518 PK11_DestroyGenericObject(obj); | |
| 519 free(wrap); | |
| 520 } | |
| 521 | |
| 522 /* same as nss_destroy_object() but for CRL items */ | |
| 523 static void nss_destroy_crl_item(void *user, void *ptr) | |
| 524 { | |
| 525 struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; | |
| 526 SECItem *crl_der = (SECItem *) wrap->ptr; | |
| 527 (void) user; | |
| 528 SECITEM_FreeItem(crl_der, PR_TRUE); | |
| 529 free(wrap); | |
| 530 } | |
| 531 | |
| 532 static CURLcode nss_load_cert(struct ssl_connect_data *ssl, | |
| 533 const char *filename, PRBool cacert) | |
| 534 { | |
| 535 CURLcode result = (cacert) | |
| 536 ? CURLE_SSL_CACERT_BADFILE | |
| 537 : CURLE_SSL_CERTPROBLEM; | |
| 538 | |
| 539 /* libnsspem.so leaks memory if the requested file does not exist. For more | |
| 540 * details, go to <https://bugzilla.redhat.com/734760>. */ | |
| 541 if(is_file(filename)) | |
| 542 result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); | |
| 543 | |
| 544 if(!result && !cacert) { | |
| 545 /* we have successfully loaded a client certificate */ | |
| 546 CERTCertificate *cert; | |
| 547 char *nickname = NULL; | |
| 548 char *n = strrchr(filename, '/'); | |
| 549 if(n) | |
| 550 n++; | |
| 551 | |
| 552 /* The following undocumented magic helps to avoid a SIGSEGV on call | |
| 553 * of PK11_ReadRawAttribute() from SelectClientCert() when using an | |
| 554 * immature version of libnsspem.so. For more details, go to | |
| 555 * <https://bugzilla.redhat.com/733685>. */ | |
| 556 nickname = aprintf("PEM Token #1:%s", n); | |
| 557 if(nickname) { | |
| 558 cert = PK11_FindCertFromNickname(nickname, NULL); | |
| 559 if(cert) | |
| 560 CERT_DestroyCertificate(cert); | |
| 561 | |
| 562 free(nickname); | |
| 563 } | |
| 564 } | |
| 565 | |
| 566 return result; | |
| 567 } | |
| 568 | |
| 569 /* add given CRL to cache if it is not already there */ | |
| 570 static CURLcode nss_cache_crl(SECItem *crl_der) | |
| 571 { | |
| 572 CERTCertDBHandle *db = CERT_GetDefaultCertDB(); | |
| 573 CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0); | |
| 574 if(crl) { | |
| 575 /* CRL already cached */ | |
| 576 SEC_DestroyCrl(crl); | |
| 577 SECITEM_FreeItem(crl_der, PR_TRUE); | |
| 578 return CURLE_OK; | |
| 579 } | |
| 580 | |
| 581 /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ | |
| 582 PR_Lock(nss_crllock); | |
| 583 | |
| 584 if(SECSuccess != CERT_CacheCRL(db, crl_der)) { | |
| 585 /* unable to cache CRL */ | |
| 586 SECITEM_FreeItem(crl_der, PR_TRUE); | |
| 587 PR_Unlock(nss_crllock); | |
| 588 return CURLE_SSL_CRL_BADFILE; | |
| 589 } | |
| 590 | |
| 591 /* store the CRL item so that we can free it in Curl_nss_cleanup() */ | |
| 592 if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { | |
| 593 if(SECSuccess == CERT_UncacheCRL(db, crl_der)) | |
| 594 SECITEM_FreeItem(crl_der, PR_TRUE); | |
| 595 PR_Unlock(nss_crllock); | |
| 596 return CURLE_OUT_OF_MEMORY; | |
| 597 } | |
| 598 | |
| 599 /* we need to clear session cache, so that the CRL could take effect */ | |
| 600 SSL_ClearSessionCache(); | |
| 601 PR_Unlock(nss_crllock); | |
| 602 return CURLE_OK; | |
| 603 } | |
| 604 | |
| 605 static CURLcode nss_load_crl(const char *crlfilename) | |
| 606 { | |
| 607 PRFileDesc *infile; | |
| 608 PRFileInfo info; | |
| 609 SECItem filedata = { 0, NULL, 0 }; | |
| 610 SECItem *crl_der = NULL; | |
| 611 char *body; | |
| 612 | |
| 613 infile = PR_Open(crlfilename, PR_RDONLY, 0); | |
| 614 if(!infile) | |
| 615 return CURLE_SSL_CRL_BADFILE; | |
| 616 | |
| 617 if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) | |
| 618 goto fail; | |
| 619 | |
| 620 if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) | |
| 621 goto fail; | |
| 622 | |
| 623 if(info.size != PR_Read(infile, filedata.data, info.size)) | |
| 624 goto fail; | |
| 625 | |
| 626 crl_der = SECITEM_AllocItem(NULL, NULL, 0U); | |
| 627 if(!crl_der) | |
| 628 goto fail; | |
| 629 | |
| 630 /* place a trailing zero right after the visible data */ | |
| 631 body = (char *)filedata.data; | |
| 632 body[--filedata.len] = '\0'; | |
| 633 | |
| 634 body = strstr(body, "-----BEGIN"); | |
| 635 if(body) { | |
| 636 /* assume ASCII */ | |
| 637 char *trailer; | |
| 638 char *begin = PORT_Strchr(body, '\n'); | |
| 639 if(!begin) | |
| 640 begin = PORT_Strchr(body, '\r'); | |
| 641 if(!begin) | |
| 642 goto fail; | |
| 643 | |
| 644 trailer = strstr(++begin, "-----END"); | |
| 645 if(!trailer) | |
| 646 goto fail; | |
| 647 | |
| 648 /* retrieve DER from ASCII */ | |
| 649 *trailer = '\0'; | |
| 650 if(ATOB_ConvertAsciiToItem(crl_der, begin)) | |
| 651 goto fail; | |
| 652 | |
| 653 SECITEM_FreeItem(&filedata, PR_FALSE); | |
| 654 } | |
| 655 else | |
| 656 /* assume DER */ | |
| 657 *crl_der = filedata; | |
| 658 | |
| 659 PR_Close(infile); | |
| 660 return nss_cache_crl(crl_der); | |
| 661 | |
| 662 fail: | |
| 663 PR_Close(infile); | |
| 664 SECITEM_FreeItem(crl_der, PR_TRUE); | |
| 665 SECITEM_FreeItem(&filedata, PR_FALSE); | |
| 666 return CURLE_SSL_CRL_BADFILE; | |
| 667 } | |
| 668 | |
| 669 static CURLcode nss_load_key(struct connectdata *conn, int sockindex, | |
| 670 char *key_file) | |
| 671 { | |
| 672 PK11SlotInfo *slot, *tmp; | |
| 673 SECStatus status; | |
| 674 CURLcode result; | |
| 675 struct ssl_connect_data *ssl = conn->ssl; | |
| 676 struct Curl_easy *data = conn->data; | |
| 677 | |
| 678 (void)sockindex; /* unused */ | |
| 679 | |
| 680 result = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); | |
| 681 if(result) { | |
| 682 PR_SetError(SEC_ERROR_BAD_KEY, 0); | |
| 683 return result; | |
| 684 } | |
| 685 | |
| 686 slot = nss_find_slot_by_name("PEM Token #1"); | |
| 687 if(!slot) | |
| 688 return CURLE_SSL_CERTPROBLEM; | |
| 689 | |
| 690 /* This will force the token to be seen as re-inserted */ | |
| 691 tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); | |
| 692 if(tmp) | |
| 693 PK11_FreeSlot(tmp); | |
| 694 if(!PK11_IsPresent(slot)) { | |
| 695 PK11_FreeSlot(slot); | |
| 696 return CURLE_SSL_CERTPROBLEM; | |
| 697 } | |
| 698 | |
| 699 status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd)); | |
| 700 PK11_FreeSlot(slot); | |
| 701 | |
| 702 return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; | |
| 703 } | |
| 704 | |
| 705 static int display_error(struct connectdata *conn, PRInt32 err, | |
| 706 const char *filename) | |
| 707 { | |
| 708 switch(err) { | |
| 709 case SEC_ERROR_BAD_PASSWORD: | |
| 710 failf(conn->data, "Unable to load client key: Incorrect password"); | |
| 711 return 1; | |
| 712 case SEC_ERROR_UNKNOWN_CERT: | |
| 713 failf(conn->data, "Unable to load certificate %s", filename); | |
| 714 return 1; | |
| 715 default: | |
| 716 break; | |
| 717 } | |
| 718 return 0; /* The caller will print a generic error */ | |
| 719 } | |
| 720 | |
| 721 static CURLcode cert_stuff(struct connectdata *conn, int sockindex, | |
| 722 char *cert_file, char *key_file) | |
| 723 { | |
| 724 struct Curl_easy *data = conn->data; | |
| 725 CURLcode result; | |
| 726 | |
| 727 if(cert_file) { | |
| 728 result = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); | |
| 729 if(result) { | |
| 730 const PRErrorCode err = PR_GetError(); | |
| 731 if(!display_error(conn, err, cert_file)) { | |
| 732 const char *err_name = nss_error_to_name(err); | |
| 733 failf(data, "unable to load client cert: %d (%s)", err, err_name); | |
| 734 } | |
| 735 | |
| 736 return result; | |
| 737 } | |
| 738 } | |
| 739 | |
| 740 if(key_file || (is_file(cert_file))) { | |
| 741 if(key_file) | |
| 742 result = nss_load_key(conn, sockindex, key_file); | |
| 743 else | |
| 744 /* In case the cert file also has the key */ | |
| 745 result = nss_load_key(conn, sockindex, cert_file); | |
| 746 if(result) { | |
| 747 const PRErrorCode err = PR_GetError(); | |
| 748 if(!display_error(conn, err, key_file)) { | |
| 749 const char *err_name = nss_error_to_name(err); | |
| 750 failf(data, "unable to load client key: %d (%s)", err, err_name); | |
| 751 } | |
| 752 | |
| 753 return result; | |
| 754 } | |
| 755 } | |
| 756 | |
| 757 return CURLE_OK; | |
| 758 } | |
| 759 | |
| 760 static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) | |
| 761 { | |
| 762 (void)slot; /* unused */ | |
| 763 | |
| 764 if(retry || NULL == arg) | |
| 765 return NULL; | |
| 766 else | |
| 767 return (char *)PORT_Strdup((char *)arg); | |
| 768 } | |
| 769 | |
| 770 /* bypass the default SSL_AuthCertificate() hook in case we do not want to | |
| 771 * verify peer */ | |
| 772 static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, | |
| 773 PRBool isServer) | |
| 774 { | |
| 775 struct connectdata *conn = (struct connectdata *)arg; | |
| 776 | |
| 777 #ifdef SSL_ENABLE_OCSP_STAPLING | |
| 778 if(SSL_CONN_CONFIG(verifystatus)) { | |
| 779 SECStatus cacheResult; | |
| 780 | |
| 781 const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); | |
| 782 if(!csa) { | |
| 783 failf(conn->data, "Invalid OCSP response"); | |
| 784 return SECFailure; | |
| 785 } | |
| 786 | |
| 787 if(csa->len == 0) { | |
| 788 failf(conn->data, "No OCSP response received"); | |
| 789 return SECFailure; | |
| 790 } | |
| 791 | |
| 792 cacheResult = CERT_CacheOCSPResponseFromSideChannel( | |
| 793 CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), | |
| 794 PR_Now(), &csa->items[0], arg | |
| 795 ); | |
| 796 | |
| 797 if(cacheResult != SECSuccess) { | |
| 798 failf(conn->data, "Invalid OCSP response"); | |
| 799 return cacheResult; | |
| 800 } | |
| 801 } | |
| 802 #endif | |
| 803 | |
| 804 if(!SSL_CONN_CONFIG(verifypeer)) { | |
| 805 infof(conn->data, "skipping SSL peer certificate verification\n"); | |
| 806 return SECSuccess; | |
| 807 } | |
| 808 | |
| 809 return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); | |
| 810 } | |
| 811 | |
| 812 /** | |
| 813 * Inform the application that the handshake is complete. | |
| 814 */ | |
| 815 static void HandshakeCallback(PRFileDesc *sock, void *arg) | |
| 816 { | |
| 817 struct connectdata *conn = (struct connectdata*) arg; | |
| 818 unsigned int buflenmax = 50; | |
| 819 unsigned char buf[50]; | |
| 820 unsigned int buflen; | |
| 821 SSLNextProtoState state; | |
| 822 | |
| 823 if(!conn->bits.tls_enable_npn && !conn->bits.tls_enable_alpn) { | |
| 824 return; | |
| 825 } | |
| 826 | |
| 827 if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { | |
| 828 | |
| 829 switch(state) { | |
| 830 #if NSSVERNUM >= 0x031a00 /* 3.26.0 */ | |
| 831 /* used by NSS internally to implement 0-RTT */ | |
| 832 case SSL_NEXT_PROTO_EARLY_VALUE: | |
| 833 /* fall through! */ | |
| 834 #endif | |
| 835 case SSL_NEXT_PROTO_NO_SUPPORT: | |
| 836 case SSL_NEXT_PROTO_NO_OVERLAP: | |
| 837 infof(conn->data, "ALPN/NPN, server did not agree to a protocol\n"); | |
| 838 return; | |
| 839 #ifdef SSL_ENABLE_ALPN | |
| 840 case SSL_NEXT_PROTO_SELECTED: | |
| 841 infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf); | |
| 842 break; | |
| 843 #endif | |
| 844 case SSL_NEXT_PROTO_NEGOTIATED: | |
| 845 infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf); | |
| 846 break; | |
| 847 } | |
| 848 | |
| 849 #ifdef USE_NGHTTP2 | |
| 850 if(buflen == NGHTTP2_PROTO_VERSION_ID_LEN && | |
| 851 !memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)) { | |
| 852 conn->negnpn = CURL_HTTP_VERSION_2; | |
| 853 } | |
| 854 else | |
| 855 #endif | |
| 856 if(buflen == ALPN_HTTP_1_1_LENGTH && | |
| 857 !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { | |
| 858 conn->negnpn = CURL_HTTP_VERSION_1_1; | |
| 859 } | |
| 860 Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? | |
| 861 BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); | |
| 862 } | |
| 863 } | |
| 864 | |
| 865 #if NSSVERNUM >= 0x030f04 /* 3.15.4 */ | |
| 866 static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, | |
| 867 PRBool *canFalseStart) | |
| 868 { | |
| 869 struct connectdata *conn = client_data; | |
| 870 struct Curl_easy *data = conn->data; | |
| 871 | |
| 872 SSLChannelInfo channelInfo; | |
| 873 SSLCipherSuiteInfo cipherInfo; | |
| 874 | |
| 875 SECStatus rv; | |
| 876 PRBool negotiatedExtension; | |
| 877 | |
| 878 *canFalseStart = PR_FALSE; | |
| 879 | |
| 880 if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) | |
| 881 return SECFailure; | |
| 882 | |
| 883 if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, | |
| 884 sizeof(cipherInfo)) != SECSuccess) | |
| 885 return SECFailure; | |
| 886 | |
| 887 /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for | |
| 888 * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 | |
| 889 */ | |
| 890 if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) | |
| 891 goto end; | |
| 892 | |
| 893 /* Only allow ECDHE key exchange algorithm. | |
| 894 * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ | |
| 895 if(cipherInfo.keaType != ssl_kea_ecdh) | |
| 896 goto end; | |
| 897 | |
| 898 /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC | |
| 899 * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt | |
| 900 * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ | |
| 901 if(cipherInfo.symCipher != ssl_calg_aes_gcm) | |
| 902 goto end; | |
| 903 | |
| 904 /* Enforce ALPN or NPN to do False Start, as an indicator of server | |
| 905 * compatibility. */ | |
| 906 rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, | |
| 907 &negotiatedExtension); | |
| 908 if(rv != SECSuccess || !negotiatedExtension) { | |
| 909 rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, | |
| 910 &negotiatedExtension); | |
| 911 } | |
| 912 | |
| 913 if(rv != SECSuccess || !negotiatedExtension) | |
| 914 goto end; | |
| 915 | |
| 916 *canFalseStart = PR_TRUE; | |
| 917 | |
| 918 infof(data, "Trying TLS False Start\n"); | |
| 919 | |
| 920 end: | |
| 921 return SECSuccess; | |
| 922 } | |
| 923 #endif | |
| 924 | |
| 925 static void display_cert_info(struct Curl_easy *data, | |
| 926 CERTCertificate *cert) | |
| 927 { | |
| 928 char *subject, *issuer, *common_name; | |
| 929 PRExplodedTime printableTime; | |
| 930 char timeString[256]; | |
| 931 PRTime notBefore, notAfter; | |
| 932 | |
| 933 subject = CERT_NameToAscii(&cert->subject); | |
| 934 issuer = CERT_NameToAscii(&cert->issuer); | |
| 935 common_name = CERT_GetCommonName(&cert->subject); | |
| 936 infof(data, "\tsubject: %s\n", subject); | |
| 937 | |
| 938 CERT_GetCertTimes(cert, ¬Before, ¬After); | |
| 939 PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); | |
| 940 PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); | |
| 941 infof(data, "\tstart date: %s\n", timeString); | |
| 942 PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); | |
| 943 PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); | |
| 944 infof(data, "\texpire date: %s\n", timeString); | |
| 945 infof(data, "\tcommon name: %s\n", common_name); | |
| 946 infof(data, "\tissuer: %s\n", issuer); | |
| 947 | |
| 948 PR_Free(subject); | |
| 949 PR_Free(issuer); | |
| 950 PR_Free(common_name); | |
| 951 } | |
| 952 | |
| 953 static CURLcode display_conn_info(struct connectdata *conn, PRFileDesc *sock) | |
| 954 { | |
| 955 CURLcode result = CURLE_OK; | |
| 956 SSLChannelInfo channel; | |
| 957 SSLCipherSuiteInfo suite; | |
| 958 CERTCertificate *cert; | |
| 959 CERTCertificate *cert2; | |
| 960 CERTCertificate *cert3; | |
| 961 PRTime now; | |
| 962 int i; | |
| 963 | |
| 964 if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) == | |
| 965 SECSuccess && channel.length == sizeof(channel) && | |
| 966 channel.cipherSuite) { | |
| 967 if(SSL_GetCipherSuiteInfo(channel.cipherSuite, | |
| 968 &suite, sizeof(suite)) == SECSuccess) { | |
| 969 infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); | |
| 970 } | |
| 971 } | |
| 972 | |
| 973 cert = SSL_PeerCertificate(sock); | |
| 974 if(cert) { | |
| 975 infof(conn->data, "Server certificate:\n"); | |
| 976 | |
| 977 if(!conn->data->set.ssl.certinfo) { | |
| 978 display_cert_info(conn->data, cert); | |
| 979 CERT_DestroyCertificate(cert); | |
| 980 } | |
| 981 else { | |
| 982 /* Count certificates in chain. */ | |
| 983 now = PR_Now(); | |
| 984 i = 1; | |
| 985 if(!cert->isRoot) { | |
| 986 cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); | |
| 987 while(cert2) { | |
| 988 i++; | |
| 989 if(cert2->isRoot) { | |
| 990 CERT_DestroyCertificate(cert2); | |
| 991 break; | |
| 992 } | |
| 993 cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); | |
| 994 CERT_DestroyCertificate(cert2); | |
| 995 cert2 = cert3; | |
| 996 } | |
| 997 } | |
| 998 | |
| 999 result = Curl_ssl_init_certinfo(conn->data, i); | |
| 1000 if(!result) { | |
| 1001 for(i = 0; cert; cert = cert2) { | |
| 1002 result = Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data, | |
| 1003 (char *)cert->derCert.data + | |
| 1004 cert->derCert.len); | |
| 1005 if(result) | |
| 1006 break; | |
| 1007 | |
| 1008 if(cert->isRoot) { | |
| 1009 CERT_DestroyCertificate(cert); | |
| 1010 break; | |
| 1011 } | |
| 1012 | |
| 1013 cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); | |
| 1014 CERT_DestroyCertificate(cert); | |
| 1015 } | |
| 1016 } | |
| 1017 } | |
| 1018 } | |
| 1019 | |
| 1020 return result; | |
| 1021 } | |
| 1022 | |
| 1023 static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) | |
| 1024 { | |
| 1025 struct connectdata *conn = (struct connectdata *)arg; | |
| 1026 struct Curl_easy *data = conn->data; | |
| 1027 PRErrorCode err = PR_GetError(); | |
| 1028 CERTCertificate *cert; | |
| 1029 | |
| 1030 /* remember the cert verification result */ | |
| 1031 if(SSL_IS_PROXY()) | |
| 1032 data->set.proxy_ssl.certverifyresult = err; | |
| 1033 else | |
| 1034 data->set.ssl.certverifyresult = err; | |
| 1035 | |
| 1036 if(err == SSL_ERROR_BAD_CERT_DOMAIN && !SSL_CONN_CONFIG(verifyhost)) | |
| 1037 /* we are asked not to verify the host name */ | |
| 1038 return SECSuccess; | |
| 1039 | |
| 1040 /* print only info about the cert, the error is printed off the callback */ | |
| 1041 cert = SSL_PeerCertificate(sock); | |
| 1042 if(cert) { | |
| 1043 infof(data, "Server certificate:\n"); | |
| 1044 display_cert_info(data, cert); | |
| 1045 CERT_DestroyCertificate(cert); | |
| 1046 } | |
| 1047 | |
| 1048 return SECFailure; | |
| 1049 } | |
| 1050 | |
| 1051 /** | |
| 1052 * | |
| 1053 * Check that the Peer certificate's issuer certificate matches the one found | |
| 1054 * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the | |
| 1055 * issuer check, so we provide comments that mimic the OpenSSL | |
| 1056 * X509_check_issued function (in x509v3/v3_purp.c) | |
| 1057 */ | |
| 1058 static SECStatus check_issuer_cert(PRFileDesc *sock, | |
| 1059 char *issuer_nickname) | |
| 1060 { | |
| 1061 CERTCertificate *cert, *cert_issuer, *issuer; | |
| 1062 SECStatus res = SECSuccess; | |
| 1063 void *proto_win = NULL; | |
| 1064 | |
| 1065 cert = SSL_PeerCertificate(sock); | |
| 1066 cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); | |
| 1067 | |
| 1068 proto_win = SSL_RevealPinArg(sock); | |
| 1069 issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); | |
| 1070 | |
| 1071 if((!cert_issuer) || (!issuer)) | |
| 1072 res = SECFailure; | |
| 1073 else if(SECITEM_CompareItem(&cert_issuer->derCert, | |
| 1074 &issuer->derCert) != SECEqual) | |
| 1075 res = SECFailure; | |
| 1076 | |
| 1077 CERT_DestroyCertificate(cert); | |
| 1078 CERT_DestroyCertificate(issuer); | |
| 1079 CERT_DestroyCertificate(cert_issuer); | |
| 1080 return res; | |
| 1081 } | |
| 1082 | |
| 1083 static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, | |
| 1084 const char *pinnedpubkey) | |
| 1085 { | |
| 1086 CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; | |
| 1087 struct Curl_easy *data = BACKEND->data; | |
| 1088 CERTCertificate *cert; | |
| 1089 | |
| 1090 if(!pinnedpubkey) | |
| 1091 /* no pinned public key specified */ | |
| 1092 return CURLE_OK; | |
| 1093 | |
| 1094 /* get peer certificate */ | |
| 1095 cert = SSL_PeerCertificate(BACKEND->handle); | |
| 1096 if(cert) { | |
| 1097 /* extract public key from peer certificate */ | |
| 1098 SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); | |
| 1099 if(pubkey) { | |
| 1100 /* encode the public key as DER */ | |
| 1101 SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); | |
| 1102 if(cert_der) { | |
| 1103 /* compare the public key with the pinned public key */ | |
| 1104 result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data, | |
| 1105 cert_der->len); | |
| 1106 SECITEM_FreeItem(cert_der, PR_TRUE); | |
| 1107 } | |
| 1108 SECKEY_DestroyPublicKey(pubkey); | |
| 1109 } | |
| 1110 CERT_DestroyCertificate(cert); | |
| 1111 } | |
| 1112 | |
| 1113 /* report the resulting status */ | |
| 1114 switch(result) { | |
| 1115 case CURLE_OK: | |
| 1116 infof(data, "pinned public key verified successfully!\n"); | |
| 1117 break; | |
| 1118 case CURLE_SSL_PINNEDPUBKEYNOTMATCH: | |
| 1119 failf(data, "failed to verify pinned public key"); | |
| 1120 break; | |
| 1121 default: | |
| 1122 /* OOM, etc. */ | |
| 1123 break; | |
| 1124 } | |
| 1125 | |
| 1126 return result; | |
| 1127 } | |
| 1128 | |
| 1129 /** | |
| 1130 * | |
| 1131 * Callback to pick the SSL client certificate. | |
| 1132 */ | |
| 1133 static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, | |
| 1134 struct CERTDistNamesStr *caNames, | |
| 1135 struct CERTCertificateStr **pRetCert, | |
| 1136 struct SECKEYPrivateKeyStr **pRetKey) | |
| 1137 { | |
| 1138 struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; | |
| 1139 struct Curl_easy *data = BACKEND->data; | |
| 1140 const char *nickname = BACKEND->client_nickname; | |
| 1141 static const char pem_slotname[] = "PEM Token #1"; | |
| 1142 | |
| 1143 if(BACKEND->obj_clicert) { | |
| 1144 /* use the cert/key provided by PEM reader */ | |
| 1145 SECItem cert_der = { 0, NULL, 0 }; | |
| 1146 void *proto_win = SSL_RevealPinArg(sock); | |
| 1147 struct CERTCertificateStr *cert; | |
| 1148 struct SECKEYPrivateKeyStr *key; | |
| 1149 | |
| 1150 PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); | |
| 1151 if(NULL == slot) { | |
| 1152 failf(data, "NSS: PK11 slot not found: %s", pem_slotname); | |
| 1153 return SECFailure; | |
| 1154 } | |
| 1155 | |
| 1156 if(PK11_ReadRawAttribute(PK11_TypeGeneric, BACKEND->obj_clicert, CKA_VALUE, | |
| 1157 &cert_der) != SECSuccess) { | |
| 1158 failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); | |
| 1159 PK11_FreeSlot(slot); | |
| 1160 return SECFailure; | |
| 1161 } | |
| 1162 | |
| 1163 cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); | |
| 1164 SECITEM_FreeItem(&cert_der, PR_FALSE); | |
| 1165 if(NULL == cert) { | |
| 1166 failf(data, "NSS: client certificate from file not found"); | |
| 1167 PK11_FreeSlot(slot); | |
| 1168 return SECFailure; | |
| 1169 } | |
| 1170 | |
| 1171 key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); | |
| 1172 PK11_FreeSlot(slot); | |
| 1173 if(NULL == key) { | |
| 1174 failf(data, "NSS: private key from file not found"); | |
| 1175 CERT_DestroyCertificate(cert); | |
| 1176 return SECFailure; | |
| 1177 } | |
| 1178 | |
| 1179 infof(data, "NSS: client certificate from file\n"); | |
| 1180 display_cert_info(data, cert); | |
| 1181 | |
| 1182 *pRetCert = cert; | |
| 1183 *pRetKey = key; | |
| 1184 return SECSuccess; | |
| 1185 } | |
| 1186 | |
| 1187 /* use the default NSS hook */ | |
| 1188 if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, | |
| 1189 pRetCert, pRetKey) | |
| 1190 || NULL == *pRetCert) { | |
| 1191 | |
| 1192 if(NULL == nickname) | |
| 1193 failf(data, "NSS: client certificate not found (nickname not " | |
| 1194 "specified)"); | |
| 1195 else | |
| 1196 failf(data, "NSS: client certificate not found: %s", nickname); | |
| 1197 | |
| 1198 return SECFailure; | |
| 1199 } | |
| 1200 | |
| 1201 /* get certificate nickname if any */ | |
| 1202 nickname = (*pRetCert)->nickname; | |
| 1203 if(NULL == nickname) | |
| 1204 nickname = "[unknown]"; | |
| 1205 | |
| 1206 if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { | |
| 1207 failf(data, "NSS: refusing previously loaded certificate from file: %s", | |
| 1208 nickname); | |
| 1209 return SECFailure; | |
| 1210 } | |
| 1211 | |
| 1212 if(NULL == *pRetKey) { | |
| 1213 failf(data, "NSS: private key not found for certificate: %s", nickname); | |
| 1214 return SECFailure; | |
| 1215 } | |
| 1216 | |
| 1217 infof(data, "NSS: using client certificate: %s\n", nickname); | |
| 1218 display_cert_info(data, *pRetCert); | |
| 1219 return SECSuccess; | |
| 1220 } | |
| 1221 | |
| 1222 /* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ | |
| 1223 static void nss_update_connecting_state(ssl_connect_state state, void *secret) | |
| 1224 { | |
| 1225 struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret; | |
| 1226 if(PR_GetError() != PR_WOULD_BLOCK_ERROR) | |
| 1227 /* an unrelated error is passing by */ | |
| 1228 return; | |
| 1229 | |
| 1230 switch(connssl->connecting_state) { | |
| 1231 case ssl_connect_2: | |
| 1232 case ssl_connect_2_reading: | |
| 1233 case ssl_connect_2_writing: | |
| 1234 break; | |
| 1235 default: | |
| 1236 /* we are not called from an SSL handshake */ | |
| 1237 return; | |
| 1238 } | |
| 1239 | |
| 1240 /* update the state accordingly */ | |
| 1241 connssl->connecting_state = state; | |
| 1242 } | |
| 1243 | |
| 1244 /* recv() wrapper we use to detect blocking direction during SSL handshake */ | |
| 1245 static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, | |
| 1246 PRIntn flags, PRIntervalTime timeout) | |
| 1247 { | |
| 1248 const PRRecvFN recv_fn = fd->lower->methods->recv; | |
| 1249 const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout); | |
| 1250 if(rv < 0) | |
| 1251 /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ | |
| 1252 nss_update_connecting_state(ssl_connect_2_reading, fd->secret); | |
| 1253 return rv; | |
| 1254 } | |
| 1255 | |
| 1256 /* send() wrapper we use to detect blocking direction during SSL handshake */ | |
| 1257 static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, | |
| 1258 PRIntn flags, PRIntervalTime timeout) | |
| 1259 { | |
| 1260 const PRSendFN send_fn = fd->lower->methods->send; | |
| 1261 const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout); | |
| 1262 if(rv < 0) | |
| 1263 /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ | |
| 1264 nss_update_connecting_state(ssl_connect_2_writing, fd->secret); | |
| 1265 return rv; | |
| 1266 } | |
| 1267 | |
| 1268 /* close() wrapper to avoid assertion failure due to fd->secret != NULL */ | |
| 1269 static PRStatus nspr_io_close(PRFileDesc *fd) | |
| 1270 { | |
| 1271 const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close; | |
| 1272 fd->secret = NULL; | |
| 1273 return close_fn(fd); | |
| 1274 } | |
| 1275 | |
| 1276 /* load a PKCS #11 module */ | |
| 1277 static CURLcode nss_load_module(SECMODModule **pmod, const char *library, | |
| 1278 const char *name) | |
| 1279 { | |
| 1280 char *config_string; | |
| 1281 SECMODModule *module = *pmod; | |
| 1282 if(module) | |
| 1283 /* already loaded */ | |
| 1284 return CURLE_OK; | |
| 1285 | |
| 1286 config_string = aprintf("library=%s name=%s", library, name); | |
| 1287 if(!config_string) | |
| 1288 return CURLE_OUT_OF_MEMORY; | |
| 1289 | |
| 1290 module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); | |
| 1291 free(config_string); | |
| 1292 | |
| 1293 if(module && module->loaded) { | |
| 1294 /* loaded successfully */ | |
| 1295 *pmod = module; | |
| 1296 return CURLE_OK; | |
| 1297 } | |
| 1298 | |
| 1299 if(module) | |
| 1300 SECMOD_DestroyModule(module); | |
| 1301 return CURLE_FAILED_INIT; | |
| 1302 } | |
| 1303 | |
| 1304 /* unload a PKCS #11 module */ | |
| 1305 static void nss_unload_module(SECMODModule **pmod) | |
| 1306 { | |
| 1307 SECMODModule *module = *pmod; | |
| 1308 if(!module) | |
| 1309 /* not loaded */ | |
| 1310 return; | |
| 1311 | |
| 1312 if(SECMOD_UnloadUserModule(module) != SECSuccess) | |
| 1313 /* unload failed */ | |
| 1314 return; | |
| 1315 | |
| 1316 SECMOD_DestroyModule(module); | |
| 1317 *pmod = NULL; | |
| 1318 } | |
| 1319 | |
| 1320 /* data might be NULL */ | |
| 1321 static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) | |
| 1322 { | |
| 1323 NSSInitParameters initparams; | |
| 1324 PRErrorCode err; | |
| 1325 const char *err_name; | |
| 1326 | |
| 1327 if(nss_context != NULL) | |
| 1328 return CURLE_OK; | |
| 1329 | |
| 1330 memset((void *) &initparams, '\0', sizeof(initparams)); | |
| 1331 initparams.length = sizeof(initparams); | |
| 1332 | |
| 1333 if(cert_dir) { | |
| 1334 char *certpath = aprintf("sql:%s", cert_dir); | |
| 1335 if(!certpath) | |
| 1336 return CURLE_OUT_OF_MEMORY; | |
| 1337 | |
| 1338 infof(data, "Initializing NSS with certpath: %s\n", certpath); | |
| 1339 nss_context = NSS_InitContext(certpath, "", "", "", &initparams, | |
| 1340 NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); | |
| 1341 free(certpath); | |
| 1342 | |
| 1343 if(nss_context != NULL) | |
| 1344 return CURLE_OK; | |
| 1345 | |
| 1346 err = PR_GetError(); | |
| 1347 err_name = nss_error_to_name(err); | |
| 1348 infof(data, "Unable to initialize NSS database: %d (%s)\n", err, err_name); | |
| 1349 } | |
| 1350 | |
| 1351 infof(data, "Initializing NSS with certpath: none\n"); | |
| 1352 nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY | |
| 1353 | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN | |
| 1354 | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); | |
| 1355 if(nss_context != NULL) | |
| 1356 return CURLE_OK; | |
| 1357 | |
| 1358 err = PR_GetError(); | |
| 1359 err_name = nss_error_to_name(err); | |
| 1360 failf(data, "Unable to initialize NSS: %d (%s)", err, err_name); | |
| 1361 return CURLE_SSL_CACERT_BADFILE; | |
| 1362 } | |
| 1363 | |
| 1364 /* data might be NULL */ | |
| 1365 static CURLcode nss_init(struct Curl_easy *data) | |
| 1366 { | |
| 1367 char *cert_dir; | |
| 1368 struct_stat st; | |
| 1369 CURLcode result; | |
| 1370 | |
| 1371 if(initialized) | |
| 1372 return CURLE_OK; | |
| 1373 | |
| 1374 /* list of all CRL items we need to destroy in Curl_nss_cleanup() */ | |
| 1375 Curl_llist_init(&nss_crl_list, nss_destroy_crl_item); | |
| 1376 | |
| 1377 /* First we check if $SSL_DIR points to a valid dir */ | |
| 1378 cert_dir = getenv("SSL_DIR"); | |
| 1379 if(cert_dir) { | |
| 1380 if((stat(cert_dir, &st) != 0) || | |
| 1381 (!S_ISDIR(st.st_mode))) { | |
| 1382 cert_dir = NULL; | |
| 1383 } | |
| 1384 } | |
| 1385 | |
| 1386 /* Now we check if the default location is a valid dir */ | |
| 1387 if(!cert_dir) { | |
| 1388 if((stat(SSL_DIR, &st) == 0) && | |
| 1389 (S_ISDIR(st.st_mode))) { | |
| 1390 cert_dir = (char *)SSL_DIR; | |
| 1391 } | |
| 1392 } | |
| 1393 | |
| 1394 if(nspr_io_identity == PR_INVALID_IO_LAYER) { | |
| 1395 /* allocate an identity for our own NSPR I/O layer */ | |
| 1396 nspr_io_identity = PR_GetUniqueIdentity("libcurl"); | |
| 1397 if(nspr_io_identity == PR_INVALID_IO_LAYER) | |
| 1398 return CURLE_OUT_OF_MEMORY; | |
| 1399 | |
| 1400 /* the default methods just call down to the lower I/O layer */ | |
| 1401 memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(), | |
| 1402 sizeof(nspr_io_methods)); | |
| 1403 | |
| 1404 /* override certain methods in the table by our wrappers */ | |
| 1405 nspr_io_methods.recv = nspr_io_recv; | |
| 1406 nspr_io_methods.send = nspr_io_send; | |
| 1407 nspr_io_methods.close = nspr_io_close; | |
| 1408 } | |
| 1409 | |
| 1410 result = nss_init_core(data, cert_dir); | |
| 1411 if(result) | |
| 1412 return result; | |
| 1413 | |
| 1414 if(!any_cipher_enabled()) | |
| 1415 NSS_SetDomesticPolicy(); | |
| 1416 | |
| 1417 initialized = 1; | |
| 1418 | |
| 1419 return CURLE_OK; | |
| 1420 } | |
| 1421 | |
| 1422 /** | |
| 1423 * Global SSL init | |
| 1424 * | |
| 1425 * @retval 0 error initializing SSL | |
| 1426 * @retval 1 SSL initialized successfully | |
| 1427 */ | |
| 1428 static int Curl_nss_init(void) | |
| 1429 { | |
| 1430 /* curl_global_init() is not thread-safe so this test is ok */ | |
| 1431 if(nss_initlock == NULL) { | |
| 1432 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); | |
| 1433 nss_initlock = PR_NewLock(); | |
| 1434 nss_crllock = PR_NewLock(); | |
| 1435 nss_findslot_lock = PR_NewLock(); | |
| 1436 nss_trustload_lock = PR_NewLock(); | |
| 1437 } | |
| 1438 | |
| 1439 /* We will actually initialize NSS later */ | |
| 1440 | |
| 1441 return 1; | |
| 1442 } | |
| 1443 | |
| 1444 /* data might be NULL */ | |
| 1445 CURLcode Curl_nss_force_init(struct Curl_easy *data) | |
| 1446 { | |
| 1447 CURLcode result; | |
| 1448 if(!nss_initlock) { | |
| 1449 if(data) | |
| 1450 failf(data, "unable to initialize NSS, curl_global_init() should have " | |
| 1451 "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); | |
| 1452 return CURLE_FAILED_INIT; | |
| 1453 } | |
| 1454 | |
| 1455 PR_Lock(nss_initlock); | |
| 1456 result = nss_init(data); | |
| 1457 PR_Unlock(nss_initlock); | |
| 1458 | |
| 1459 return result; | |
| 1460 } | |
| 1461 | |
| 1462 /* Global cleanup */ | |
| 1463 static void Curl_nss_cleanup(void) | |
| 1464 { | |
| 1465 /* This function isn't required to be threadsafe and this is only done | |
| 1466 * as a safety feature. | |
| 1467 */ | |
| 1468 PR_Lock(nss_initlock); | |
| 1469 if(initialized) { | |
| 1470 /* Free references to client certificates held in the SSL session cache. | |
| 1471 * Omitting this hampers destruction of the security module owning | |
| 1472 * the certificates. */ | |
| 1473 SSL_ClearSessionCache(); | |
| 1474 | |
| 1475 nss_unload_module(&pem_module); | |
| 1476 nss_unload_module(&trust_module); | |
| 1477 NSS_ShutdownContext(nss_context); | |
| 1478 nss_context = NULL; | |
| 1479 } | |
| 1480 | |
| 1481 /* destroy all CRL items */ | |
| 1482 Curl_llist_destroy(&nss_crl_list, NULL); | |
| 1483 | |
| 1484 PR_Unlock(nss_initlock); | |
| 1485 | |
| 1486 PR_DestroyLock(nss_initlock); | |
| 1487 PR_DestroyLock(nss_crllock); | |
| 1488 PR_DestroyLock(nss_findslot_lock); | |
| 1489 PR_DestroyLock(nss_trustload_lock); | |
| 1490 nss_initlock = NULL; | |
| 1491 | |
| 1492 initialized = 0; | |
| 1493 } | |
| 1494 | |
| 1495 /* | |
| 1496 * This function uses SSL_peek to determine connection status. | |
| 1497 * | |
| 1498 * Return codes: | |
| 1499 * 1 means the connection is still in place | |
| 1500 * 0 means the connection has been closed | |
| 1501 * -1 means the connection status is unknown | |
| 1502 */ | |
| 1503 static int Curl_nss_check_cxn(struct connectdata *conn) | |
| 1504 { | |
| 1505 struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; | |
| 1506 int rc; | |
| 1507 char buf; | |
| 1508 | |
| 1509 rc = | |
| 1510 PR_Recv(BACKEND->handle, (void *)&buf, 1, PR_MSG_PEEK, | |
| 1511 PR_SecondsToInterval(1)); | |
| 1512 if(rc > 0) | |
| 1513 return 1; /* connection still in place */ | |
| 1514 | |
| 1515 if(rc == 0) | |
| 1516 return 0; /* connection has been closed */ | |
| 1517 | |
| 1518 return -1; /* connection status unknown */ | |
| 1519 } | |
| 1520 | |
| 1521 static void nss_close(struct ssl_connect_data *connssl) | |
| 1522 { | |
| 1523 /* before the cleanup, check whether we are using a client certificate */ | |
| 1524 const bool client_cert = (BACKEND->client_nickname != NULL) | |
| 1525 || (BACKEND->obj_clicert != NULL); | |
| 1526 | |
| 1527 free(BACKEND->client_nickname); | |
| 1528 BACKEND->client_nickname = NULL; | |
| 1529 | |
| 1530 /* destroy all NSS objects in order to avoid failure of NSS shutdown */ | |
| 1531 Curl_llist_destroy(&BACKEND->obj_list, NULL); | |
| 1532 BACKEND->obj_clicert = NULL; | |
| 1533 | |
| 1534 if(BACKEND->handle) { | |
| 1535 if(client_cert) | |
| 1536 /* A server might require different authentication based on the | |
| 1537 * particular path being requested by the client. To support this | |
| 1538 * scenario, we must ensure that a connection will never reuse the | |
| 1539 * authentication data from a previous connection. */ | |
| 1540 SSL_InvalidateSession(BACKEND->handle); | |
| 1541 | |
| 1542 PR_Close(BACKEND->handle); | |
| 1543 BACKEND->handle = NULL; | |
| 1544 } | |
| 1545 } | |
| 1546 | |
| 1547 /* | |
| 1548 * This function is called when an SSL connection is closed. | |
| 1549 */ | |
| 1550 static void Curl_nss_close(struct connectdata *conn, int sockindex) | |
| 1551 { | |
| 1552 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1553 struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; | |
| 1554 | |
| 1555 if(BACKEND->handle || connssl_proxy->backend->handle) { | |
| 1556 /* NSS closes the socket we previously handed to it, so we must mark it | |
| 1557 as closed to avoid double close */ | |
| 1558 fake_sclose(conn->sock[sockindex]); | |
| 1559 conn->sock[sockindex] = CURL_SOCKET_BAD; | |
| 1560 } | |
| 1561 | |
| 1562 if(BACKEND->handle) | |
| 1563 /* nss_close(connssl) will transitively close also | |
| 1564 connssl_proxy->backend->handle if both are used. Clear it to avoid | |
| 1565 a double close leading to crash. */ | |
| 1566 connssl_proxy->backend->handle = NULL; | |
| 1567 | |
| 1568 nss_close(connssl); | |
| 1569 nss_close(connssl_proxy); | |
| 1570 } | |
| 1571 | |
| 1572 /* return true if NSS can provide error code (and possibly msg) for the | |
| 1573 error */ | |
| 1574 static bool is_nss_error(CURLcode err) | |
| 1575 { | |
| 1576 switch(err) { | |
| 1577 case CURLE_PEER_FAILED_VERIFICATION: | |
| 1578 case CURLE_SSL_CERTPROBLEM: | |
| 1579 case CURLE_SSL_CONNECT_ERROR: | |
| 1580 case CURLE_SSL_ISSUER_ERROR: | |
| 1581 return true; | |
| 1582 | |
| 1583 default: | |
| 1584 return false; | |
| 1585 } | |
| 1586 } | |
| 1587 | |
| 1588 /* return true if the given error code is related to a client certificate */ | |
| 1589 static bool is_cc_error(PRInt32 err) | |
| 1590 { | |
| 1591 switch(err) { | |
| 1592 case SSL_ERROR_BAD_CERT_ALERT: | |
| 1593 case SSL_ERROR_EXPIRED_CERT_ALERT: | |
| 1594 case SSL_ERROR_REVOKED_CERT_ALERT: | |
| 1595 return true; | |
| 1596 | |
| 1597 default: | |
| 1598 return false; | |
| 1599 } | |
| 1600 } | |
| 1601 | |
| 1602 static Curl_recv nss_recv; | |
| 1603 static Curl_send nss_send; | |
| 1604 | |
| 1605 static CURLcode nss_load_ca_certificates(struct connectdata *conn, | |
| 1606 int sockindex) | |
| 1607 { | |
| 1608 struct Curl_easy *data = conn->data; | |
| 1609 const char *cafile = SSL_CONN_CONFIG(CAfile); | |
| 1610 const char *capath = SSL_CONN_CONFIG(CApath); | |
| 1611 bool use_trust_module; | |
| 1612 CURLcode result = CURLE_OK; | |
| 1613 | |
| 1614 /* treat empty string as unset */ | |
| 1615 if(cafile && !cafile[0]) | |
| 1616 cafile = NULL; | |
| 1617 if(capath && !capath[0]) | |
| 1618 capath = NULL; | |
| 1619 | |
| 1620 infof(data, " CAfile: %s\n CApath: %s\n", | |
| 1621 cafile ? cafile : "none", | |
| 1622 capath ? capath : "none"); | |
| 1623 | |
| 1624 /* load libnssckbi.so if no other trust roots were specified */ | |
| 1625 use_trust_module = !cafile && !capath; | |
| 1626 | |
| 1627 PR_Lock(nss_trustload_lock); | |
| 1628 if(use_trust_module && !trust_module) { | |
| 1629 /* libnssckbi.so needed but not yet loaded --> load it! */ | |
| 1630 result = nss_load_module(&trust_module, trust_library, "trust"); | |
| 1631 infof(data, "%s %s\n", (result) ? "failed to load" : "loaded", | |
| 1632 trust_library); | |
| 1633 if(result == CURLE_FAILED_INIT) | |
| 1634 /* If libnssckbi.so is not available (or fails to load), one can still | |
| 1635 use CA certificates stored in NSS database. Ignore the failure. */ | |
| 1636 result = CURLE_OK; | |
| 1637 } | |
| 1638 else if(!use_trust_module && trust_module) { | |
| 1639 /* libnssckbi.so not needed but already loaded --> unload it! */ | |
| 1640 infof(data, "unloading %s\n", trust_library); | |
| 1641 nss_unload_module(&trust_module); | |
| 1642 } | |
| 1643 PR_Unlock(nss_trustload_lock); | |
| 1644 | |
| 1645 if(cafile) | |
| 1646 result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); | |
| 1647 | |
| 1648 if(result) | |
| 1649 return result; | |
| 1650 | |
| 1651 if(capath) { | |
| 1652 struct_stat st; | |
| 1653 if(stat(capath, &st) == -1) | |
| 1654 return CURLE_SSL_CACERT_BADFILE; | |
| 1655 | |
| 1656 if(S_ISDIR(st.st_mode)) { | |
| 1657 PRDirEntry *entry; | |
| 1658 PRDir *dir = PR_OpenDir(capath); | |
| 1659 if(!dir) | |
| 1660 return CURLE_SSL_CACERT_BADFILE; | |
| 1661 | |
| 1662 while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) { | |
| 1663 char *fullpath = aprintf("%s/%s", capath, entry->name); | |
| 1664 if(!fullpath) { | |
| 1665 PR_CloseDir(dir); | |
| 1666 return CURLE_OUT_OF_MEMORY; | |
| 1667 } | |
| 1668 | |
| 1669 if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) | |
| 1670 /* This is purposefully tolerant of errors so non-PEM files can | |
| 1671 * be in the same directory */ | |
| 1672 infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath); | |
| 1673 | |
| 1674 free(fullpath); | |
| 1675 } | |
| 1676 | |
| 1677 PR_CloseDir(dir); | |
| 1678 } | |
| 1679 else | |
| 1680 infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath); | |
| 1681 } | |
| 1682 | |
| 1683 return CURLE_OK; | |
| 1684 } | |
| 1685 | |
| 1686 static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version) | |
| 1687 { | |
| 1688 switch(version) { | |
| 1689 case CURL_SSLVERSION_SSLv2: | |
| 1690 *nssver = SSL_LIBRARY_VERSION_2; | |
| 1691 return CURLE_OK; | |
| 1692 | |
| 1693 case CURL_SSLVERSION_SSLv3: | |
| 1694 *nssver = SSL_LIBRARY_VERSION_3_0; | |
| 1695 return CURLE_OK; | |
| 1696 | |
| 1697 case CURL_SSLVERSION_TLSv1_0: | |
| 1698 *nssver = SSL_LIBRARY_VERSION_TLS_1_0; | |
| 1699 return CURLE_OK; | |
| 1700 | |
| 1701 case CURL_SSLVERSION_TLSv1_1: | |
| 1702 #ifdef SSL_LIBRARY_VERSION_TLS_1_1 | |
| 1703 *nssver = SSL_LIBRARY_VERSION_TLS_1_1; | |
| 1704 return CURLE_OK; | |
| 1705 #else | |
| 1706 return CURLE_SSL_CONNECT_ERROR; | |
| 1707 #endif | |
| 1708 | |
| 1709 case CURL_SSLVERSION_TLSv1_2: | |
| 1710 #ifdef SSL_LIBRARY_VERSION_TLS_1_2 | |
| 1711 *nssver = SSL_LIBRARY_VERSION_TLS_1_2; | |
| 1712 return CURLE_OK; | |
| 1713 #else | |
| 1714 return CURLE_SSL_CONNECT_ERROR; | |
| 1715 #endif | |
| 1716 | |
| 1717 case CURL_SSLVERSION_TLSv1_3: | |
| 1718 #ifdef SSL_LIBRARY_VERSION_TLS_1_3 | |
| 1719 *nssver = SSL_LIBRARY_VERSION_TLS_1_3; | |
| 1720 return CURLE_OK; | |
| 1721 #else | |
| 1722 return CURLE_SSL_CONNECT_ERROR; | |
| 1723 #endif | |
| 1724 | |
| 1725 default: | |
| 1726 return CURLE_SSL_CONNECT_ERROR; | |
| 1727 } | |
| 1728 } | |
| 1729 | |
| 1730 static CURLcode nss_init_sslver(SSLVersionRange *sslver, | |
| 1731 struct Curl_easy *data, | |
| 1732 struct connectdata *conn) | |
| 1733 { | |
| 1734 CURLcode result; | |
| 1735 const long min = SSL_CONN_CONFIG(version); | |
| 1736 const long max = SSL_CONN_CONFIG(version_max); | |
| 1737 SSLVersionRange vrange; | |
| 1738 | |
| 1739 switch(min) { | |
| 1740 case CURL_SSLVERSION_TLSv1: | |
| 1741 case CURL_SSLVERSION_DEFAULT: | |
| 1742 /* Bump our minimum TLS version if NSS has stricter requirements. */ | |
| 1743 if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) | |
| 1744 return CURLE_SSL_CONNECT_ERROR; | |
| 1745 if(sslver->min < vrange.min) | |
| 1746 sslver->min = vrange.min; | |
| 1747 break; | |
| 1748 default: | |
| 1749 result = nss_sslver_from_curl(&sslver->min, min); | |
| 1750 if(result) { | |
| 1751 failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); | |
| 1752 return result; | |
| 1753 } | |
| 1754 } | |
| 1755 | |
| 1756 switch(max) { | |
| 1757 case CURL_SSLVERSION_MAX_NONE: | |
| 1758 case CURL_SSLVERSION_MAX_DEFAULT: | |
| 1759 break; | |
| 1760 default: | |
| 1761 result = nss_sslver_from_curl(&sslver->max, max >> 16); | |
| 1762 if(result) { | |
| 1763 failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); | |
| 1764 return result; | |
| 1765 } | |
| 1766 } | |
| 1767 | |
| 1768 return CURLE_OK; | |
| 1769 } | |
| 1770 | |
| 1771 static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, | |
| 1772 struct Curl_easy *data, | |
| 1773 CURLcode curlerr) | |
| 1774 { | |
| 1775 PRErrorCode err = 0; | |
| 1776 | |
| 1777 if(is_nss_error(curlerr)) { | |
| 1778 /* read NSPR error code */ | |
| 1779 err = PR_GetError(); | |
| 1780 if(is_cc_error(err)) | |
| 1781 curlerr = CURLE_SSL_CERTPROBLEM; | |
| 1782 | |
| 1783 /* print the error number and error string */ | |
| 1784 infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); | |
| 1785 | |
| 1786 /* print a human-readable message describing the error if available */ | |
| 1787 nss_print_error_message(data, err); | |
| 1788 } | |
| 1789 | |
| 1790 /* cleanup on connection failure */ | |
| 1791 Curl_llist_destroy(&BACKEND->obj_list, NULL); | |
| 1792 | |
| 1793 return curlerr; | |
| 1794 } | |
| 1795 | |
| 1796 /* Switch the SSL socket into blocking or non-blocking mode. */ | |
| 1797 static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, | |
| 1798 struct Curl_easy *data, | |
| 1799 bool blocking) | |
| 1800 { | |
| 1801 static PRSocketOptionData sock_opt; | |
| 1802 sock_opt.option = PR_SockOpt_Nonblocking; | |
| 1803 sock_opt.value.non_blocking = !blocking; | |
| 1804 | |
| 1805 if(PR_SetSocketOption(BACKEND->handle, &sock_opt) != PR_SUCCESS) | |
| 1806 return nss_fail_connect(connssl, data, CURLE_SSL_CONNECT_ERROR); | |
| 1807 | |
| 1808 return CURLE_OK; | |
| 1809 } | |
| 1810 | |
| 1811 static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) | |
| 1812 { | |
| 1813 PRFileDesc *model = NULL; | |
| 1814 PRFileDesc *nspr_io = NULL; | |
| 1815 PRFileDesc *nspr_io_stub = NULL; | |
| 1816 PRBool ssl_no_cache; | |
| 1817 PRBool ssl_cbc_random_iv; | |
| 1818 struct Curl_easy *data = conn->data; | |
| 1819 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 1820 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1821 CURLcode result; | |
| 1822 bool second_layer = FALSE; | |
| 1823 SSLVersionRange sslver_supported; | |
| 1824 | |
| 1825 SSLVersionRange sslver = { | |
| 1826 SSL_LIBRARY_VERSION_TLS_1_0, /* min */ | |
| 1827 #ifdef SSL_LIBRARY_VERSION_TLS_1_3 | |
| 1828 SSL_LIBRARY_VERSION_TLS_1_3 /* max */ | |
| 1829 #elif defined SSL_LIBRARY_VERSION_TLS_1_2 | |
| 1830 SSL_LIBRARY_VERSION_TLS_1_2 | |
| 1831 #elif defined SSL_LIBRARY_VERSION_TLS_1_1 | |
| 1832 SSL_LIBRARY_VERSION_TLS_1_1 | |
| 1833 #else | |
| 1834 SSL_LIBRARY_VERSION_TLS_1_0 | |
| 1835 #endif | |
| 1836 }; | |
| 1837 | |
| 1838 BACKEND->data = data; | |
| 1839 | |
| 1840 /* list of all NSS objects we need to destroy in Curl_nss_close() */ | |
| 1841 Curl_llist_init(&BACKEND->obj_list, nss_destroy_object); | |
| 1842 | |
| 1843 PR_Lock(nss_initlock); | |
| 1844 result = nss_init(conn->data); | |
| 1845 if(result) { | |
| 1846 PR_Unlock(nss_initlock); | |
| 1847 goto error; | |
| 1848 } | |
| 1849 | |
| 1850 PK11_SetPasswordFunc(nss_get_password); | |
| 1851 | |
| 1852 result = nss_load_module(&pem_module, pem_library, "PEM"); | |
| 1853 PR_Unlock(nss_initlock); | |
| 1854 if(result == CURLE_FAILED_INIT) | |
| 1855 infof(data, "WARNING: failed to load NSS PEM library %s. Using " | |
| 1856 "OpenSSL PEM certificates will not work.\n", pem_library); | |
| 1857 else if(result) | |
| 1858 goto error; | |
| 1859 | |
| 1860 result = CURLE_SSL_CONNECT_ERROR; | |
| 1861 | |
| 1862 model = PR_NewTCPSocket(); | |
| 1863 if(!model) | |
| 1864 goto error; | |
| 1865 model = SSL_ImportFD(NULL, model); | |
| 1866 | |
| 1867 if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) | |
| 1868 goto error; | |
| 1869 if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) | |
| 1870 goto error; | |
| 1871 if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) | |
| 1872 goto error; | |
| 1873 | |
| 1874 /* do not use SSL cache if disabled or we are not going to verify peer */ | |
| 1875 ssl_no_cache = (SSL_SET_OPTION(primary.sessionid) | |
| 1876 && SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE; | |
| 1877 if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) | |
| 1878 goto error; | |
| 1879 | |
| 1880 /* enable/disable the requested SSL version(s) */ | |
| 1881 if(nss_init_sslver(&sslver, data, conn) != CURLE_OK) | |
| 1882 goto error; | |
| 1883 if(SSL_VersionRangeGetSupported(ssl_variant_stream, | |
| 1884 &sslver_supported) != SECSuccess) | |
| 1885 goto error; | |
| 1886 if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { | |
| 1887 char *sslver_req_str, *sslver_supp_str; | |
| 1888 sslver_req_str = nss_sslver_to_name(sslver.max); | |
| 1889 sslver_supp_str = nss_sslver_to_name(sslver_supported.max); | |
| 1890 if(sslver_req_str && sslver_supp_str) | |
| 1891 infof(data, "Falling back from %s to max supported SSL version (%s)\n", | |
| 1892 sslver_req_str, sslver_supp_str); | |
| 1893 free(sslver_req_str); | |
| 1894 free(sslver_supp_str); | |
| 1895 sslver.max = sslver_supported.max; | |
| 1896 } | |
| 1897 if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) | |
| 1898 goto error; | |
| 1899 | |
| 1900 ssl_cbc_random_iv = !SSL_SET_OPTION(enable_beast); | |
| 1901 #ifdef SSL_CBC_RANDOM_IV | |
| 1902 /* unless the user explicitly asks to allow the protocol vulnerability, we | |
| 1903 use the work-around */ | |
| 1904 if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) | |
| 1905 infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n", | |
| 1906 ssl_cbc_random_iv); | |
| 1907 #else | |
| 1908 if(ssl_cbc_random_iv) | |
| 1909 infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n"); | |
| 1910 #endif | |
| 1911 | |
| 1912 if(SSL_CONN_CONFIG(cipher_list)) { | |
| 1913 if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) { | |
| 1914 result = CURLE_SSL_CIPHER; | |
| 1915 goto error; | |
| 1916 } | |
| 1917 } | |
| 1918 | |
| 1919 if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost)) | |
| 1920 infof(data, "warning: ignoring value of ssl.verifyhost\n"); | |
| 1921 | |
| 1922 /* bypass the default SSL_AuthCertificate() hook in case we do not want to | |
| 1923 * verify peer */ | |
| 1924 if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess) | |
| 1925 goto error; | |
| 1926 | |
| 1927 /* not checked yet */ | |
| 1928 if(SSL_IS_PROXY()) | |
| 1929 data->set.proxy_ssl.certverifyresult = 0; | |
| 1930 else | |
| 1931 data->set.ssl.certverifyresult = 0; | |
| 1932 | |
| 1933 if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess) | |
| 1934 goto error; | |
| 1935 | |
| 1936 if(SSL_HandshakeCallback(model, HandshakeCallback, conn) != SECSuccess) | |
| 1937 goto error; | |
| 1938 | |
| 1939 { | |
| 1940 const CURLcode rv = nss_load_ca_certificates(conn, sockindex); | |
| 1941 if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer)) | |
| 1942 /* not a fatal error because we are not going to verify the peer */ | |
| 1943 infof(data, "warning: CA certificates failed to load\n"); | |
| 1944 else if(rv) { | |
| 1945 result = rv; | |
| 1946 goto error; | |
| 1947 } | |
| 1948 } | |
| 1949 | |
| 1950 if(SSL_SET_OPTION(CRLfile)) { | |
| 1951 const CURLcode rv = nss_load_crl(SSL_SET_OPTION(CRLfile)); | |
| 1952 if(rv) { | |
| 1953 result = rv; | |
| 1954 goto error; | |
| 1955 } | |
| 1956 infof(data, " CRLfile: %s\n", SSL_SET_OPTION(CRLfile)); | |
| 1957 } | |
| 1958 | |
| 1959 if(SSL_SET_OPTION(cert)) { | |
| 1960 char *nickname = dup_nickname(data, SSL_SET_OPTION(cert)); | |
| 1961 if(nickname) { | |
| 1962 /* we are not going to use libnsspem.so to read the client cert */ | |
| 1963 BACKEND->obj_clicert = NULL; | |
| 1964 } | |
| 1965 else { | |
| 1966 CURLcode rv = cert_stuff(conn, sockindex, SSL_SET_OPTION(cert), | |
| 1967 SSL_SET_OPTION(key)); | |
| 1968 if(rv) { | |
| 1969 /* failf() is already done in cert_stuff() */ | |
| 1970 result = rv; | |
| 1971 goto error; | |
| 1972 } | |
| 1973 } | |
| 1974 | |
| 1975 /* store the nickname for SelectClientCert() called during handshake */ | |
| 1976 BACKEND->client_nickname = nickname; | |
| 1977 } | |
| 1978 else | |
| 1979 BACKEND->client_nickname = NULL; | |
| 1980 | |
| 1981 if(SSL_GetClientAuthDataHook(model, SelectClientCert, | |
| 1982 (void *)connssl) != SECSuccess) { | |
| 1983 result = CURLE_SSL_CERTPROBLEM; | |
| 1984 goto error; | |
| 1985 } | |
| 1986 | |
| 1987 if(conn->proxy_ssl[sockindex].use) { | |
| 1988 DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); | |
| 1989 DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL); | |
| 1990 nspr_io = conn->proxy_ssl[sockindex].backend->handle; | |
| 1991 second_layer = TRUE; | |
| 1992 } | |
| 1993 else { | |
| 1994 /* wrap OS file descriptor by NSPR's file descriptor abstraction */ | |
| 1995 nspr_io = PR_ImportTCPSocket(sockfd); | |
| 1996 if(!nspr_io) | |
| 1997 goto error; | |
| 1998 } | |
| 1999 | |
| 2000 /* create our own NSPR I/O layer */ | |
| 2001 nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); | |
| 2002 if(!nspr_io_stub) { | |
| 2003 if(!second_layer) | |
| 2004 PR_Close(nspr_io); | |
| 2005 goto error; | |
| 2006 } | |
| 2007 | |
| 2008 /* make the per-connection data accessible from NSPR I/O callbacks */ | |
| 2009 nspr_io_stub->secret = (void *)connssl; | |
| 2010 | |
| 2011 /* push our new layer to the NSPR I/O stack */ | |
| 2012 if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { | |
| 2013 if(!second_layer) | |
| 2014 PR_Close(nspr_io); | |
| 2015 PR_Close(nspr_io_stub); | |
| 2016 goto error; | |
| 2017 } | |
| 2018 | |
| 2019 /* import our model socket onto the current I/O stack */ | |
| 2020 BACKEND->handle = SSL_ImportFD(model, nspr_io); | |
| 2021 if(!BACKEND->handle) { | |
| 2022 if(!second_layer) | |
| 2023 PR_Close(nspr_io); | |
| 2024 goto error; | |
| 2025 } | |
| 2026 | |
| 2027 PR_Close(model); /* We don't need this any more */ | |
| 2028 model = NULL; | |
| 2029 | |
| 2030 /* This is the password associated with the cert that we're using */ | |
| 2031 if(SSL_SET_OPTION(key_passwd)) { | |
| 2032 SSL_SetPKCS11PinArg(BACKEND->handle, SSL_SET_OPTION(key_passwd)); | |
| 2033 } | |
| 2034 | |
| 2035 #ifdef SSL_ENABLE_OCSP_STAPLING | |
| 2036 if(SSL_CONN_CONFIG(verifystatus)) { | |
| 2037 if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) | |
| 2038 != SECSuccess) | |
| 2039 goto error; | |
| 2040 } | |
| 2041 #endif | |
| 2042 | |
| 2043 #ifdef SSL_ENABLE_NPN | |
| 2044 if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn | |
| 2045 ? PR_TRUE : PR_FALSE) != SECSuccess) | |
| 2046 goto error; | |
| 2047 #endif | |
| 2048 | |
| 2049 #ifdef SSL_ENABLE_ALPN | |
| 2050 if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn | |
| 2051 ? PR_TRUE : PR_FALSE) != SECSuccess) | |
| 2052 goto error; | |
| 2053 #endif | |
| 2054 | |
| 2055 #if NSSVERNUM >= 0x030f04 /* 3.15.4 */ | |
| 2056 if(data->set.ssl.falsestart) { | |
| 2057 if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_FALSE_START, PR_TRUE) | |
| 2058 != SECSuccess) | |
| 2059 goto error; | |
| 2060 | |
| 2061 if(SSL_SetCanFalseStartCallback(BACKEND->handle, CanFalseStartCallback, | |
| 2062 conn) != SECSuccess) | |
| 2063 goto error; | |
| 2064 } | |
| 2065 #endif | |
| 2066 | |
| 2067 #if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) | |
| 2068 if(conn->bits.tls_enable_npn || conn->bits.tls_enable_alpn) { | |
| 2069 int cur = 0; | |
| 2070 unsigned char protocols[128]; | |
| 2071 | |
| 2072 #ifdef USE_NGHTTP2 | |
| 2073 if(data->set.httpversion >= CURL_HTTP_VERSION_2 && | |
| 2074 (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)) { | |
| 2075 protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN; | |
| 2076 memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID, | |
| 2077 NGHTTP2_PROTO_VERSION_ID_LEN); | |
| 2078 cur += NGHTTP2_PROTO_VERSION_ID_LEN; | |
| 2079 } | |
| 2080 #endif | |
| 2081 protocols[cur++] = ALPN_HTTP_1_1_LENGTH; | |
| 2082 memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); | |
| 2083 cur += ALPN_HTTP_1_1_LENGTH; | |
| 2084 | |
| 2085 if(SSL_SetNextProtoNego(BACKEND->handle, protocols, cur) != SECSuccess) | |
| 2086 goto error; | |
| 2087 } | |
| 2088 #endif | |
| 2089 | |
| 2090 | |
| 2091 /* Force handshake on next I/O */ | |
| 2092 if(SSL_ResetHandshake(BACKEND->handle, /* asServer */ PR_FALSE) | |
| 2093 != SECSuccess) | |
| 2094 goto error; | |
| 2095 | |
| 2096 /* propagate hostname to the TLS layer */ | |
| 2097 if(SSL_SetURL(BACKEND->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name : | |
| 2098 conn->host.name) != SECSuccess) | |
| 2099 goto error; | |
| 2100 | |
| 2101 /* prevent NSS from re-using the session for a different hostname */ | |
| 2102 if(SSL_SetSockPeerID(BACKEND->handle, SSL_IS_PROXY() ? | |
| 2103 conn->http_proxy.host.name : conn->host.name) | |
| 2104 != SECSuccess) | |
| 2105 goto error; | |
| 2106 | |
| 2107 return CURLE_OK; | |
| 2108 | |
| 2109 error: | |
| 2110 if(model) | |
| 2111 PR_Close(model); | |
| 2112 | |
| 2113 return nss_fail_connect(connssl, data, result); | |
| 2114 } | |
| 2115 | |
| 2116 static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) | |
| 2117 { | |
| 2118 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 2119 struct Curl_easy *data = conn->data; | |
| 2120 CURLcode result = CURLE_SSL_CONNECT_ERROR; | |
| 2121 PRUint32 timeout; | |
| 2122 long * const certverifyresult = SSL_IS_PROXY() ? | |
| 2123 &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; | |
| 2124 const char * const pinnedpubkey = SSL_IS_PROXY() ? | |
| 2125 data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : | |
| 2126 data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; | |
| 2127 | |
| 2128 | |
| 2129 /* check timeout situation */ | |
| 2130 const time_t time_left = Curl_timeleft(data, NULL, TRUE); | |
| 2131 if(time_left < 0) { | |
| 2132 failf(data, "timed out before SSL handshake"); | |
| 2133 result = CURLE_OPERATION_TIMEDOUT; | |
| 2134 goto error; | |
| 2135 } | |
| 2136 | |
| 2137 /* Force the handshake now */ | |
| 2138 timeout = PR_MillisecondsToInterval((PRUint32) time_left); | |
| 2139 if(SSL_ForceHandshakeWithTimeout(BACKEND->handle, timeout) != SECSuccess) { | |
| 2140 if(PR_GetError() == PR_WOULD_BLOCK_ERROR) | |
| 2141 /* blocking direction is updated by nss_update_connecting_state() */ | |
| 2142 return CURLE_AGAIN; | |
| 2143 else if(*certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) | |
| 2144 result = CURLE_PEER_FAILED_VERIFICATION; | |
| 2145 else if(*certverifyresult != 0) | |
| 2146 result = CURLE_PEER_FAILED_VERIFICATION; | |
| 2147 goto error; | |
| 2148 } | |
| 2149 | |
| 2150 result = display_conn_info(conn, BACKEND->handle); | |
| 2151 if(result) | |
| 2152 goto error; | |
| 2153 | |
| 2154 if(SSL_SET_OPTION(issuercert)) { | |
| 2155 SECStatus ret = SECFailure; | |
| 2156 char *nickname = dup_nickname(data, SSL_SET_OPTION(issuercert)); | |
| 2157 if(nickname) { | |
| 2158 /* we support only nicknames in case of issuercert for now */ | |
| 2159 ret = check_issuer_cert(BACKEND->handle, nickname); | |
| 2160 free(nickname); | |
| 2161 } | |
| 2162 | |
| 2163 if(SECFailure == ret) { | |
| 2164 infof(data, "SSL certificate issuer check failed\n"); | |
| 2165 result = CURLE_SSL_ISSUER_ERROR; | |
| 2166 goto error; | |
| 2167 } | |
| 2168 else { | |
| 2169 infof(data, "SSL certificate issuer check ok\n"); | |
| 2170 } | |
| 2171 } | |
| 2172 | |
| 2173 result = cmp_peer_pubkey(connssl, pinnedpubkey); | |
| 2174 if(result) | |
| 2175 /* status already printed */ | |
| 2176 goto error; | |
| 2177 | |
| 2178 return CURLE_OK; | |
| 2179 | |
| 2180 error: | |
| 2181 return nss_fail_connect(connssl, data, result); | |
| 2182 } | |
| 2183 | |
| 2184 static CURLcode nss_connect_common(struct connectdata *conn, int sockindex, | |
| 2185 bool *done) | |
| 2186 { | |
| 2187 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 2188 struct Curl_easy *data = conn->data; | |
| 2189 const bool blocking = (done == NULL); | |
| 2190 CURLcode result; | |
| 2191 | |
| 2192 if(connssl->state == ssl_connection_complete) { | |
| 2193 if(!blocking) | |
| 2194 *done = TRUE; | |
| 2195 return CURLE_OK; | |
| 2196 } | |
| 2197 | |
| 2198 if(connssl->connecting_state == ssl_connect_1) { | |
| 2199 result = nss_setup_connect(conn, sockindex); | |
| 2200 if(result) | |
| 2201 /* we do not expect CURLE_AGAIN from nss_setup_connect() */ | |
| 2202 return result; | |
| 2203 | |
| 2204 connssl->connecting_state = ssl_connect_2; | |
| 2205 } | |
| 2206 | |
| 2207 /* enable/disable blocking mode before handshake */ | |
| 2208 result = nss_set_blocking(connssl, data, blocking); | |
| 2209 if(result) | |
| 2210 return result; | |
| 2211 | |
| 2212 result = nss_do_connect(conn, sockindex); | |
| 2213 switch(result) { | |
| 2214 case CURLE_OK: | |
| 2215 break; | |
| 2216 case CURLE_AGAIN: | |
| 2217 if(!blocking) | |
| 2218 /* CURLE_AGAIN in non-blocking mode is not an error */ | |
| 2219 return CURLE_OK; | |
| 2220 /* FALLTHROUGH */ | |
| 2221 default: | |
| 2222 return result; | |
| 2223 } | |
| 2224 | |
| 2225 if(blocking) { | |
| 2226 /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ | |
| 2227 result = nss_set_blocking(connssl, data, /* blocking */ FALSE); | |
| 2228 if(result) | |
| 2229 return result; | |
| 2230 } | |
| 2231 else | |
| 2232 /* signal completed SSL handshake */ | |
| 2233 *done = TRUE; | |
| 2234 | |
| 2235 connssl->state = ssl_connection_complete; | |
| 2236 conn->recv[sockindex] = nss_recv; | |
| 2237 conn->send[sockindex] = nss_send; | |
| 2238 | |
| 2239 /* ssl_connect_done is never used outside, go back to the initial state */ | |
| 2240 connssl->connecting_state = ssl_connect_1; | |
| 2241 | |
| 2242 return CURLE_OK; | |
| 2243 } | |
| 2244 | |
| 2245 static CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) | |
| 2246 { | |
| 2247 return nss_connect_common(conn, sockindex, /* blocking */ NULL); | |
| 2248 } | |
| 2249 | |
| 2250 static CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn, | |
| 2251 int sockindex, bool *done) | |
| 2252 { | |
| 2253 return nss_connect_common(conn, sockindex, done); | |
| 2254 } | |
| 2255 | |
| 2256 static ssize_t nss_send(struct connectdata *conn, /* connection data */ | |
| 2257 int sockindex, /* socketindex */ | |
| 2258 const void *mem, /* send this data */ | |
| 2259 size_t len, /* amount to write */ | |
| 2260 CURLcode *curlcode) | |
| 2261 { | |
| 2262 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 2263 ssize_t rc; | |
| 2264 | |
| 2265 /* The SelectClientCert() hook uses this for infof() and failf() but the | |
| 2266 handle stored in nss_setup_connect() could have already been freed. */ | |
| 2267 BACKEND->data = conn->data; | |
| 2268 | |
| 2269 rc = PR_Send(BACKEND->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); | |
| 2270 if(rc < 0) { | |
| 2271 PRInt32 err = PR_GetError(); | |
| 2272 if(err == PR_WOULD_BLOCK_ERROR) | |
| 2273 *curlcode = CURLE_AGAIN; | |
| 2274 else { | |
| 2275 /* print the error number and error string */ | |
| 2276 const char *err_name = nss_error_to_name(err); | |
| 2277 infof(conn->data, "SSL write: error %d (%s)\n", err, err_name); | |
| 2278 | |
| 2279 /* print a human-readable message describing the error if available */ | |
| 2280 nss_print_error_message(conn->data, err); | |
| 2281 | |
| 2282 *curlcode = (is_cc_error(err)) | |
| 2283 ? CURLE_SSL_CERTPROBLEM | |
| 2284 : CURLE_SEND_ERROR; | |
| 2285 } | |
| 2286 | |
| 2287 return -1; | |
| 2288 } | |
| 2289 | |
| 2290 return rc; /* number of bytes */ | |
| 2291 } | |
| 2292 | |
| 2293 static ssize_t nss_recv(struct connectdata *conn, /* connection data */ | |
| 2294 int sockindex, /* socketindex */ | |
| 2295 char *buf, /* store read data here */ | |
| 2296 size_t buffersize, /* max amount to read */ | |
| 2297 CURLcode *curlcode) | |
| 2298 { | |
| 2299 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 2300 ssize_t nread; | |
| 2301 | |
| 2302 /* The SelectClientCert() hook uses this for infof() and failf() but the | |
| 2303 handle stored in nss_setup_connect() could have already been freed. */ | |
| 2304 BACKEND->data = conn->data; | |
| 2305 | |
| 2306 nread = PR_Recv(BACKEND->handle, buf, (int)buffersize, 0, | |
| 2307 PR_INTERVAL_NO_WAIT); | |
| 2308 if(nread < 0) { | |
| 2309 /* failed SSL read */ | |
| 2310 PRInt32 err = PR_GetError(); | |
| 2311 | |
| 2312 if(err == PR_WOULD_BLOCK_ERROR) | |
| 2313 *curlcode = CURLE_AGAIN; | |
| 2314 else { | |
| 2315 /* print the error number and error string */ | |
| 2316 const char *err_name = nss_error_to_name(err); | |
| 2317 infof(conn->data, "SSL read: errno %d (%s)\n", err, err_name); | |
| 2318 | |
| 2319 /* print a human-readable message describing the error if available */ | |
| 2320 nss_print_error_message(conn->data, err); | |
| 2321 | |
| 2322 *curlcode = (is_cc_error(err)) | |
| 2323 ? CURLE_SSL_CERTPROBLEM | |
| 2324 : CURLE_RECV_ERROR; | |
| 2325 } | |
| 2326 | |
| 2327 return -1; | |
| 2328 } | |
| 2329 | |
| 2330 return nread; | |
| 2331 } | |
| 2332 | |
| 2333 static size_t Curl_nss_version(char *buffer, size_t size) | |
| 2334 { | |
| 2335 return msnprintf(buffer, size, "NSS/%s", NSS_VERSION); | |
| 2336 } | |
| 2337 | |
| 2338 /* data might be NULL */ | |
| 2339 static int Curl_nss_seed(struct Curl_easy *data) | |
| 2340 { | |
| 2341 /* make sure that NSS is initialized */ | |
| 2342 return !!Curl_nss_force_init(data); | |
| 2343 } | |
| 2344 | |
| 2345 /* data might be NULL */ | |
| 2346 static CURLcode Curl_nss_random(struct Curl_easy *data, | |
| 2347 unsigned char *entropy, | |
| 2348 size_t length) | |
| 2349 { | |
| 2350 Curl_nss_seed(data); /* Initiate the seed if not already done */ | |
| 2351 | |
| 2352 if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) | |
| 2353 /* signal a failure */ | |
| 2354 return CURLE_FAILED_INIT; | |
| 2355 | |
| 2356 return CURLE_OK; | |
| 2357 } | |
| 2358 | |
| 2359 static CURLcode Curl_nss_md5sum(unsigned char *tmp, /* input */ | |
| 2360 size_t tmplen, | |
| 2361 unsigned char *md5sum, /* output */ | |
| 2362 size_t md5len) | |
| 2363 { | |
| 2364 PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); | |
| 2365 unsigned int MD5out; | |
| 2366 | |
| 2367 PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen)); | |
| 2368 PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len)); | |
| 2369 PK11_DestroyContext(MD5pw, PR_TRUE); | |
| 2370 | |
| 2371 return CURLE_OK; | |
| 2372 } | |
| 2373 | |
| 2374 static CURLcode Curl_nss_sha256sum(const unsigned char *tmp, /* input */ | |
| 2375 size_t tmplen, | |
| 2376 unsigned char *sha256sum, /* output */ | |
| 2377 size_t sha256len) | |
| 2378 { | |
| 2379 PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); | |
| 2380 unsigned int SHA256out; | |
| 2381 | |
| 2382 PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); | |
| 2383 PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); | |
| 2384 PK11_DestroyContext(SHA256pw, PR_TRUE); | |
| 2385 | |
| 2386 return CURLE_OK; | |
| 2387 } | |
| 2388 | |
| 2389 static bool Curl_nss_cert_status_request(void) | |
| 2390 { | |
| 2391 #ifdef SSL_ENABLE_OCSP_STAPLING | |
| 2392 return TRUE; | |
| 2393 #else | |
| 2394 return FALSE; | |
| 2395 #endif | |
| 2396 } | |
| 2397 | |
| 2398 static bool Curl_nss_false_start(void) | |
| 2399 { | |
| 2400 #if NSSVERNUM >= 0x030f04 /* 3.15.4 */ | |
| 2401 return TRUE; | |
| 2402 #else | |
| 2403 return FALSE; | |
| 2404 #endif | |
| 2405 } | |
| 2406 | |
| 2407 static void *Curl_nss_get_internals(struct ssl_connect_data *connssl, | |
| 2408 CURLINFO info UNUSED_PARAM) | |
| 2409 { | |
| 2410 (void)info; | |
| 2411 return BACKEND->handle; | |
| 2412 } | |
| 2413 | |
| 2414 const struct Curl_ssl Curl_ssl_nss = { | |
| 2415 { CURLSSLBACKEND_NSS, "nss" }, /* info */ | |
| 2416 | |
| 2417 SSLSUPP_CA_PATH | | |
| 2418 SSLSUPP_CERTINFO | | |
| 2419 SSLSUPP_PINNEDPUBKEY | | |
| 2420 SSLSUPP_HTTPS_PROXY, | |
| 2421 | |
| 2422 sizeof(struct ssl_backend_data), | |
| 2423 | |
| 2424 Curl_nss_init, /* init */ | |
| 2425 Curl_nss_cleanup, /* cleanup */ | |
| 2426 Curl_nss_version, /* version */ | |
| 2427 Curl_nss_check_cxn, /* check_cxn */ | |
| 2428 /* NSS has no shutdown function provided and thus always fail */ | |
| 2429 Curl_none_shutdown, /* shutdown */ | |
| 2430 Curl_none_data_pending, /* data_pending */ | |
| 2431 Curl_nss_random, /* random */ | |
| 2432 Curl_nss_cert_status_request, /* cert_status_request */ | |
| 2433 Curl_nss_connect, /* connect */ | |
| 2434 Curl_nss_connect_nonblocking, /* connect_nonblocking */ | |
| 2435 Curl_nss_get_internals, /* get_internals */ | |
| 2436 Curl_nss_close, /* close_one */ | |
| 2437 Curl_none_close_all, /* close_all */ | |
| 2438 /* NSS has its own session ID cache */ | |
| 2439 Curl_none_session_free, /* session_free */ | |
| 2440 Curl_none_set_engine, /* set_engine */ | |
| 2441 Curl_none_set_engine_default, /* set_engine_default */ | |
| 2442 Curl_none_engines_list, /* engines_list */ | |
| 2443 Curl_nss_false_start, /* false_start */ | |
| 2444 Curl_nss_md5sum, /* md5sum */ | |
| 2445 Curl_nss_sha256sum /* sha256sum */ | |
| 2446 }; | |
| 2447 | |
| 2448 #endif /* USE_NSS */ |
