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