2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/ssl-socket.h"
18 #include "hphp/runtime/base/complex_types.h"
19 #include "hphp/runtime/base/runtime-error.h"
20 #include "hphp/util/util.h"
24 ///////////////////////////////////////////////////////////////////////////////
26 StaticString
SSLSocket::s_class_name("SSLSocket");
27 StaticString
Certificate::s_class_name("OpenSSL X.509");
29 ///////////////////////////////////////////////////////////////////////////////
31 Mutex
SSLSocket::s_mutex
;
32 int SSLSocket::s_ex_data_index
= -1;
33 int SSLSocket::GetSSLExDataIndex() {
34 if (s_ex_data_index
>= 0) {
35 return s_ex_data_index
;
38 if (s_ex_data_index
< 0) {
39 s_ex_data_index
= SSL_get_ex_new_index(0, (void*)"PHP stream index",
40 nullptr, nullptr, nullptr);
41 assert(s_ex_data_index
>= 0);
43 return s_ex_data_index
;
47 s_allow_self_signed("allow_self_signed"),
48 s_verify_depth("verify_depth");
50 static int verify_callback(int preverify_ok
, X509_STORE_CTX
*ctx
) {
51 int ret
= preverify_ok
;
53 /* determine the status for the current cert */
54 X509_STORE_CTX_get_current_cert(ctx
);
55 int err
= X509_STORE_CTX_get_error(ctx
);
56 int depth
= X509_STORE_CTX_get_error_depth(ctx
);
58 /* conjure the stream & context to use */
59 SSL
*ssl
= (SSL
*)X509_STORE_CTX_get_ex_data
60 (ctx
, SSL_get_ex_data_X509_STORE_CTX_idx());
62 (SSLSocket
*)SSL_get_ex_data(ssl
, SSLSocket::GetSSLExDataIndex());
64 /* if allow_self_signed is set, make sure that verification succeeds */
65 if (err
== X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&&
66 stream
->getContext()[s_allow_self_signed
].toBoolean()) {
71 Variant vdepth
= stream
->getContext()[s_verify_depth
];
72 if (vdepth
.toBoolean() && depth
> vdepth
.toInt64()) {
74 X509_STORE_CTX_set_error(ctx
, X509_V_ERR_CERT_CHAIN_TOO_LONG
);
80 const StaticString
s_passphrase("passphrase");
82 static int passwd_callback(char *buf
, int num
, int verify
, void *data
) {
83 /* TODO: could expand this to make a callback into PHP user-space */
84 SSLSocket
*stream
= (SSLSocket
*)data
;
85 String passphrase
= stream
->getContext()[s_passphrase
].toString();
86 if (!passphrase
.empty() && passphrase
.size() < num
- 1) {
87 memcpy(buf
, passphrase
.data(), passphrase
.size() + 1);
88 return passphrase
.size();
94 s_verify_peer("verify_peer"),
98 s_local_cert("local_cert");
100 SSL
*SSLSocket::createSSL(SSL_CTX
*ctx
) {
103 /* look at options in the stream and set appropriate verification flags */
104 if (m_context
[s_verify_peer
].toBoolean()) {
105 /* turn on verification callback */
106 SSL_CTX_set_verify(ctx
, SSL_VERIFY_PEER
, verify_callback
);
109 String cafile
= m_context
[s_cafile
].toString();
110 String capath
= m_context
[s_capath
].toString();
112 if (!cafile
.empty() || !capath
.empty()) {
113 if (!SSL_CTX_load_verify_locations(ctx
, cafile
.data(), capath
.data())) {
114 raise_warning("Unable to set verify locations `%s' `%s'",
115 cafile
.data(), capath
.data());
120 int64_t depth
= m_context
[s_verify_depth
].toInt64();
122 SSL_CTX_set_verify_depth(ctx
, depth
);
125 SSL_CTX_set_verify(ctx
, SSL_VERIFY_NONE
, nullptr);
128 /* callback for the passphrase (for localcert) */
129 if (!m_context
[s_passphrase
].toString().empty()) {
130 SSL_CTX_set_default_passwd_cb_userdata(ctx
, this);
131 SSL_CTX_set_default_passwd_cb(ctx
, passwd_callback
);
134 String cipherlist
= m_context
[s_ciphers
].toString();
135 if (cipherlist
.empty()) {
136 cipherlist
= "DEFAULT";
138 SSL_CTX_set_cipher_list(ctx
, cipherlist
.data());
140 String certfile
= m_context
[s_local_cert
].toString();
141 if (!certfile
.empty()) {
142 String resolved_path_buff
= File::TranslatePath(certfile
);
143 if (!resolved_path_buff
.empty()) {
144 /* a certificate to use for authentication */
145 if (SSL_CTX_use_certificate_chain_file(ctx
, resolved_path_buff
.data())
147 raise_warning("Unable to set local cert chain file `%s'; Check "
148 "that your cafile/capath settings include details of "
149 "your certificate and its issuer", certfile
.data());
153 if (SSL_CTX_use_PrivateKey_file(ctx
, resolved_path_buff
.data(),
154 SSL_FILETYPE_PEM
) != 1) {
155 raise_warning("Unable to set private key file `%s'",
156 resolved_path_buff
.data());
160 SSL
*tmpssl
= SSL_new(ctx
);
161 X509
*cert
= SSL_get_certificate(tmpssl
);
163 EVP_PKEY
*key
= X509_get_pubkey(cert
);
164 EVP_PKEY_copy_parameters(key
, SSL_get_privatekey(tmpssl
));
169 if (!SSL_CTX_check_private_key(ctx
)) {
170 raise_warning("Private key does not match certificate!");
175 SSL
*ssl
= SSL_new(ctx
);
177 SSL_set_ex_data(ssl
, GetSSLExDataIndex(), this); /* map SSL => stream */
182 ///////////////////////////////////////////////////////////////////////////////
183 // constructors and destructor
185 SSLSocket::SSLSocket()
186 : m_handle(nullptr), m_ssl_active(false), m_method((CryptoMethod
)-1),
187 m_client(false), m_connect_timeout(0), m_enable_on_connect(false),
188 m_state_set(false), m_is_blocked(true) {
191 SSLSocket::SSLSocket(int sockfd
, int type
, const char *address
/* = NULL */,
193 : Socket(sockfd
, type
, address
, port
),
194 m_handle(nullptr), m_ssl_active(false), m_method((CryptoMethod
)-1),
195 m_client(false), m_connect_timeout(0), m_enable_on_connect(false),
196 m_state_set(false), m_is_blocked(true) {
199 SSLSocket::~SSLSocket() {
200 m_context
.reset(); // for sweeping
204 bool SSLSocket::onConnect() {
205 return setupCrypto() && enableCrypto();
208 bool SSLSocket::onAccept() {
209 if (m_fd
>= 0 && m_enable_on_connect
) {
211 case CryptoMethod::ClientSSLv23
:
212 m_method
= CryptoMethod::ServerSSLv23
;
214 case CryptoMethod::ClientSSLv2
:
215 m_method
= CryptoMethod::ServerSSLv2
;
217 case CryptoMethod::ClientSSLv3
:
218 m_method
= CryptoMethod::ServerSSLv3
;
220 case CryptoMethod::ClientTLS
:
221 m_method
= CryptoMethod::ServerTLS
;
227 if (setupCrypto() && enableCrypto()) {
231 raise_warning("Failed to enable crypto");
237 ///////////////////////////////////////////////////////////////////////////////
239 bool SSLSocket::handleError(int64_t nr_bytes
, bool is_init
) {
245 int err
= SSL_get_error(m_handle
, nr_bytes
);
247 case SSL_ERROR_ZERO_RETURN
:
248 /* SSL terminated (but socket may still be active) */
251 case SSL_ERROR_WANT_READ
:
252 case SSL_ERROR_WANT_WRITE
:
253 /* re-negotiation, or perhaps the SSL layer needs more
254 * packets: retry in next iteration */
256 retry
= (is_init
|| m_is_blocked
);
258 case SSL_ERROR_SYSCALL
:
259 if (ERR_peek_error() == 0) {
261 if (ERR_get_error()) {
262 raise_warning("SSL: fatal protocol error");
264 SSL_set_shutdown(m_handle
, SSL_SENT_SHUTDOWN
|SSL_RECEIVED_SHUTDOWN
);
268 string estr
= Util::safe_strerror(errno
);
269 raise_warning("SSL: %s", estr
.c_str());
276 /* some other error */
277 ecode
= ERR_get_error();
278 switch (ERR_GET_REASON(ecode
)) {
279 case SSL_R_NO_SHARED_CIPHER
:
280 raise_warning("SSL_R_NO_SHARED_CIPHER: no suitable shared cipher "
281 "could be used. This could be because the server is "
282 "missing an SSL certificate (local_cert context "
289 // NULL is automatically added
290 ERR_error_string_n(ecode
, esbuf
, sizeof(esbuf
));
295 } while ((ecode
= ERR_get_error()) != 0);
297 raise_warning("SSL operation failed with code %d. %s%s",
298 err
, !ebuf
.empty() ? "OpenSSL Error messages:\n" : "",
299 !ebuf
.empty() ? ebuf
.c_str() : "");
309 ///////////////////////////////////////////////////////////////////////////////
311 SSLSocket
*SSLSocket::Create(const char *&name
, int port
, double timeout
) {
313 if (strncmp(name
, "ssl://", 6) == 0) {
315 method
= CryptoMethod::ClientSSLv23
;
316 } else if (strncmp(name
, "sslv2://", 8) == 0) {
318 method
= CryptoMethod::ClientSSLv2
;
319 } else if (strncmp(name
, "sslv3://", 8) == 0) {
321 method
= CryptoMethod::ClientSSLv3
;
322 } else if (strncmp(name
, "tls://", 6) == 0) {
324 method
= CryptoMethod::ClientTLS
;
329 int domain
= AF_INET
;
330 int type
= SOCK_STREAM
;
331 SSLSocket
*sock
= new SSLSocket(socket(domain
, type
, 0), domain
, name
, port
);
332 sock
->m_method
= method
;
333 sock
->m_connect_timeout
= timeout
;
334 sock
->m_enable_on_connect
= true;
339 bool SSLSocket::close() {
343 bool SSLSocket::closeImpl() {
345 SSL_shutdown(m_handle
);
346 m_ssl_active
= false;
352 return Socket::closeImpl();
355 int64_t SSLSocket::readImpl(char *buffer
, int64_t length
) {
356 int64_t nr_bytes
= 0;
361 Socket::waitForData();
365 // could get here and we only have parts of an SSL packet
367 nr_bytes
= SSL_read(m_handle
, buffer
, length
);
368 if (nr_bytes
> 0) break; /* we got the data */
369 retry
= handleError(nr_bytes
, false);
370 m_eof
= (!retry
&& errno
!= EAGAIN
&& !SSL_pending(m_handle
));
373 nr_bytes
= Socket::readImpl(buffer
, length
);
375 return nr_bytes
< 0 ? 0 : nr_bytes
;
378 int64_t SSLSocket::writeImpl(const char *buffer
, int64_t length
) {
383 didwrite
= SSL_write(m_handle
, buffer
, length
);
384 if (didwrite
> 0) break;
385 retry
= handleError(didwrite
, false);
388 didwrite
= Socket::writeImpl(buffer
, length
);
390 return didwrite
< 0 ? 0 : didwrite
;
393 bool SSLSocket::setupCrypto(SSLSocket
*session
/* = NULL */) {
395 raise_warning("SSL/TLS already set-up for this stream");
399 /* need to do slightly different things, based on client/server method,
400 * so lets remember which method was selected */
401 #if OPENSSL_VERSION_NUMBER < 0x00909000L
404 const SSL_METHOD
*smethod
;
407 case CryptoMethod::ClientSSLv23
:
409 smethod
= SSLv23_client_method();
411 case CryptoMethod::ClientSSLv3
:
413 smethod
= SSLv3_client_method();
415 case CryptoMethod::ClientTLS
:
417 smethod
= TLSv1_client_method();
419 case CryptoMethod::ServerSSLv23
:
421 smethod
= SSLv23_server_method();
423 case CryptoMethod::ServerSSLv3
:
425 smethod
= SSLv3_server_method();
428 /* SSLv2 protocol might be disabled in the OpenSSL library */
429 #ifndef OPENSSL_NO_SSL2
430 case CryptoMethod::ClientSSLv2
:
432 smethod
= SSLv2_client_method();
434 case CryptoMethod::ServerSSLv2
:
436 smethod
= SSLv2_server_method();
439 case CryptoMethod::ClientSSLv2
:
440 case CryptoMethod::ServerSSLv2
:
441 raise_warning("OpenSSL library does not support SSL2 protocol");
446 case CryptoMethod::ServerTLS
:
448 smethod
= TLSv1_server_method();
454 SSL_CTX
*ctx
= SSL_CTX_new(smethod
);
455 if (ctx
== nullptr) {
456 raise_warning("failed to create an SSL context");
460 SSL_CTX_set_options(ctx
, SSL_OP_ALL
);
461 m_handle
= createSSL(ctx
);
462 if (m_handle
== nullptr) {
463 raise_warning("failed to create an SSL handle");
468 if (!SSL_set_fd(m_handle
, m_fd
)) {
469 handleError(0, true);
472 SSL_copy_session_id(m_handle
, session
->m_handle
);
477 const StaticString
s_CN_match("CN_match");
479 bool SSLSocket::applyVerificationPolicy(X509
*peer
) {
480 /* verification is turned off */
481 if (!m_context
[s_verify_peer
].toBoolean()) {
485 if (peer
== nullptr) {
486 raise_warning("Could not get peer certificate");
490 int err
= SSL_get_verify_result(m_handle
);
495 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
:
496 if (m_context
[s_allow_self_signed
].toBoolean()) {
500 /* not allowed, so fall through */
502 raise_warning("Could not verify peer: code:%d %s", err
,
503 X509_verify_cert_error_string(err
));
507 /* if the cert passed the usual checks, apply our own local policies now */
509 /* Does the common name match ? (used primarily for https://) */
510 String cnmatch
= m_context
[s_CN_match
].toString();
511 if (!cnmatch
.empty()) {
512 X509_NAME
*name
= X509_get_subject_name(peer
);
514 int name_len
= X509_NAME_get_text_by_NID(name
, NID_commonName
, buf
,
518 raise_warning("Unable to locate peer certificate CN");
520 } else if (name_len
!= (int)strlen(buf
)) {
521 raise_warning("Peer certificate CN=`%.*s' is malformed", name_len
, buf
);
525 bool match
= (strcmp(cnmatch
.c_str(), buf
) == 0);
526 if (!match
&& strlen(buf
) > 3 && buf
[0] == '*' && buf
[1] == '.') {
528 if (strchr(buf
+2, '.')) {
529 const char* cnmatch_str
= cnmatch
.c_str();
530 const char *tmp
= strstr(cnmatch_str
, buf
+1);
531 match
= tmp
&& strcmp(tmp
, buf
+2) && tmp
== strchr(cnmatch_str
, '.');
537 raise_warning("Peer certificate CN=`%.*s' did not match expected CN=`%s'",
538 name_len
, buf
, cnmatch
.c_str());
547 s_capture_peer_cert("capture_peer_cert"),
548 s_peer_certificate("peer_certificate"),
549 s_capture_peer_cert_chain("capture_peer_cert_chain"),
550 s_peer_certificate_chain("peer_certificate_chain");
552 bool SSLSocket::enableCrypto(bool activate
/* = true */) {
553 if (activate
&& !m_ssl_active
) {
554 double timeout
= m_connect_timeout
;
555 bool blocked
= m_is_blocked
;
558 SSL_set_connect_state(m_handle
);
560 SSL_set_accept_state(m_handle
);
565 if (m_client
&& setBlocking(false)) {
566 m_is_blocked
= false;
573 struct timeval tvs
, tve
;
576 gettimeofday(&tvs
, &tz
);
577 n
= SSL_connect(m_handle
);
578 gettimeofday(&tve
, &tz
);
580 timeout
-= (tve
.tv_sec
+ (double) tve
.tv_usec
/ 1000000) -
581 (tvs
.tv_sec
+ (double) tvs
.tv_usec
/ 1000000);
583 raise_warning("SSL: connection timeout");
587 n
= SSL_accept(m_handle
);
591 retry
= handleError(n
, true);
597 if (m_client
&& m_is_blocked
!= blocked
&& setBlocking(blocked
)) {
598 m_is_blocked
= blocked
;
602 X509
*peer_cert
= SSL_get_peer_certificate(m_handle
);
603 if (!applyVerificationPolicy(peer_cert
)) {
604 SSL_shutdown(m_handle
);
608 /* allow the script to capture the peer cert
609 * and/or the certificate chain */
610 if (m_context
[s_capture_peer_cert
].toBoolean()) {
611 Resource
cert(new Certificate(peer_cert
));
612 m_context
.set(s_peer_certificate
, cert
);
616 if (m_context
[s_capture_peer_cert_chain
].toBoolean()) {
618 STACK_OF(X509
) *chain
= SSL_get_peer_cert_chain(m_handle
);
620 for (int i
= 0; i
< sk_X509_num(chain
); i
++) {
621 X509
*mycert
= X509_dup(sk_X509_value(chain
, i
));
622 arr
.append(Resource(new Certificate(mycert
)));
625 m_context
.set(s_peer_certificate_chain
, arr
);
630 X509_free(peer_cert
);
633 n
= errno
== EAGAIN
? 0 : -1;
638 } else if (!activate
&& m_ssl_active
) {
639 /* deactivate - common for server/client */
640 SSL_shutdown(m_handle
);
641 m_ssl_active
= false;
646 bool SSLSocket::checkLiveness() {
653 p
.events
= POLLIN
| POLLERR
| POLLHUP
| POLLPRI
;
655 if (poll(&p
, 1, 0) > 0 && p
.revents
> 0) {
659 int n
= SSL_peek(m_handle
, &buf
, sizeof(buf
));
661 int err
= SSL_get_error(m_handle
, n
);
662 if (err
== SSL_ERROR_SYSCALL
) {
663 return errno
== EAGAIN
;
666 if (err
== SSL_ERROR_WANT_READ
|| err
== SSL_ERROR_WANT_WRITE
) {
671 /* any other problem is a fatal error */
674 /* either peek succeeded or there was an error; we
675 * have set the alive flag appropriately */
678 } else if (0 == recv(m_fd
, &buf
, sizeof(buf
), MSG_PEEK
) &&
686 ///////////////////////////////////////////////////////////////////////////////
689 BIO
*Certificate::ReadData(CVarRef var
, bool *file
/* = NULL */) {
690 if (var
.isString() || var
.isObject()) {
691 String svar
= var
.toString();
692 if (svar
.substr(0, 7) == "file://") {
693 if (file
) *file
= true;
694 BIO
*ret
= BIO_new_file((char*)svar
.substr(7).data(), "r");
695 if (ret
== nullptr) {
696 raise_warning("error opening the file, %s", svar
.data());
701 if (file
) *file
= false;
702 return BIO_new_mem_buf((char*)svar
.data(), svar
.size());
708 Resource
Certificate::Get(CVarRef var
) {
709 if (var
.isResource()) {
710 return var
.toResource();
712 if (var
.isString() || var
.isObject()) {
714 BIO
*in
= ReadData(var
, &file
);
715 if (in
== nullptr) return Resource();
720 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
722 cert = (X509 *)PEM_ASN1_read_bio
723 ((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
726 cert
= PEM_read_bio_X509(in
, nullptr, nullptr, nullptr);
729 return Resource(new Certificate(cert
));
735 ///////////////////////////////////////////////////////////////////////////////