/* Provenance: curl/curl nss_setup_connect (fixed). * repo: curl/curl * commit: f18af4f874cecab82a9797e8c7541e0990c7a64c * parent: 7e92d12b4e6911f424678a133b19de670e183a59 * commit_url: https://github.com/curl/curl/commit/f18af4f874cecab82a9797e8c7541e0990c7a64c * cve: CVE-2022-27782 * license: curl * function: nss_setup_connect * relpath: lib/vtls/nss.c * provenance: human * mechanism: other */ static CURLcode nss_setup_connect(struct Curl_easy *data, struct connectdata *conn, int sockindex) { PRFileDesc *model = NULL; PRFileDesc *nspr_io = NULL; PRFileDesc *nspr_io_stub = NULL; PRBool ssl_no_cache; PRBool ssl_cbc_random_iv; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; CURLcode result; bool second_layer = FALSE; SSLVersionRange sslver_supported; SSLVersionRange sslver = { SSL_LIBRARY_VERSION_TLS_1_0, /* min */ #ifdef SSL_LIBRARY_VERSION_TLS_1_3 SSL_LIBRARY_VERSION_TLS_1_3 /* max */ #elif defined SSL_LIBRARY_VERSION_TLS_1_2 SSL_LIBRARY_VERSION_TLS_1_2 #elif defined SSL_LIBRARY_VERSION_TLS_1_1 SSL_LIBRARY_VERSION_TLS_1_1 #else SSL_LIBRARY_VERSION_TLS_1_0 #endif }; char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); if(!snihost) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } DEBUGASSERT(backend); backend->data = data; /* list of all NSS objects we need to destroy in nss_do_close() */ Curl_llist_init(&backend->obj_list, nss_destroy_object); PR_Lock(nss_initlock); result = nss_setup(data); if(result) { PR_Unlock(nss_initlock); goto error; } PK11_SetPasswordFunc(nss_get_password); result = nss_load_module(&pem_module, pem_library, "PEM"); PR_Unlock(nss_initlock); if(result == CURLE_FAILED_INIT) infof(data, "WARNING: failed to load NSS PEM library %s. Using " "OpenSSL PEM certificates will not work.", pem_library); else if(result) goto error; result = CURLE_SSL_CONNECT_ERROR; model = PR_NewTCPSocket(); if(!model) goto error; model = SSL_ImportFD(NULL, model); if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) goto error; /* do not use SSL cache if disabled or we are not going to verify peer */ ssl_no_cache = (SSL_SET_OPTION(primary.sessionid) && SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE; if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) goto error; /* enable/disable the requested SSL version(s) */ if(nss_init_sslver(&sslver, data, conn) != CURLE_OK) goto error; if(SSL_VersionRangeGetSupported(ssl_variant_stream, &sslver_supported) != SECSuccess) goto error; if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { char *sslver_req_str, *sslver_supp_str; sslver_req_str = nss_sslver_to_name(sslver.max); sslver_supp_str = nss_sslver_to_name(sslver_supported.max); if(sslver_req_str && sslver_supp_str) infof(data, "Falling back from %s to max supported SSL version (%s)", sslver_req_str, sslver_supp_str); free(sslver_req_str); free(sslver_supp_str); sslver.max = sslver_supported.max; } if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) goto error; ssl_cbc_random_iv = !SSL_SET_OPTION(enable_beast); #ifdef SSL_CBC_RANDOM_IV /* unless the user explicitly asks to allow the protocol vulnerability, we use the work-around */ if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", ssl_cbc_random_iv); #else if(ssl_cbc_random_iv) infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); #endif if(SSL_CONN_CONFIG(cipher_list)) { if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) { result = CURLE_SSL_CIPHER; goto error; } } if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost)) infof(data, "WARNING: ignoring value of ssl.verifyhost"); /* bypass the default SSL_AuthCertificate() hook in case we do not want to * verify peer */ if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, data) != SECSuccess) goto error; /* not checked yet */ SSL_SET_OPTION_LVALUE(certverifyresult) = 0; if(SSL_BadCertHook(model, BadCertHandler, data) != SECSuccess) goto error; if(SSL_HandshakeCallback(model, HandshakeCallback, data) != SECSuccess) goto error; { const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex); if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer)) /* not a fatal error because we are not going to verify the peer */ infof(data, "WARNING: CA certificates failed to load"); else if(rv) { result = rv; goto error; } } if(SSL_SET_OPTION(primary.CRLfile)) { const CURLcode rv = nss_load_crl(SSL_SET_OPTION(primary.CRLfile)); if(rv) { result = rv; goto error; } infof(data, " CRLfile: %s", SSL_SET_OPTION(primary.CRLfile)); } if(SSL_SET_OPTION(primary.clientcert)) { char *nickname = dup_nickname(data, SSL_SET_OPTION(primary.clientcert)); if(nickname) { /* we are not going to use libnsspem.so to read the client cert */ backend->obj_clicert = NULL; } else { CURLcode rv = cert_stuff(data, conn, sockindex, SSL_SET_OPTION(primary.clientcert), SSL_SET_OPTION(key)); if(rv) { /* failf() is already done in cert_stuff() */ result = rv; goto error; } } /* store the nickname for SelectClientCert() called during handshake */ backend->client_nickname = nickname; } else backend->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { result = CURLE_SSL_CERTPROBLEM; goto error; } #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { struct ssl_backend_data *proxy_backend; proxy_backend = conn->proxy_ssl[sockindex].backend; DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); DEBUGASSERT(proxy_backend); DEBUGASSERT(proxy_backend->handle); nspr_io = proxy_backend->handle; second_layer = TRUE; } #endif else { /* wrap OS file descriptor by NSPR's file descriptor abstraction */ nspr_io = PR_ImportTCPSocket(sockfd); if(!nspr_io) goto error; } /* create our own NSPR I/O layer */ nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); if(!nspr_io_stub) { if(!second_layer) PR_Close(nspr_io); goto error; } /* make the per-connection data accessible from NSPR I/O callbacks */ nspr_io_stub->secret = (void *)connssl; /* push our new layer to the NSPR I/O stack */ if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { if(!second_layer) PR_Close(nspr_io); PR_Close(nspr_io_stub); goto error; } /* import our model socket onto the current I/O stack */ backend->handle = SSL_ImportFD(model, nspr_io); if(!backend->handle) { if(!second_layer) PR_Close(nspr_io); goto error; } PR_Close(model); /* We don't need this any more */ model = NULL; /* This is the password associated with the cert that we're using */ if(SSL_SET_OPTION(key_passwd)) { SSL_SetPKCS11PinArg(backend->handle, SSL_SET_OPTION(key_passwd)); } #ifdef SSL_ENABLE_OCSP_STAPLING if(SSL_CONN_CONFIG(verifystatus)) { if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) != SECSuccess) goto error; } #endif #ifdef SSL_ENABLE_NPN if(SSL_OptionSet(backend->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn ? PR_TRUE : PR_FALSE) != SECSuccess) goto error; #endif #ifdef SSL_ENABLE_ALPN if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn ? PR_TRUE : PR_FALSE) != SECSuccess) goto error; #endif #if NSSVERNUM >= 0x030f04 /* 3.15.4 */ if(data->set.ssl.falsestart) { if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) != SECSuccess) goto error; if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, data) != SECSuccess) goto error; } #endif #if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) if(conn->bits.tls_enable_npn || conn->bits.tls_enable_alpn) { int cur = 0; unsigned char protocols[128]; #ifdef USE_HTTP2 if(data->state.httpwant >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif ) { protocols[cur++] = ALPN_H2_LENGTH; memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); cur += ALPN_H2_LENGTH; } #endif protocols[cur++] = ALPN_HTTP_1_1_LENGTH; memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); cur += ALPN_HTTP_1_1_LENGTH; if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess) goto error; } #endif /* Force handshake on next I/O */ if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) != SECSuccess) goto error; /* propagate hostname to the TLS layer */ if(SSL_SetURL(backend->handle, snihost) != SECSuccess) goto error; /* prevent NSS from re-using the session for a different hostname */ if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) goto error; return CURLE_OK; error: if(model) PR_Close(model); return nss_fail_connect(connssl, data, result); }