Annotation of embedaddon/curl/lib/vtls/nss.c, revision 1.1
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>