// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/socket/ssl_server_socket_nss.h" #if defined(OS_WIN) #include #endif #if defined(USE_SYSTEM_SSL) #include #endif #if defined(OS_MACOSX) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/lazy_instance.h" #include "base/memory/ref_counted.h" #include "crypto/rsa_private_key.h" #include "crypto/nss_util_internal.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/ocsp/nss_ocsp.h" #include "net/socket/nss_ssl_util.h" #include "net/socket/ssl_error_params.h" // SSL plaintext fragments are shorter than 16KB. Although the record layer // overhead is allowed to be 2K + 5 bytes, in practice the overhead is much // smaller than 1KB. So a 17KB buffer should be large enough to hold an // entire SSL record. static const int kRecvBufferSize = 17 * 1024; static const int kSendBufferSize = 17 * 1024; #define GotoState(s) next_handshake_state_ = s namespace net { namespace { bool g_nss_server_sockets_init = false; class NSSSSLServerInitSingleton { public: NSSSSLServerInitSingleton() { EnsureNSSSSLInit(); SSL_ConfigServerSessionIDCache(1024, 5, 5, NULL); g_nss_server_sockets_init = true; } ~NSSSSLServerInitSingleton() { SSL_ShutdownServerSessionIDCache(); g_nss_server_sockets_init = false; } }; static base::LazyInstance g_nss_ssl_server_init_singleton = LAZY_INSTANCE_INITIALIZER; } // namespace void EnableSSLServerSockets() { g_nss_ssl_server_init_singleton.Get(); } SSLServerSocket* CreateSSLServerSocket( StreamSocket* socket, X509Certificate* cert, crypto::RSAPrivateKey* key, const SSLConfig& ssl_config) { DCHECK(g_nss_server_sockets_init) << "EnableSSLServerSockets() has not been" << "called yet!"; return new SSLServerSocketNSS(socket, cert, key, ssl_config); } SSLServerSocketNSS::SSLServerSocketNSS( StreamSocket* transport_socket, scoped_refptr cert, crypto::RSAPrivateKey* key, const SSLConfig& ssl_config) : transport_send_busy_(false), transport_recv_busy_(false), user_read_buf_len_(0), user_write_buf_len_(0), nss_fd_(NULL), nss_bufs_(NULL), transport_socket_(transport_socket), ssl_config_(ssl_config), cert_(cert), next_handshake_state_(STATE_NONE), completed_handshake_(false) { ssl_config_.false_start_enabled = false; ssl_config_.version_min = SSL_PROTOCOL_VERSION_SSL3; ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_1; // TODO(hclam): Need a better way to clone a key. std::vector key_bytes; CHECK(key->ExportPrivateKey(&key_bytes)); key_.reset(crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); CHECK(key_.get()); } SSLServerSocketNSS::~SSLServerSocketNSS() { if (nss_fd_ != NULL) { PR_Close(nss_fd_); nss_fd_ = NULL; } } int SSLServerSocketNSS::Handshake(const CompletionCallback& callback) { net_log_.BeginEvent(NetLog::TYPE_SSL_SERVER_HANDSHAKE); int rv = Init(); if (rv != OK) { LOG(ERROR) << "Failed to initialize NSS"; net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); return rv; } rv = InitializeSSLOptions(); if (rv != OK) { LOG(ERROR) << "Failed to initialize SSL options"; net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); return rv; } // Set peer address. TODO(hclam): This should be in a separate method. PRNetAddr peername; memset(&peername, 0, sizeof(peername)); peername.raw.family = AF_INET; memio_SetPeerName(nss_fd_, &peername); GotoState(STATE_HANDSHAKE); rv = DoHandshakeLoop(OK); if (rv == ERR_IO_PENDING) { user_handshake_callback_ = callback; } else { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); } return rv > OK ? OK : rv; } int SSLServerSocketNSS::ExportKeyingMaterial(const base::StringPiece& label, bool has_context, const base::StringPiece& context, unsigned char* out, unsigned int outlen) { if (!IsConnected()) return ERR_SOCKET_NOT_CONNECTED; SECStatus result = SSL_ExportKeyingMaterial( nss_fd_, label.data(), label.size(), has_context, reinterpret_cast(context.data()), context.length(), out, outlen); if (result != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_ExportKeyingMaterial", ""); return MapNSSError(PORT_GetError()); } return OK; } int SSLServerSocketNSS::GetTLSUniqueChannelBinding(std::string* out) { if (!IsConnected()) return ERR_SOCKET_NOT_CONNECTED; unsigned char buf[64]; unsigned int len; SECStatus result = SSL_GetChannelBinding(nss_fd_, SSL_CHANNEL_BINDING_TLS_UNIQUE, buf, &len, arraysize(buf)); if (result != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_GetChannelBinding", ""); return MapNSSError(PORT_GetError()); } out->assign(reinterpret_cast(buf), len); return OK; } int SSLServerSocketNSS::Connect(const CompletionCallback& callback) { NOTIMPLEMENTED(); return ERR_NOT_IMPLEMENTED; } int SSLServerSocketNSS::Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { DCHECK(user_read_callback_.is_null()); DCHECK(user_handshake_callback_.is_null()); DCHECK(!user_read_buf_); DCHECK(nss_bufs_); DCHECK(!callback.is_null()); user_read_buf_ = buf; user_read_buf_len_ = buf_len; DCHECK(completed_handshake_); int rv = DoReadLoop(OK); if (rv == ERR_IO_PENDING) { user_read_callback_ = callback; } else { user_read_buf_ = NULL; user_read_buf_len_ = 0; } return rv; } int SSLServerSocketNSS::Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { DCHECK(user_write_callback_.is_null()); DCHECK(!user_write_buf_); DCHECK(nss_bufs_); DCHECK(!callback.is_null()); user_write_buf_ = buf; user_write_buf_len_ = buf_len; int rv = DoWriteLoop(OK); if (rv == ERR_IO_PENDING) { user_write_callback_ = callback; } else { user_write_buf_ = NULL; user_write_buf_len_ = 0; } return rv; } bool SSLServerSocketNSS::SetReceiveBufferSize(int32 size) { return transport_socket_->SetReceiveBufferSize(size); } bool SSLServerSocketNSS::SetSendBufferSize(int32 size) { return transport_socket_->SetSendBufferSize(size); } bool SSLServerSocketNSS::IsConnected() const { return completed_handshake_; } void SSLServerSocketNSS::Disconnect() { transport_socket_->Disconnect(); } bool SSLServerSocketNSS::IsConnectedAndIdle() const { return completed_handshake_ && transport_socket_->IsConnectedAndIdle(); } int SSLServerSocketNSS::GetPeerAddress(IPEndPoint* address) const { if (!IsConnected()) return ERR_SOCKET_NOT_CONNECTED; return transport_socket_->GetPeerAddress(address); } int SSLServerSocketNSS::GetLocalAddress(IPEndPoint* address) const { if (!IsConnected()) return ERR_SOCKET_NOT_CONNECTED; return transport_socket_->GetLocalAddress(address); } const BoundNetLog& SSLServerSocketNSS::NetLog() const { return net_log_; } void SSLServerSocketNSS::SetSubresourceSpeculation() { transport_socket_->SetSubresourceSpeculation(); } void SSLServerSocketNSS::SetOmniboxSpeculation() { transport_socket_->SetOmniboxSpeculation(); } bool SSLServerSocketNSS::WasEverUsed() const { return transport_socket_->WasEverUsed(); } bool SSLServerSocketNSS::UsingTCPFastOpen() const { return transport_socket_->UsingTCPFastOpen(); } int64 SSLServerSocketNSS::NumBytesRead() const { return transport_socket_->NumBytesRead(); } base::TimeDelta SSLServerSocketNSS::GetConnectTimeMicros() const { return transport_socket_->GetConnectTimeMicros(); } bool SSLServerSocketNSS::WasNpnNegotiated() const { return false; } NextProto SSLServerSocketNSS::GetNegotiatedProtocol() const { // NPN is not supported by this class. return kProtoUnknown; } bool SSLServerSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { NOTIMPLEMENTED(); return false; } int SSLServerSocketNSS::InitializeSSLOptions() { // Transport connected, now hook it up to nss nss_fd_ = memio_CreateIOLayer(kRecvBufferSize, kSendBufferSize); if (nss_fd_ == NULL) { return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR error code. } // Grab pointer to buffers nss_bufs_ = memio_GetSecret(nss_fd_); /* Create SSL state machine */ /* Push SSL onto our fake I/O socket */ nss_fd_ = SSL_ImportFD(NULL, nss_fd_); if (nss_fd_ == NULL) { LogFailedNSSFunction(net_log_, "SSL_ImportFD", ""); return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR/NSS error code. } // TODO(port): set more ssl options! Check errors! int rv; rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_SECURITY"); return ERR_UNEXPECTED; } rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, PR_FALSE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL2"); return ERR_UNEXPECTED; } SSLVersionRange version_range; version_range.min = ssl_config_.version_min; version_range.max = ssl_config_.version_max; rv = SSL_VersionRangeSet(nss_fd_, &version_range); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_VersionRangeSet", ""); return ERR_NO_SSL_VERSIONS_ENABLED; } for (std::vector::const_iterator it = ssl_config_.disabled_cipher_suites.begin(); it != ssl_config_.disabled_cipher_suites.end(); ++it) { // This will fail if the specified cipher is not implemented by NSS, but // the failure is harmless. SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE); } // Server socket doesn't need session tickets. rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); if (rv != SECSuccess) { LogFailedNSSFunction( net_log_, "SSL_OptionSet", "SSL_ENABLE_SESSION_TICKETS"); } // Doing this will force PR_Accept perform handshake as server. rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_CLIENT"); return ERR_UNEXPECTED; } rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_SERVER, PR_TRUE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_SERVER"); return ERR_UNEXPECTED; } rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_FALSE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUEST_CERTIFICATE"); return ERR_UNEXPECTED; } rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_CERTIFICATE, PR_FALSE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUIRE_CERTIFICATE"); return ERR_UNEXPECTED; } rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_AuthCertificateHook", ""); return ERR_UNEXPECTED; } rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_HandshakeCallback", ""); return ERR_UNEXPECTED; } // Get a certificate of CERTCertificate structure. std::string der_string; if (!X509Certificate::GetDEREncoded(cert_->os_cert_handle(), &der_string)) return ERR_UNEXPECTED; SECItem der_cert; der_cert.data = reinterpret_cast(const_cast( der_string.data())); der_cert.len = der_string.length(); der_cert.type = siDERCertBuffer; // Parse into a CERTCertificate structure. CERTCertificate* cert = CERT_NewTempCertificate( CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); if (!cert) { LogFailedNSSFunction(net_log_, "CERT_NewTempCertificate", ""); return MapNSSError(PORT_GetError()); } // Get a key of SECKEYPrivateKey* structure. std::vector key_vector; if (!key_->ExportPrivateKey(&key_vector)) { CERT_DestroyCertificate(cert); return ERR_UNEXPECTED; } SECKEYPrivateKeyStr* private_key = NULL; PK11SlotInfo* slot = crypto::GetPrivateNSSKeySlot(); if (!slot) { CERT_DestroyCertificate(cert); return ERR_UNEXPECTED; } SECItem der_private_key_info; der_private_key_info.data = const_cast(&key_vector.front()); der_private_key_info.len = key_vector.size(); // The server's RSA private key must be imported into NSS with the // following key usage bits: // - KU_KEY_ENCIPHERMENT, required for the RSA key exchange algorithm. // - KU_DIGITAL_SIGNATURE, required for the DHE_RSA and ECDHE_RSA key // exchange algorithms. const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE; rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( slot, &der_private_key_info, NULL, NULL, PR_FALSE, PR_FALSE, key_usage, &private_key, NULL); PK11_FreeSlot(slot); if (rv != SECSuccess) { CERT_DestroyCertificate(cert); return ERR_UNEXPECTED; } // Assign server certificate and private key. SSLKEAType cert_kea = NSS_FindCertKEAType(cert); rv = SSL_ConfigSecureServer(nss_fd_, cert, private_key, cert_kea); CERT_DestroyCertificate(cert); SECKEY_DestroyPrivateKey(private_key); if (rv != SECSuccess) { PRErrorCode prerr = PR_GetError(); LOG(ERROR) << "Failed to config SSL server: " << prerr; LogFailedNSSFunction(net_log_, "SSL_ConfigureSecureServer", ""); return ERR_UNEXPECTED; } // Tell SSL we're a server; needed if not letting NSPR do socket I/O rv = SSL_ResetHandshake(nss_fd_, PR_TRUE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_ResetHandshake", ""); return ERR_UNEXPECTED; } return OK; } void SSLServerSocketNSS::OnSendComplete(int result) { if (next_handshake_state_ == STATE_HANDSHAKE) { // In handshake phase. OnHandshakeIOComplete(result); return; } if (!completed_handshake_) return; if (user_write_buf_) { int rv = DoWriteLoop(result); if (rv != ERR_IO_PENDING) DoWriteCallback(rv); } else { // Ensure that any queued ciphertext is flushed. DoTransportIO(); } } void SSLServerSocketNSS::OnRecvComplete(int result) { if (next_handshake_state_ == STATE_HANDSHAKE) { // In handshake phase. OnHandshakeIOComplete(result); return; } // Network layer received some data, check if client requested to read // decrypted data. if (!user_read_buf_ || !completed_handshake_) return; int rv = DoReadLoop(result); if (rv != ERR_IO_PENDING) DoReadCallback(rv); } void SSLServerSocketNSS::OnHandshakeIOComplete(int result) { int rv = DoHandshakeLoop(result); if (rv != ERR_IO_PENDING) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); if (!user_handshake_callback_.is_null()) DoHandshakeCallback(rv); } } // Return 0 for EOF, // > 0 for bytes transferred immediately, // < 0 for error (or the non-error ERR_IO_PENDING). int SSLServerSocketNSS::BufferSend(void) { if (transport_send_busy_) return ERR_IO_PENDING; const char* buf1; const char* buf2; unsigned int len1, len2; memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2); const unsigned int len = len1 + len2; int rv = 0; if (len) { scoped_refptr send_buffer(new IOBuffer(len)); memcpy(send_buffer->data(), buf1, len1); memcpy(send_buffer->data() + len1, buf2, len2); rv = transport_socket_->Write( send_buffer, len, base::Bind(&SSLServerSocketNSS::BufferSendComplete, base::Unretained(this))); if (rv == ERR_IO_PENDING) { transport_send_busy_ = true; } else { memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); } } return rv; } void SSLServerSocketNSS::BufferSendComplete(int result) { memio_PutWriteResult(nss_bufs_, MapErrorToNSS(result)); transport_send_busy_ = false; OnSendComplete(result); } int SSLServerSocketNSS::BufferRecv(void) { if (transport_recv_busy_) return ERR_IO_PENDING; char* buf; int nb = memio_GetReadParams(nss_bufs_, &buf); int rv; if (!nb) { // buffer too full to read into, so no I/O possible at moment rv = ERR_IO_PENDING; } else { recv_buffer_ = new IOBuffer(nb); rv = transport_socket_->Read( recv_buffer_, nb, base::Bind(&SSLServerSocketNSS::BufferRecvComplete, base::Unretained(this))); if (rv == ERR_IO_PENDING) { transport_recv_busy_ = true; } else { if (rv > 0) memcpy(buf, recv_buffer_->data(), rv); memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); recv_buffer_ = NULL; } } return rv; } void SSLServerSocketNSS::BufferRecvComplete(int result) { if (result > 0) { char* buf; memio_GetReadParams(nss_bufs_, &buf); memcpy(buf, recv_buffer_->data(), result); } recv_buffer_ = NULL; memio_PutReadResult(nss_bufs_, MapErrorToNSS(result)); transport_recv_busy_ = false; OnRecvComplete(result); } // Do as much network I/O as possible between the buffer and the // transport socket. Return true if some I/O performed, false // otherwise (error or ERR_IO_PENDING). bool SSLServerSocketNSS::DoTransportIO() { bool network_moved = false; if (nss_bufs_ != NULL) { int rv; // Read and write as much data as we can. The loop is neccessary // because Write() may return synchronously. do { rv = BufferSend(); if (rv > 0) network_moved = true; } while (rv > 0); if (BufferRecv() >= 0) network_moved = true; } return network_moved; } int SSLServerSocketNSS::DoPayloadRead() { DCHECK(user_read_buf_); DCHECK_GT(user_read_buf_len_, 0); int rv = PR_Read(nss_fd_, user_read_buf_->data(), user_read_buf_len_); if (rv >= 0) return rv; PRErrorCode prerr = PR_GetError(); if (prerr == PR_WOULD_BLOCK_ERROR) { return ERR_IO_PENDING; } rv = MapNSSError(prerr); net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, CreateNetLogSSLErrorCallback(rv, prerr)); return rv; } int SSLServerSocketNSS::DoPayloadWrite() { DCHECK(user_write_buf_); int rv = PR_Write(nss_fd_, user_write_buf_->data(), user_write_buf_len_); if (rv >= 0) return rv; PRErrorCode prerr = PR_GetError(); if (prerr == PR_WOULD_BLOCK_ERROR) { return ERR_IO_PENDING; } rv = MapNSSError(prerr); net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, CreateNetLogSSLErrorCallback(rv, prerr)); return rv; } int SSLServerSocketNSS::DoHandshakeLoop(int last_io_result) { int rv = last_io_result; do { // Default to STATE_NONE for next state. // (This is a quirk carried over from the windows // implementation. It makes reading the logs a bit harder.) // State handlers can and often do call GotoState just // to stay in the current state. State state = next_handshake_state_; GotoState(STATE_NONE); switch (state) { case STATE_HANDSHAKE: rv = DoHandshake(); break; case STATE_NONE: default: rv = ERR_UNEXPECTED; LOG(DFATAL) << "unexpected state " << state; break; } // Do the actual network I/O bool network_moved = DoTransportIO(); if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) { // In general we exit the loop if rv is ERR_IO_PENDING. In this // special case we keep looping even if rv is ERR_IO_PENDING because // the transport IO may allow DoHandshake to make progress. rv = OK; // This causes us to stay in the loop. } } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE); return rv; } int SSLServerSocketNSS::DoReadLoop(int result) { DCHECK(completed_handshake_); DCHECK(next_handshake_state_ == STATE_NONE); if (result < 0) return result; if (!nss_bufs_) { LOG(DFATAL) << "!nss_bufs_"; int rv = ERR_UNEXPECTED; net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, CreateNetLogSSLErrorCallback(rv, 0)); return rv; } bool network_moved; int rv; do { rv = DoPayloadRead(); network_moved = DoTransportIO(); } while (rv == ERR_IO_PENDING && network_moved); return rv; } int SSLServerSocketNSS::DoWriteLoop(int result) { DCHECK(completed_handshake_); DCHECK(next_handshake_state_ == STATE_NONE); if (result < 0) return result; if (!nss_bufs_) { LOG(DFATAL) << "!nss_bufs_"; int rv = ERR_UNEXPECTED; net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, CreateNetLogSSLErrorCallback(rv, 0)); return rv; } bool network_moved; int rv; do { rv = DoPayloadWrite(); network_moved = DoTransportIO(); } while (rv == ERR_IO_PENDING && network_moved); return rv; } int SSLServerSocketNSS::DoHandshake() { int net_error = OK; SECStatus rv = SSL_ForceHandshake(nss_fd_); if (rv == SECSuccess) { completed_handshake_ = true; } else { PRErrorCode prerr = PR_GetError(); net_error = MapNSSError(prerr); // If not done, stay in this state if (net_error == ERR_IO_PENDING) { GotoState(STATE_HANDSHAKE); } else { LOG(ERROR) << "handshake failed; NSS error code " << prerr << ", net_error " << net_error; net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, CreateNetLogSSLErrorCallback(net_error, prerr)); } } return net_error; } void SSLServerSocketNSS::DoHandshakeCallback(int rv) { DCHECK_NE(rv, ERR_IO_PENDING); CompletionCallback c = user_handshake_callback_; user_handshake_callback_.Reset(); c.Run(rv > OK ? OK : rv); } void SSLServerSocketNSS::DoReadCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(!user_read_callback_.is_null()); // Since Run may result in Read being called, clear |user_read_callback_| // up front. CompletionCallback c = user_read_callback_; user_read_callback_.Reset(); user_read_buf_ = NULL; user_read_buf_len_ = 0; c.Run(rv); } void SSLServerSocketNSS::DoWriteCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(!user_write_callback_.is_null()); // Since Run may result in Write being called, clear |user_write_callback_| // up front. CompletionCallback c = user_write_callback_; user_write_callback_.Reset(); user_write_buf_ = NULL; user_write_buf_len_ = 0; c.Run(rv); } // static // NSS calls this if an incoming certificate needs to be verified. // Do nothing but return SECSuccess. // This is called only in full handshake mode. // Peer certificate is retrieved in HandshakeCallback() later, which is called // in full handshake mode or in resumption handshake mode. SECStatus SSLServerSocketNSS::OwnAuthCertHandler(void* arg, PRFileDesc* socket, PRBool checksig, PRBool is_server) { // TODO(hclam): Implement. // Tell NSS to not verify the certificate. return SECSuccess; } // static // NSS calls this when handshake is completed. // After the SSL handshake is finished we need to verify the certificate. void SSLServerSocketNSS::HandshakeCallback(PRFileDesc* socket, void* arg) { // TODO(hclam): Implement. } int SSLServerSocketNSS::Init() { // Initialize the NSS SSL library in a threadsafe way. This also // initializes the NSS base library. EnsureNSSSSLInit(); if (!NSS_IsInitialized()) return ERR_UNEXPECTED; EnableSSLServerSockets(); return OK; } } // namespace net