static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond) { struct scd *conn = data; /* Right now we don't have any verification functionality for NSS. */ if (conn->verify) { conn->func(conn->data, 1, NULL, cond); if (source >= 0) { closesocket(source); } g_free(conn->hostname); g_free(conn); return FALSE; } if (source == -1) { goto ssl_connected_failure; } /* Until we find out how to handle non-blocking I/O with NSS... */ sock_make_blocking(conn->fd); conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); if (!conn->prfd) { goto ssl_connected_failure; } SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); SSL_BadCertHook(conn->prfd, (SSLBadCertHandler) nss_bad_cert, NULL); SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate) nss_auth_cert, (void *) CERT_GetDefaultCertDB()); SSL_SetURL(conn->prfd, conn->hostname); SSL_ResetHandshake(conn->prfd, PR_FALSE); if (SSL_ForceHandshake(conn->prfd)) { goto ssl_connected_failure; } conn->established = TRUE; conn->func(conn->data, 0, conn, cond); return FALSE; ssl_connected_failure: conn->func(conn->data, 0, NULL, cond); if (conn->prfd) { PR_Close(conn->prfd); } else if (source >= 0) { /* proxy_disconnect() would be redundant here */ closesocket(source); } g_free(conn->hostname); g_free(conn); return FALSE; }
SslSocket::SslSocket(const std::string& certName, bool clientAuth) : nssSocket(0), certname(certName), prototype(0), hostnameVerification(true) { //configure prototype socket: prototype = SSL_ImportFD(0, PR_NewTCPSocket()); if (clientAuth) { NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE)); NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE)); } }
nsresult SetupTLS(Connection *aConn, PRFileDesc *aModelSocket) { PRFileDesc *sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket); if (!sslSocket) { PrintPRError("SSL_ImportFD failed"); return NS_ERROR_FAILURE; } aConn->mSocket = sslSocket; SSL_OptionSet(sslSocket, SSL_SECURITY, true); SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false); SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true); SSL_ResetHandshake(sslSocket, /* asServer */ 1); return NS_OK; }
struct tls_connection * tls_connection_init(void *tls_ctx) { struct tls_connection *conn; conn = os_zalloc(sizeof (*conn)); if (conn == NULL) return NULL; conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io); if (conn->fd == NULL) { os_free(conn); return NULL; } conn->fd->secret = (void *) conn; conn->fd = SSL_ImportFD(NULL, conn->fd); if (conn->fd == NULL) { os_free(conn); return NULL; } if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess || SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess || SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess || SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) != SECSuccess) { wpa_printf(MSG_ERROR, "NSS: Failed to set options"); PR_Close(conn->fd); os_free(conn); return NULL; } SSL_ResetHandshake(conn->fd, PR_FALSE); return conn; }
int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, bool clientAuth) const { //configure prototype socket: prototype = SSL_ImportFD(0, PR_NewTCPSocket()); if (clientAuth) { NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE)); NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE)); } //get certificate and key (is this the correct way?) CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(certName.c_str()), 0); if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << certName << "'")); SECKEYPrivateKey *key = PK11_FindKeyByAnyCert(cert, 0); if (!key) throw Exception(QPID_MSG("Failed to retrieve private key from certificate")); NSS_CHECK(SSL_ConfigSecureServer(prototype, cert, key, NSS_FindCertKEAType(cert))); SECKEY_DestroyPrivateKey(key); CERT_DestroyCertificate(cert); //bind and listen const int& socket = impl->fd; int yes=1; QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons(port); name.sin_addr.s_addr = 0; if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); if (::listen(socket, backlog) < 0) throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); socklen_t namelen = sizeof(name); if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) throw QPID_POSIX_ERROR(errno); return ntohs(name.sin_port); }
static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) { struct scd *conn = data; if( source == -1 ) goto ssl_connected_failure; /* Until we find out how to handle non-blocking I/O with NSS... */ sock_make_blocking( conn->fd ); conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); SSL_ResetHandshake(conn->prfd, PR_FALSE); if (SSL_ForceHandshake(conn->prfd)) { goto ssl_connected_failure; } conn->established = TRUE; conn->func( conn->data, conn, cond ); return FALSE; ssl_connected_failure: conn->func( conn->data, NULL, cond ); PR_Close( conn -> prfd ); if( source >= 0 ) closesocket( source ); g_free( conn ); return FALSE; }
bool TransportLayerDtls::SetupAlpn(PRFileDesc* ssl_fd) const { if (alpn_allowed_.empty()) { return true; } SECStatus rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_NPN, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable NPN"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_ALPN, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't enable ALPN"); return false; } unsigned char buf[MAX_ALPN_LENGTH]; size_t offset = 0; for (auto tag = alpn_allowed_.begin(); tag != alpn_allowed_.end(); ++tag) { if ((offset + 1 + tag->length()) >= sizeof(buf)) { MOZ_MTLOG(ML_ERROR, "ALPN too long"); return false; } buf[offset++] = tag->length(); memcpy(buf + offset, tag->c_str(), tag->length()); offset += tag->length(); } rv = SSL_SetNextProtoNego(ssl_fd, buf, offset); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set ALPN string"); return false; } return true; }
/** Create socket. If ssl is >0, socket is ssl enabled. @param ssl Enable ssl (Client, SSL2+3, no TLS, compatible hello) if PR_TRUE, otherwise no. @param ipv6 New socket will be IPv4 if this value is 0, otherwise it will be ipv6 @return NULL on error, otherwise socket. */ static PRFileDesc *create_socket(int ssl,int ipv6) { PRFileDesc *res_socket; res_socket=PR_OpenTCPSocket((ipv6?PR_AF_INET6:PR_AF_INET)); if (res_socket==NULL) { print_nspr_error(); return NULL; } if (!ssl) return res_socket; if (!(res_socket=SSL_ImportFD(NULL,res_socket))) { print_nspr_error(); return NULL; } if ((SSL_OptionSet(res_socket,SSL_SECURITY,ssl)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_SERVER,PR_FALSE)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_HANDSHAKE_AS_CLIENT,PR_TRUE)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_ENABLE_SSL2,ssl)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_ENABLE_SSL3,ssl)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_ENABLE_TLS,PR_FALSE)!=SECSuccess) || (SSL_OptionSet(res_socket,SSL_V2_COMPATIBLE_HELLO,ssl)!=SECSuccess) || (SSL_SetPKCS11PinArg(res_socket,NULL)==-1) || (SSL_AuthCertificateHook(res_socket,SSL_AuthCertificate,CERT_GetDefaultCertDB())!=SECSuccess) || (SSL_BadCertHook(res_socket,nss_bad_cert_hook,NULL)!=SECSuccess)) { print_nspr_error(); if (PR_Close(res_socket)!=PR_SUCCESS) { print_nspr_error(); } return NULL; } return res_socket; }
/** * Performs SSL handshake on a TCP socket. */ PRFileDesc *Connection::secureSocket(const std::string &certDBPasswd, const std::string &certNickName, bool alwaysTrustServerCert, PRFileDesc *rawSocket) { bool upgradeExisting = false; // Use object's socket if none passed if (rawSocket == static_cast<PRFileDesc *>(NULL)) { rawSocket = socket; upgradeExisting = true; } PRFileDesc *sslSocket = SSL_ImportFD(NULL, rawSocket); if (static_cast<PRFileDesc *>(NULL) != sslSocket) { SECStatus secStatus = SECSuccess; const char *sslMethodName; // In case there was any communication on the socket // before the upgrade we should call a reset if (upgradeExisting) { sslMethodName = "SSL_ResetHandshake"; secStatus = SSL_ResetHandshake(sslSocket, false); } if (SECSuccess == secStatus) { sslMethodName = "SSL_OptionSet"; secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE); } if (SECSuccess == secStatus) { secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); if (SECSuccess == secStatus && alwaysTrustServerCert) { sslMethodName = "SSL_AuthCertificateHook"; secStatus = SSL_AuthCertificateHook(sslSocket, acceptAnyCert, NULL); } if (SECSuccess == secStatus && certDBPasswd.size() > 0) { sslMethodName = "SSL_SetPKCS11PinArg"; secStatus = SSL_SetPKCS11PinArg(sslSocket, certdbpasswd); } if (SECSuccess == secStatus) { if (certNickName.size() > 0) { certnickname = strdup(certNickName.c_str()); } sslMethodName = "SSL_GetClientAuthDataHook"; secStatus = SSL_GetClientAuthDataHook(sslSocket, NSS_GetClientAuthData, static_cast<void *>(certnickname)); } if (SECSuccess == secStatus) { sslMethodName = "SSL_HandshakeCallback"; secStatus = SSL_HandshakeCallback(sslSocket, (SSLHandshakeCallback)finishedHandshakeHandler, NULL); } } if (SECSuccess != secStatus) { PRErrorCode error = PR_GetError(); PR_Close(sslSocket); throw NSPRException("Connection::secureSocket", sslMethodName, error); } } else { PRErrorCode error = PR_GetError(); PR_Close(rawSocket); throw NSPRException("Connection::secureSocket", "SSL_ImportFD", error); } socket = sslSocket; return sslSocket; }
// TODO: make sure this is called from STS. Otherwise // we have thread safety issues bool TransportLayerDtls::Setup() { CheckThread(); SECStatus rv; if (!downward_) { MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless"); return false; } nspr_io_adapter_ = new TransportLayerNSPRAdapter(downward_); if (!identity_) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity"); return false; } if (verification_mode_ == VERIFY_UNSET) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without specifying a verification mode"); return false; } if (transport_layer_identity == PR_INVALID_IO_LAYER) { transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); } ScopedPRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity, &TransportLayerMethods)); MOZ_ASSERT(pr_fd != nullptr); if (!pr_fd) return false; pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get()); ScopedPRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd)); MOZ_ASSERT(ssl_fd != nullptr); // This should never happen if (!ssl_fd) { return false; } pr_fd.forget(); // ownership transfered to ssl_fd; if (role_ == CLIENT) { MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as client"); rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook, this); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } } else { MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as server"); // Server side rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), identity_->privkey(), kt_rsa); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } // Insist on a certificate from the client rv = SSL_OptionSet(ssl_fd, SSL_REQUEST_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't request certificate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REQUIRE_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't require certificate"); return false; } } // Require TLS 1.1 or 1.2. Perhaps some day in the future we will allow TLS // 1.0 for stream modes. SSLVersionRange version_range = { SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_2 }; rv = SSL_VersionRangeSet(ssl_fd, &version_range); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_DEFLATE, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable false start"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_LOCKS, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable locks"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse"); return false; } if (!SetupCipherSuites(ssl_fd)) { return false; } // Certificate validation rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook, reinterpret_cast<void *>(this)); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook"); return false; } // Now start the handshake rv = SSL_ResetHandshake(ssl_fd, role_ == SERVER ? PR_TRUE : PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake"); return false; } ssl_fd_ = ssl_fd.forget(); // Finally, get ready to receive data downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange); downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived); if (downward_->state() == TS_OPEN) { Handshake(); } return true; }
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; PRBool ssl2, ssl3, tlsv1; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; char *certDir = NULL; int curlerr; const int *cipher_to_enable; curlerr = CURLE_SSL_CONNECT_ERROR; if (connssl->state == ssl_connection_complete) return CURLE_OK; connssl->data = data; #ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; connssl->key = NULL; #endif /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); if(!initialized) { struct_stat st; /* First we check if $SSL_DIR points to a valid dir */ certDir = getenv("SSL_DIR"); if(certDir) { if((stat(certDir, &st) != 0) || (!S_ISDIR(st.st_mode))) { certDir = NULL; } } /* Now we check if the default location is a valid dir */ if(!certDir) { if((stat(SSL_DIR, &st) == 0) && (S_ISDIR(st.st_mode))) { certDir = (char *)SSL_DIR; } } if (!NSS_IsInitialized()) { initialized = 1; infof(conn->data, "Initializing NSS with certpath: %s\n", certDir ? certDir : "none"); if(!certDir) { rv = NSS_NoDB_Init(NULL); } else { char *certpath = PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", certDir); rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); PR_smprintf_free(certpath); } if(rv != SECSuccess) { infof(conn->data, "Unable to initialize NSS database\n"); curlerr = CURLE_SSL_CACERT_BADFILE; initialized = 0; PR_Unlock(nss_initlock); goto error; } } if(num_enabled_ciphers() == 0) NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT if(!mod) { char *configstring = aprintf("library=%s name=PEM", pem_library); if(!configstring) { PR_Unlock(nss_initlock); goto error; } mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); if(!mod || !mod->loaded) { if(mod) { SECMOD_DestroyModule(mod); mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " "PEM certificates will not work.\n", pem_library); } } #endif PK11_SetPasswordFunc(nss_get_password); } PR_Unlock(nss_initlock); 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; ssl2 = ssl3 = tlsv1 = PR_FALSE; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl3 = tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { if (SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } cipher_to_enable++; } if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } if(data->set.ssl.verifyhost == 1) infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n"); data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { goto error; } if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, NULL) != SECSuccess) goto error; if(!data->set.ssl.verifypeer) /* skip the verifying of the peer */ ; else if(data->set.ssl.CAfile) { int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile, PR_TRUE); if(!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } } else if(data->set.ssl.CApath) { struct_stat st; PRDir *dir; PRDirEntry *entry; if(stat(data->set.ssl.CApath, &st) == -1) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } if(S_ISDIR(st.st_mode)) { int rc; dir = PR_OpenDir(data->set.ssl.CApath); do { entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); if(entry) { char fullpath[PATH_MAX]; snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE); /* FIXME: check this return value! */ } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ } while(entry != NULL); PR_CloseDir(dir); } } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if (data->set.ssl.CRLfile) { int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE); if (!rc) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none"); } if(data->set.str[STRING_CERT]) { bool nickname_alloc = FALSE; char *nickname = fmt_nickname(data->set.str[STRING_CERT], &nickname_alloc); if(!nickname) return CURLE_OUT_OF_MEMORY; if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ if(nickname_alloc) free(nickname); return CURLE_SSL_CERTPROBLEM; } /* this "takes over" the pointer to the allocated name or makes a dup of it */ connssl->client_nickname = nickname_alloc?nickname:strdup(nickname); if(!connssl->client_nickname) return CURLE_OUT_OF_MEMORY; } else connssl->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; PR_Close(model); /* We don't need this any more */ /* This is the password associated with the cert that we're using */ if (data->set.str[STRING_KEY_PASSWD]) { SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ if(SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } connssl->state = ssl_connection_complete; display_conn_info(conn, connssl->handle); if (data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret; bool nickname_alloc = FALSE; char *nickname = fmt_nickname(data->set.str[STRING_SSL_ISSUERCERT], &nickname_alloc); if(!nickname) return CURLE_OUT_OF_MEMORY; ret = check_issuer_cert(connssl->handle, nickname); if(nickname_alloc) free(nickname); if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); curlerr = CURLE_SSL_ISSUER_ERROR; goto error; } else { infof(data, "SSL certificate issuer check ok\n"); } } return CURLE_OK; error: err = PR_GetError(); infof(data, "NSS error %d\n", err); if(model) PR_Close(model); return curlerr; }
CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; PRBool ssl2, ssl3, tlsv1; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; #ifdef HAVE_PK11_CREATEGENERICOBJECT char *configstring = NULL; #endif char *certDir = NULL; int curlerr; curlerr = CURLE_SSL_CONNECT_ERROR; /* FIXME. NSS doesn't support multiple databases open at the same time. */ if(!initialized) { initialized = 1; certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ if (!certDir) { struct stat st; if (stat(SSL_DIR, &st) == 0) if (S_ISDIR(st.st_mode)) { certDir = (char *)SSL_DIR; } } if(!certDir) { rv = NSS_NoDB_Init(NULL); } else { rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", NSS_INIT_READONLY); } if(rv != SECSuccess) { infof(conn->data, "Unable to initialize NSS database\n"); curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT configstring = (char *)malloc(PATH_MAX); PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library); mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); if (!mod || !mod->loaded) { if (mod) { SECMOD_DestroyModule(mod); mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " "PEM certificates will not work.\n", pem_library); } #endif } 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; ssl2 = ssl3 = tlsv1 = PR_FALSE; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl2 = ssl3 = tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { goto error; } if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, NULL) != SECSuccess) goto error; if(!data->set.ssl.verifypeer) /* skip the verifying of the peer */ ; else if (data->set.ssl.CAfile) { int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); if (!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } } else if (data->set.ssl.CApath) { struct stat st; PRDir *dir; PRDirEntry *entry; if (stat(data->set.ssl.CApath, &st) == -1) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } if (S_ISDIR(st.st_mode)) { int rc; dir = PR_OpenDir(data->set.ssl.CApath); do { entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); if (entry) { char fullpath[PATH_MAX]; snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); rc = nss_load_cert(fullpath, PR_TRUE); /* FIXME: check this return value! */ } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ } while (entry != NULL); PR_CloseDir(dir); } } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if(data->set.str[STRING_CERT]) { char *n; char *nickname; nickname = (char *)malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { n = strrchr(data->set.str[STRING_CERT], '/'); if (n) { n++; /* skip last slash */ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); } } else { strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX); } if(nss_Init_Tokens(conn) != SECSuccess) { free(nickname); goto error; } if (!cert_stuff(conn, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ free(nickname); return CURLE_SSL_CERTPROBLEM; } connssl->client_nickname = strdup(nickname); if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, (void *)connssl->client_nickname) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } free(nickname); PK11_SetPasswordFunc(nss_no_password); } else connssl->client_nickname = NULL; /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; PR_Close(model); /* We don't need this any more */ /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ if (SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) != SECSuccess) { if (conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } display_conn_info(conn, connssl->handle); return CURLE_OK; error: err = PR_GetError(); infof(data, "NSS error %d\n", err); if(model) PR_Close(model); return curlerr; }
void OsTLSServerConnectionSocket::NSSInitSocket(PRFileDesc* pDescriptor, long timeoutInSecs, const char* szPassword) { PRFileDesc *tcpSocket = NULL; PRSocketOptionData socketOption; PRStatus prStatus; SECStatus secStatus; // PRIntn hostenum; // PRNetAddr addr; SSLKEAType certKEA; tcpSocket = pDescriptor; if (socketDescriptor > OS_INVALID_SOCKET_DESCRIPTOR) { mpCert = PK11_FindCertFromNickname((char*)mCertNickname.data(), (char*)mCertPassword.data()); if (mpCert == NULL) { mbInitializeFailed = true; goto TlsError; } unsigned char* szPwd = (unsigned char*) PR_Malloc(mCertPassword.length()+ 1); strncpy((char*)szPwd, mCertPassword.data(), mCertPassword.length()+1); mpPrivKey = PK11_FindKeyByAnyCert(mpCert, (char*)szPwd); if (mpPrivKey == NULL) { mbInitializeFailed = true; goto TlsError; } if (tcpSocket) { /* Make the socket blocking. */ socketOption.option = PR_SockOpt_Nonblocking; socketOption.value.non_blocking = PR_FALSE; prStatus = PR_SetSocketOption(tcpSocket, &socketOption); if (prStatus != PR_SUCCESS) { mbInitializeFailed = true; goto TlsError; } /* Import the socket into the SSL layer. */ mpPRfd = SSL_ImportFD(NULL, tcpSocket); if (!mpPRfd) { mbInitializeFailed = true; goto TlsError; } /* Set configuration options. */ secStatus = SSL_OptionSet(mpPRfd, SSL_SECURITY, PR_TRUE); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_OptionSet(mpPRfd, SSL_HANDSHAKE_AS_SERVER, PR_TRUE); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_AuthCertificateHook(mpPRfd, (SSLAuthCertificate)OsTLS::AuthCertificate, (void *)CERT_GetDefaultCertDB()); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_BadCertHook(mpPRfd, (SSLBadCertHandler)OsTLS::BadCertHandler, NULL); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_HandshakeCallback(mpPRfd, (SSLHandshakeCallback)OsTLS::HandshakeCallback, (void*)this); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_SetPKCS11PinArg(mpPRfd, (void*)szPassword); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } certKEA = NSS_FindCertKEAType(mpCert); secStatus = SSL_ConfigSecureServer(mpPRfd, mpCert, mpPrivKey, certKEA); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } secStatus = SSL_ResetHandshake(mpPRfd, /* asServer */ PR_TRUE); if (secStatus != SECSuccess) { mbInitializeFailed = true; goto TlsError; } PR_Free(szPwd); } else { mIsConnected = FALSE; OsConnectionSocket::close(); mbInitializeFailed = true; } } TlsError: return; }
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; PRBool ssl2 = PR_FALSE; PRBool ssl3 = PR_FALSE; PRBool tlsv1 = PR_FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; int curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; long time_left; PRUint32 timeout; if (connssl->state == ssl_connection_complete) return CURLE_OK; connssl->data = data; #ifdef HAVE_PK11_CREATEGENERICOBJECT /* list of all NSS objects we need to destroy in Curl_nss_close() */ connssl->obj_list = Curl_llist_alloc(nss_destroy_object); if(!connssl->obj_list) return CURLE_OUT_OF_MEMORY; #endif /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); curlerr = init_nss(conn->data); if(CURLE_OK != curlerr) { PR_Unlock(nss_initlock); goto error; } curlerr = CURLE_SSL_CONNECT_ERROR; #ifdef HAVE_PK11_CREATEGENERICOBJECT if(!mod) { char *configstring = aprintf("library=%s name=PEM", pem_library); if(!configstring) { PR_Unlock(nss_initlock); goto error; } mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); if(!mod || !mod->loaded) { if(mod) { SECMOD_DestroyModule(mod); mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using " "OpenSSL PEM certificates will not work.\n", pem_library); } } #endif PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); if(!model) goto error; model = SSL_ImportFD(NULL, model); /* make the socket nonblocking */ sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = PR_TRUE; if(PR_SetSocketOption(model, &sock_opt) != SECSuccess) goto error; 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; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl3 = PR_TRUE; if (data->state.ssl_connect_retry) infof(data, "TLS disabled due to previous handshake failure\n"); else tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { if (SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } cipher_to_enable++; } if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } if(data->set.ssl.verifyhost == 1) infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n"); data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { goto error; } if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, NULL) != SECSuccess) goto error; if(data->set.ssl.verifypeer && (CURLE_OK != (curlerr = nss_load_ca_certificates(conn, sockindex)))) goto error; if (data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none"); } if(data->set.str[STRING_CERT]) { bool is_nickname; char *nickname = fmt_nickname(data, STRING_CERT, &is_nickname); if(!nickname) return CURLE_OUT_OF_MEMORY; if(!is_nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ free(nickname); return CURLE_SSL_CERTPROBLEM; } /* store the nickname for SelectClientCert() called during handshake */ connssl->client_nickname = nickname; } else connssl->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) 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 (data->set.str[STRING_KEY_PASSWD]) { SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* check timeout situation */ time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); goto error; } timeout = PR_MillisecondsToInterval((PRUint32) time_left); /* Force the handshake now */ if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } connssl->state = ssl_connection_complete; conn->recv[sockindex] = nss_recv; conn->send[sockindex] = nss_send; display_conn_info(conn, connssl->handle); if (data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; bool is_nickname; char *nickname = fmt_nickname(data, STRING_SSL_ISSUERCERT, &is_nickname); if(!nickname) return CURLE_OUT_OF_MEMORY; if(is_nickname) /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */ ret = check_issuer_cert(connssl->handle, nickname); free(nickname); if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); curlerr = CURLE_SSL_ISSUER_ERROR; goto error; } else { infof(data, "SSL certificate issuer check ok\n"); } } return CURLE_OK; error: /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; err = PR_GetError(); if(handle_cc_error(err, data)) curlerr = CURLE_SSL_CERTPROBLEM; else infof(data, "NSS error %d\n", err); if(model) PR_Close(model); if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ data->state.ssl_connect_retry = TRUE; infof(data, "Error in TLS handshake, trying SSLv3...\n"); return CURLE_OK; } return curlerr; }
PRFileDesc * setupSSLSocket(PRNetAddr *addr) { PRFileDesc *tcpSocket; PRFileDesc *sslSocket; PRSocketOptionData socketOption; PRStatus prStatus; SECStatus secStatus; tcpSocket = PR_NewTCPSocket(); if (tcpSocket == NULL) { errWarn("PR_NewTCPSocket"); } /* Make the socket blocking. */ socketOption.option = PR_SockOpt_Nonblocking; socketOption.value.non_blocking = PR_FALSE; prStatus = PR_SetSocketOption(tcpSocket, &socketOption); if (prStatus != PR_SUCCESS) { errWarn("PR_SetSocketOption"); goto loser; } /* Import the socket into the SSL layer. */ sslSocket = SSL_ImportFD(NULL, tcpSocket); if (!sslSocket) { errWarn("SSL_ImportFD"); goto loser; } /* Set configuration options. */ secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE); if (secStatus != SECSuccess) { errWarn("SSL_OptionSet:SSL_SECURITY"); goto loser; } secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); if (secStatus != SECSuccess) { errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT"); goto loser; } /* Set SSL callback routines. */ secStatus = SSL_GetClientAuthDataHook(sslSocket, (SSLGetClientAuthData)myGetClientAuthData, (void *)certNickname); if (secStatus != SECSuccess) { errWarn("SSL_GetClientAuthDataHook"); goto loser; } secStatus = SSL_AuthCertificateHook(sslSocket, (SSLAuthCertificate)myAuthCertificate, (void *)CERT_GetDefaultCertDB()); if (secStatus != SECSuccess) { errWarn("SSL_AuthCertificateHook"); goto loser; } secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)myBadCertHandler, NULL); if (secStatus != SECSuccess) { errWarn("SSL_BadCertHook"); goto loser; } secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL); if (secStatus != SECSuccess) { errWarn("SSL_HandshakeCallback"); goto loser; } return sslSocket; loser: PR_Close(tcpSocket); return NULL; }
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRErrorCode err = 0; PRFileDesc *model = NULL; PRBool ssl2 = PR_FALSE; PRBool ssl3 = PR_FALSE; PRBool tlsv1 = PR_FALSE; PRBool ssl_no_cache; PRBool ssl_cbc_random_iv; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; CURLcode curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; long time_left; PRUint32 timeout; if(connssl->state == ssl_connection_complete) return CURLE_OK; connssl->data = data; /* list of all NSS objects we need to destroy in Curl_nss_close() */ connssl->obj_list = Curl_llist_alloc(nss_destroy_object); if(!connssl->obj_list) return CURLE_OUT_OF_MEMORY; /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); curlerr = nss_init(conn->data); if(CURLE_OK != curlerr) { PR_Unlock(nss_initlock); goto error; } curlerr = CURLE_SSL_CONNECT_ERROR; if(!mod) { char *configstring = aprintf("library=%s name=PEM", pem_library); if(!configstring) { PR_Unlock(nss_initlock); goto error; } mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); if(!mod || !mod->loaded) { if(mod) { SECMOD_DestroyModule(mod); mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using " "OpenSSL PEM certificates will not work.\n", pem_library); } } PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); if(!model) goto error; model = SSL_ImportFD(NULL, model); /* make the socket nonblocking */ sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = PR_TRUE; if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS) goto error; 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 we are not going to verify peer */ ssl_no_cache = (data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE; if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) goto error; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl3 = PR_TRUE; if(data->state.ssl_connect_retry) infof(data, "TLS disabled due to previous handshake failure\n"); else tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; ssl_cbc_random_iv = !data->set.ssl_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\n", ssl_cbc_random_iv); #else if(ssl_cbc_random_iv) infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n"); #endif /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } cipher_to_enable++; } if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost) infof(data, "warning: ignoring value of ssl.verifyhost\n"); /* bypass the default SSL_AuthCertificate() hook in case we do not want to * verify peer */ if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess) goto error; data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess) goto error; if(SSL_HandshakeCallback(model, HandshakeCallback, NULL) != SECSuccess) goto error; if(data->set.ssl.verifypeer) { const CURLcode rv = nss_load_ca_certificates(conn, sockindex); if(CURLE_OK != rv) { curlerr = rv; goto error; } } if(data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none"); } if(data->set.str[STRING_CERT]) { char *nickname = dup_nickname(data, STRING_CERT); if(nickname) { /* we are not going to use libnsspem.so to read the client cert */ connssl->obj_clicert = NULL; } else { CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY]); if(CURLE_OK != rv) { /* failf() is already done in cert_stuff() */ curlerr = rv; goto error; } } /* store the nickname for SelectClientCert() called during handshake */ connssl->client_nickname = nickname; } else connssl->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) 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(data->set.str[STRING_KEY_PASSWD]) { SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* check timeout situation */ time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); curlerr = CURLE_OPERATION_TIMEDOUT; goto error; } timeout = PR_MillisecondsToInterval((PRUint32) time_left); /* Force the handshake now */ if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } connssl->state = ssl_connection_complete; conn->recv[sockindex] = nss_recv; conn->send[sockindex] = nss_send; display_conn_info(conn, connssl->handle); if(data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT); if(nickname) { /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */ ret = check_issuer_cert(connssl->handle, nickname); free(nickname); } if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); curlerr = CURLE_SSL_ISSUER_ERROR; goto error; } else { infof(data, "SSL certificate issuer check ok\n"); } } return CURLE_OK; error: /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; if(is_nss_error(curlerr)) { /* read NSPR error code */ err = PR_GetError(); if(is_cc_error(err)) curlerr = CURLE_SSL_CERTPROBLEM; /* print the error number and error string */ infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); /* print a human-readable message describing the error if available */ nss_print_error_message(data, err); } if(model) PR_Close(model); /* cleanup on connection failure */ Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ data->state.ssl_connect_retry = TRUE; infof(data, "Error in TLS handshake, trying SSLv3...\n"); return CURLE_OK; } return curlerr; }
static PRFileDesc * setupSSLSocket(void) { PRFileDesc *tcpSocket; PRFileDesc *sslSocket; PRSocketOptionData socketOption; PRStatus prStatus; SECStatus secStatus; tcpSocket = PR_NewTCPSocket(); if (tcpSocket == NULL) goto loser; /* Make the socket blocking. */ socketOption.option = PR_SockOpt_Nonblocking; socketOption.value.non_blocking = PR_FALSE; prStatus = PR_SetSocketOption(tcpSocket, &socketOption); if (prStatus != PR_SUCCESS) goto loser; /* Import the socket into the SSL layer. */ sslSocket = SSL_ImportFD(NULL, tcpSocket); if (!sslSocket) goto loser; /* Set configuration options. */ secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE); if (secStatus != SECSuccess) goto loser; secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); if (secStatus != SECSuccess) goto loser; /* Set SSL callback routines. */ #if 0 /* no client authentication */ secStatus = SSL_GetClientAuthDataHook(sslSocket, (SSLGetClientAuthData)myGetClientAuthData, (void *)certNickname); if (secStatus != SECSuccess) goto loser; #endif #if 0 /* Use the default */ secStatus = SSL_AuthCertificateHook(sslSocket, (SSLAuthCertificate)myAuthCertificate, (void *)CERT_GetDefaultCertDB()); if (secStatus != SECSuccess) goto loser; #endif secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)badCertHandler, NULL); if (secStatus != SECSuccess) goto loser; #if 0 /* No handshake callback */ secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL); if (secStatus != SECSuccess) goto loser; #endif return sslSocket; loser: if (tcpSocket) PR_Close(tcpSocket); return NULL; }