Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // OpenSSL binding for SSLClientSocket. The class layout and general principle | 5 // OpenSSL binding for SSLClientSocket. The class layout and general principle |
| 6 // of operation is derived from SSLClientSocketNSS. | 6 // of operation is derived from SSLClientSocketNSS. |
| 7 | 7 |
| 8 #include "net/socket/ssl_client_socket_openssl.h" | 8 #include "net/socket/ssl_client_socket_openssl.h" |
| 9 | 9 |
| 10 #include <errno.h> | 10 #include <errno.h> |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 #endif | 50 #endif |
| 51 | 51 |
| 52 // This constant can be any non-negative/non-zero value (eg: it does not | 52 // This constant can be any non-negative/non-zero value (eg: it does not |
| 53 // overlap with any value of the net::Error range, including net::OK). | 53 // overlap with any value of the net::Error range, including net::OK). |
| 54 const int kNoPendingReadResult = 1; | 54 const int kNoPendingReadResult = 1; |
| 55 | 55 |
| 56 // If a client doesn't have a list of protocols that it supports, but | 56 // If a client doesn't have a list of protocols that it supports, but |
| 57 // the server supports NPN, choosing "http/1.1" is the best answer. | 57 // the server supports NPN, choosing "http/1.1" is the best answer. |
| 58 const char kDefaultSupportedNPNProtocol[] = "http/1.1"; | 58 const char kDefaultSupportedNPNProtocol[] = "http/1.1"; |
| 59 | 59 |
| 60 void FreeX509Stack(STACK_OF(X509)* ptr) { | |
| 61 sk_X509_pop_free(ptr, X509_free); | |
| 62 } | |
| 63 | |
| 60 typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509; | 64 typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509; |
| 65 typedef crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>::Type | |
| 66 ScopedX509Stack; | |
| 61 | 67 |
| 62 #if OPENSSL_VERSION_NUMBER < 0x1000103fL | 68 #if OPENSSL_VERSION_NUMBER < 0x1000103fL |
| 63 // This method doesn't seem to have made it into the OpenSSL headers. | 69 // This method doesn't seem to have made it into the OpenSSL headers. |
| 64 unsigned long SSL_CIPHER_get_id(const SSL_CIPHER* cipher) { return cipher->id; } | 70 unsigned long SSL_CIPHER_get_id(const SSL_CIPHER* cipher) { return cipher->id; } |
| 65 #endif | 71 #endif |
| 66 | 72 |
| 67 // Used for encoding the |connection_status| field of an SSLInfo object. | 73 // Used for encoding the |connection_status| field of an SSLInfo object. |
| 68 int EncodeSSLConnectionStatus(int cipher_suite, | 74 int EncodeSSLConnectionStatus(int cipher_suite, |
| 69 int compression, | 75 int compression, |
| 70 int version) { | 76 int version) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 88 return SSL_CONNECTION_VERSION_TLS1; | 94 return SSL_CONNECTION_VERSION_TLS1; |
| 89 case 0x0302: | 95 case 0x0302: |
| 90 return SSL_CONNECTION_VERSION_TLS1_1; | 96 return SSL_CONNECTION_VERSION_TLS1_1; |
| 91 case 0x0303: | 97 case 0x0303: |
| 92 return SSL_CONNECTION_VERSION_TLS1_2; | 98 return SSL_CONNECTION_VERSION_TLS1_2; |
| 93 default: | 99 default: |
| 94 return SSL_CONNECTION_VERSION_UNKNOWN; | 100 return SSL_CONNECTION_VERSION_UNKNOWN; |
| 95 } | 101 } |
| 96 } | 102 } |
| 97 | 103 |
| 98 void FreeX509Stack(STACK_OF(X509) * ptr) { | |
| 99 sk_X509_pop_free(ptr, X509_free); | |
| 100 } | |
| 101 | |
| 102 ScopedX509 OSCertHandleToOpenSSL( | 104 ScopedX509 OSCertHandleToOpenSSL( |
| 103 X509Certificate::OSCertHandle os_handle) { | 105 X509Certificate::OSCertHandle os_handle) { |
| 104 #if defined(USE_OPENSSL_CERTS) | 106 #if defined(USE_OPENSSL_CERTS) |
| 105 return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); | 107 return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); |
| 106 #else // !defined(USE_OPENSSL_CERTS) | 108 #else // !defined(USE_OPENSSL_CERTS) |
| 107 std::string der_encoded; | 109 std::string der_encoded; |
| 108 if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) | 110 if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) |
| 109 return ScopedX509(); | 111 return ScopedX509(); |
| 110 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); | 112 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); |
| 111 return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); | 113 return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); |
| 112 #endif // defined(USE_OPENSSL_CERTS) | 114 #endif // defined(USE_OPENSSL_CERTS) |
| 113 } | 115 } |
| 114 | 116 |
| 117 ScopedX509Stack OSCertHandlesToOpenSSL( | |
| 118 const X509Certificate::OSCertHandles& os_handles) { | |
| 119 ScopedX509Stack stack(sk_X509_new_null()); | |
| 120 for (size_t i = 0; i < os_handles.size(); i++) { | |
| 121 ScopedX509 x509 = OSCertHandleToOpenSSL(os_handles[i]); | |
| 122 if (!x509) | |
| 123 return ScopedX509Stack(); | |
| 124 sk_X509_push(stack.get(), x509.release()); | |
| 125 } | |
| 126 return stack.Pass(); | |
| 127 } | |
| 128 | |
| 115 } // namespace | 129 } // namespace |
| 116 | 130 |
| 117 class SSLClientSocketOpenSSL::SSLContext { | 131 class SSLClientSocketOpenSSL::SSLContext { |
| 118 public: | 132 public: |
| 119 static SSLContext* GetInstance() { return Singleton<SSLContext>::get(); } | 133 static SSLContext* GetInstance() { return Singleton<SSLContext>::get(); } |
| 120 SSL_CTX* ssl_ctx() { return ssl_ctx_.get(); } | 134 SSL_CTX* ssl_ctx() { return ssl_ctx_.get(); } |
| 121 SSLSessionCacheOpenSSL* session_cache() { return &session_cache_; } | 135 SSLSessionCacheOpenSSL* session_cache() { return &session_cache_; } |
| 122 | 136 |
| 123 SSLClientSocketOpenSSL* GetClientSocketFromSSL(const SSL* ssl) { | 137 SSLClientSocketOpenSSL* GetClientSocketFromSSL(const SSL* ssl) { |
| 124 DCHECK(ssl); | 138 DCHECK(ssl); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 135 private: | 149 private: |
| 136 friend struct DefaultSingletonTraits<SSLContext>; | 150 friend struct DefaultSingletonTraits<SSLContext>; |
| 137 | 151 |
| 138 SSLContext() { | 152 SSLContext() { |
| 139 crypto::EnsureOpenSSLInit(); | 153 crypto::EnsureOpenSSLInit(); |
| 140 ssl_socket_data_index_ = SSL_get_ex_new_index(0, 0, 0, 0, 0); | 154 ssl_socket_data_index_ = SSL_get_ex_new_index(0, 0, 0, 0, 0); |
| 141 DCHECK_NE(ssl_socket_data_index_, -1); | 155 DCHECK_NE(ssl_socket_data_index_, -1); |
| 142 ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method())); | 156 ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method())); |
| 143 session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig); | 157 session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig); |
| 144 SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL); | 158 SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL); |
| 145 SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback); | 159 SSL_CTX_set_cert_cb(ssl_ctx_.get(), ClientCertRequestCallback, NULL); |
| 146 SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL); | 160 SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL); |
| 147 // TODO(kristianm): Only select this if ssl_config_.next_proto is not empty. | 161 // TODO(kristianm): Only select this if ssl_config_.next_proto is not empty. |
| 148 // It would be better if the callback were not a global setting, | 162 // It would be better if the callback were not a global setting, |
| 149 // but that is an OpenSSL issue. | 163 // but that is an OpenSSL issue. |
| 150 SSL_CTX_set_next_proto_select_cb(ssl_ctx_.get(), SelectNextProtoCallback, | 164 SSL_CTX_set_next_proto_select_cb(ssl_ctx_.get(), SelectNextProtoCallback, |
| 151 NULL); | 165 NULL); |
| 152 ssl_ctx_->tlsext_channel_id_enabled_new = 1; | 166 ssl_ctx_->tlsext_channel_id_enabled_new = 1; |
| 153 } | 167 } |
| 154 | 168 |
| 155 static std::string GetSessionCacheKey(const SSL* ssl) { | 169 static std::string GetSessionCacheKey(const SSL* ssl) { |
| 156 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); | 170 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); |
| 157 DCHECK(socket); | 171 DCHECK(socket); |
| 158 return socket->GetSessionCacheKey(); | 172 return socket->GetSessionCacheKey(); |
| 159 } | 173 } |
| 160 | 174 |
| 161 static SSLSessionCacheOpenSSL::Config kDefaultSessionCacheConfig; | 175 static SSLSessionCacheOpenSSL::Config kDefaultSessionCacheConfig; |
| 162 | 176 |
| 163 static int ClientCertCallback(SSL* ssl, X509** x509, EVP_PKEY** pkey) { | 177 static int ClientCertRequestCallback(SSL* ssl, void* arg) { |
| 164 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); | 178 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); |
| 165 CHECK(socket); | 179 CHECK(socket); |
|
agl
2014/08/14 17:06:46
could probably be a DCHECK.
davidben
2014/08/14 17:50:07
Done. (*shrug* I suppose it'd crash immediately af
| |
| 166 return socket->ClientCertRequestCallback(ssl, x509, pkey); | 180 return socket->ClientCertRequestCallback(ssl); |
| 167 } | 181 } |
| 168 | 182 |
| 169 static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) { | 183 static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) { |
| 170 SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data( | 184 SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data( |
| 171 store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); | 185 store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); |
| 172 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); | 186 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); |
| 173 CHECK(socket); | 187 CHECK(socket); |
| 174 | 188 |
| 175 return socket->CertVerifyCallback(store_ctx); | 189 return socket->CertVerifyCallback(store_ctx); |
| 176 } | 190 } |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 } | 233 } |
| 220 | 234 |
| 221 X509* operator[](size_t index) const { | 235 X509* operator[](size_t index) const { |
| 222 DCHECK_LT(index, size()); | 236 DCHECK_LT(index, size()); |
| 223 return sk_X509_value(openssl_chain_.get(), index); | 237 return sk_X509_value(openssl_chain_.get(), index); |
| 224 } | 238 } |
| 225 | 239 |
| 226 bool IsValid() { return os_chain_.get() && openssl_chain_.get(); } | 240 bool IsValid() { return os_chain_.get() && openssl_chain_.get(); } |
| 227 | 241 |
| 228 private: | 242 private: |
| 229 typedef crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>::Type | |
| 230 ScopedX509Stack; | |
| 231 | |
| 232 ScopedX509Stack openssl_chain_; | 243 ScopedX509Stack openssl_chain_; |
| 233 | 244 |
| 234 scoped_refptr<X509Certificate> os_chain_; | 245 scoped_refptr<X509Certificate> os_chain_; |
| 235 }; | 246 }; |
| 236 | 247 |
| 237 SSLClientSocketOpenSSL::PeerCertificateChain& | 248 SSLClientSocketOpenSSL::PeerCertificateChain& |
| 238 SSLClientSocketOpenSSL::PeerCertificateChain::operator=( | 249 SSLClientSocketOpenSSL::PeerCertificateChain::operator=( |
| 239 const PeerCertificateChain& other) { | 250 const PeerCertificateChain& other) { |
| 240 if (this == &other) | 251 if (this == &other) |
| 241 return *this; | 252 return *this; |
| (...skipping 1146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1388 DCHECK(recv_buffer_.get()); | 1399 DCHECK(recv_buffer_.get()); |
| 1389 int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); | 1400 int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); |
| 1390 // A write into a memory BIO should always succeed. | 1401 // A write into a memory BIO should always succeed. |
| 1391 DCHECK_EQ(result, ret); | 1402 DCHECK_EQ(result, ret); |
| 1392 } | 1403 } |
| 1393 recv_buffer_ = NULL; | 1404 recv_buffer_ = NULL; |
| 1394 transport_recv_busy_ = false; | 1405 transport_recv_busy_ = false; |
| 1395 return result; | 1406 return result; |
| 1396 } | 1407 } |
| 1397 | 1408 |
| 1398 int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, | 1409 int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) { |
| 1399 X509** x509, | |
| 1400 EVP_PKEY** pkey) { | |
| 1401 DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; | 1410 DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; |
| 1402 DCHECK(ssl == ssl_); | 1411 DCHECK(ssl == ssl_); |
| 1403 DCHECK(*x509 == NULL); | 1412 |
| 1404 DCHECK(*pkey == NULL); | 1413 // Clear any currently configured certificates. |
| 1414 SSL_certs_clear(ssl_); | |
| 1405 | 1415 |
| 1406 #if defined(OS_IOS) | 1416 #if defined(OS_IOS) |
| 1407 // TODO(droger): Support client auth on iOS. See http://crbug.com/145954). | 1417 // TODO(droger): Support client auth on iOS. See http://crbug.com/145954). |
| 1408 LOG(WARNING) << "Client auth is not supported"; | 1418 LOG(WARNING) << "Client auth is not supported"; |
| 1409 #else // !defined(OS_IOS) | 1419 #else // !defined(OS_IOS) |
| 1410 if (!ssl_config_.send_client_cert) { | 1420 if (!ssl_config_.send_client_cert) { |
| 1411 // First pass: we know that a client certificate is needed, but we do not | 1421 // First pass: we know that a client certificate is needed, but we do not |
| 1412 // have one at hand. | 1422 // have one at hand. |
| 1413 client_auth_cert_needed_ = true; | 1423 client_auth_cert_needed_ = true; |
| 1414 STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl); | 1424 STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1428 for (size_t i = 0; i < num_client_cert_types; i++) { | 1438 for (size_t i = 0; i < num_client_cert_types; i++) { |
| 1429 cert_key_types_.push_back( | 1439 cert_key_types_.push_back( |
| 1430 static_cast<SSLClientCertType>(client_cert_types[i])); | 1440 static_cast<SSLClientCertType>(client_cert_types[i])); |
| 1431 } | 1441 } |
| 1432 | 1442 |
| 1433 return -1; // Suspends handshake. | 1443 return -1; // Suspends handshake. |
| 1434 } | 1444 } |
| 1435 | 1445 |
| 1436 // Second pass: a client certificate should have been selected. | 1446 // Second pass: a client certificate should have been selected. |
| 1437 if (ssl_config_.client_cert.get()) { | 1447 if (ssl_config_.client_cert.get()) { |
| 1438 // TODO(davidben): Configure OpenSSL to also send the intermediates. | |
| 1439 ScopedX509 leaf_x509 = | 1448 ScopedX509 leaf_x509 = |
| 1440 OSCertHandleToOpenSSL(ssl_config_.client_cert->os_cert_handle()); | 1449 OSCertHandleToOpenSSL(ssl_config_.client_cert->os_cert_handle()); |
| 1441 if (!leaf_x509) { | 1450 if (!leaf_x509) { |
| 1442 LOG(WARNING) << "Failed to import certificate"; | 1451 LOG(WARNING) << "Failed to import certificate"; |
| 1443 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT); | 1452 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT); |
| 1444 return -1; | 1453 return -1; |
| 1445 } | 1454 } |
| 1446 | 1455 |
| 1456 ScopedX509Stack chain = OSCertHandlesToOpenSSL( | |
| 1457 ssl_config_.client_cert->GetIntermediateCertificates()); | |
| 1458 if (!chain) { | |
| 1459 LOG(WARNING) << "Failed to import intermediate certificates"; | |
| 1460 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT); | |
| 1461 return -1; | |
| 1462 } | |
| 1463 | |
| 1447 // TODO(davidben): With Linux client auth support, this should be | 1464 // TODO(davidben): With Linux client auth support, this should be |
| 1448 // conditioned on OS_ANDROID and then, with https://crbug.com/394131, | 1465 // conditioned on OS_ANDROID and then, with https://crbug.com/394131, |
| 1449 // removed altogether. OpenSSLClientKeyStore is mostly an artifact of the | 1466 // removed altogether. OpenSSLClientKeyStore is mostly an artifact of the |
| 1450 // net/ client auth API lacking a private key handle. | 1467 // net/ client auth API lacking a private key handle. |
| 1451 #if defined(USE_OPENSSL_CERTS) | 1468 #if defined(USE_OPENSSL_CERTS) |
| 1452 crypto::ScopedEVP_PKEY privkey = | 1469 crypto::ScopedEVP_PKEY privkey = |
| 1453 OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey( | 1470 OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey( |
| 1454 ssl_config_.client_cert.get()); | 1471 ssl_config_.client_cert.get()); |
| 1455 #else // !defined(USE_OPENSSL_CERTS) | 1472 #else // !defined(USE_OPENSSL_CERTS) |
| 1456 crypto::ScopedEVP_PKEY privkey = | 1473 crypto::ScopedEVP_PKEY privkey = |
| 1457 FetchClientCertPrivateKey(ssl_config_.client_cert.get()); | 1474 FetchClientCertPrivateKey(ssl_config_.client_cert.get()); |
| 1458 #endif // defined(USE_OPENSSL_CERTS) | 1475 #endif // defined(USE_OPENSSL_CERTS) |
| 1459 if (!privkey) { | 1476 if (!privkey) { |
| 1460 // Could not find the private key. Fail the handshake and surface an | 1477 // Could not find the private key. Fail the handshake and surface an |
| 1461 // appropriate error to the caller. | 1478 // appropriate error to the caller. |
| 1462 LOG(WARNING) << "Client cert found without private key"; | 1479 LOG(WARNING) << "Client cert found without private key"; |
| 1463 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY); | 1480 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY); |
| 1464 return -1; | 1481 return -1; |
| 1465 } | 1482 } |
| 1466 | 1483 |
| 1467 // TODO(joth): (copied from NSS) We should wait for server certificate | 1484 if (!SSL_use_certificate(ssl_, leaf_x509.get()) || |
| 1468 // verification before sending our credentials. See http://crbug.com/13934 | 1485 !SSL_use_PrivateKey(ssl_, privkey.get()) || |
| 1469 *x509 = leaf_x509.release(); | 1486 !SSL_set1_chain(ssl_, chain.get())) { |
| 1470 *pkey = privkey.release(); | 1487 LOG(WARNING) << "Failed to set client certificate"; |
| 1488 return -1; | |
| 1489 } | |
| 1471 return 1; | 1490 return 1; |
| 1472 } | 1491 } |
| 1473 #endif // defined(OS_IOS) | 1492 #endif // defined(OS_IOS) |
| 1474 | 1493 |
| 1475 // Send no client certificate. | 1494 // Send no client certificate. |
| 1476 return 0; | 1495 return 1; |
| 1477 } | 1496 } |
| 1478 | 1497 |
| 1479 int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) { | 1498 int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) { |
| 1480 if (!completed_handshake_) { | 1499 if (!completed_handshake_) { |
| 1481 // If the first handshake hasn't completed then we accept any certificates | 1500 // If the first handshake hasn't completed then we accept any certificates |
| 1482 // because we verify after the handshake. | 1501 // because we verify after the handshake. |
| 1483 return 1; | 1502 return 1; |
| 1484 } | 1503 } |
| 1485 | 1504 |
| 1486 CHECK(server_cert_.get()); | 1505 CHECK(server_cert_.get()); |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1589 return socket->MaybeReplayTransportError( | 1608 return socket->MaybeReplayTransportError( |
| 1590 bio, cmd, argp, argi, argl, retvalue); | 1609 bio, cmd, argp, argi, argl, retvalue); |
| 1591 } | 1610 } |
| 1592 | 1611 |
| 1593 scoped_refptr<X509Certificate> | 1612 scoped_refptr<X509Certificate> |
| 1594 SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const { | 1613 SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const { |
| 1595 return server_cert_; | 1614 return server_cert_; |
| 1596 } | 1615 } |
| 1597 | 1616 |
| 1598 } // namespace net | 1617 } // namespace net |
| OLD | NEW |