1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Wez Furlong <wez@thebrainroom.com> |
14 | Daniel Lowrey <rdlowrey@php.net> |
15 | Chris Wright <daverandom@php.net> |
16 | Jakub Zelenka <bukka@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "php.h"
25 #include "ext/standard/file.h"
26 #include "ext/standard/url.h"
27 #include "streams/php_streams_int.h"
28 #include "zend_smart_str.h"
29 #include "php_openssl.h"
30 #include "php_network.h"
31 #include <openssl/ssl.h>
32 #include <openssl/rsa.h>
33 #include <openssl/x509.h>
34 #include <openssl/x509v3.h>
35 #include <openssl/err.h>
36
37 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
38 #include <openssl/bn.h>
39 #include <openssl/dh.h>
40 #endif
41
42 #ifdef PHP_WIN32
43 #include "win32/winutil.h"
44 #include "win32/time.h"
45 #include <Wincrypt.h>
46 /* These are from Wincrypt.h, they conflict with OpenSSL */
47 #undef X509_NAME
48 #undef X509_CERT_PAIR
49 #undef X509_EXTENSIONS
50 #endif
51
52 /* Flags for determining allowed stream crypto methods */
53 #define STREAM_CRYPTO_IS_CLIENT (1<<0)
54 #define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
55 #define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
56 #define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
57 #define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
58 #define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
59 #define STREAM_CRYPTO_METHOD_TLSv1_3 (1<<6)
60
61 #ifndef OPENSSL_NO_TLS1_METHOD
62 #define HAVE_TLS1 1
63 #endif
64
65 #ifndef OPENSSL_NO_TLS1_1_METHOD
66 #define HAVE_TLS11 1
67 #endif
68
69 #ifndef OPENSSL_NO_TLS1_2_METHOD
70 #define HAVE_TLS12 1
71 #endif
72
73 #if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(OPENSSL_NO_TLS1_3)
74 #define HAVE_TLS13 1
75 #endif
76
77 #ifndef OPENSSL_NO_ECDH
78 #define HAVE_ECDH 1
79 #endif
80
81 #ifndef OPENSSL_NO_TLSEXT
82 #define HAVE_TLS_SNI 1
83 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
84 #define HAVE_TLS_ALPN 1
85 #endif
86 #endif
87
88 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
89 #define HAVE_SEC_LEVEL 1
90 #endif
91
92 #ifndef OPENSSL_NO_SSL3
93 #define HAVE_SSL3 1
94 #define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3
95 #else
96 #define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_0
97 #endif
98 #ifdef HAVE_TLS13
99 #define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_3
100 #else
101 #define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_2
102 #endif
103
104 /* Simplify ssl context option retrieval */
105 #define GET_VER_OPT(name) \
106 (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL)
107 #define GET_VER_OPT_STRING(name, str) \
108 if (GET_VER_OPT(name)) { \
109 if (try_convert_to_string(val)) str = Z_STRVAL_P(val); \
110 }
111 #define GET_VER_OPT_LONG(name, num) \
112 if (GET_VER_OPT(name)) { num = zval_get_long(val); }
113
114 /* Used for peer verification in windows */
115 #define PHP_X509_NAME_ENTRY_TO_UTF8(ne, i, out) \
116 ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(ne, i)))
117
118 #if PHP_OPENSSL_API_VERSION < 0x10100
119 static RSA *php_openssl_tmp_rsa_cb(SSL *s, int is_export, int keylength);
120 #endif
121
122 extern php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl);
123 extern zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw);
124 extern int php_openssl_get_ssl_stream_data_index();
125 static struct timeval php_openssl_subtract_timeval(struct timeval a, struct timeval b);
126 static int php_openssl_compare_timeval(struct timeval a, struct timeval b);
127 static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, size_t count);
128
129 const php_stream_ops php_openssl_socket_ops;
130
131 /* Certificate contexts used for server-side SNI selection */
132 typedef struct _php_openssl_sni_cert_t {
133 char *name;
134 SSL_CTX *ctx;
135 } php_openssl_sni_cert_t;
136
137 /* Provides leaky bucket handhsake renegotiation rate-limiting */
138 typedef struct _php_openssl_handshake_bucket_t {
139 zend_long prev_handshake;
140 zend_long limit;
141 zend_long window;
142 float tokens;
143 unsigned should_close;
144 } php_openssl_handshake_bucket_t;
145
146 #ifdef HAVE_TLS_ALPN
147 /* Holds the available server ALPN protocols for negotiation */
148 typedef struct _php_openssl_alpn_ctx_t {
149 unsigned char *data;
150 unsigned short len;
151 } php_openssl_alpn_ctx;
152 #endif
153
154 /* This implementation is very closely tied to the that of the native
155 * sockets implemented in the core.
156 * Don't try this technique in other extensions!
157 * */
158 typedef struct _php_openssl_netstream_data_t {
159 php_netstream_data_t s;
160 SSL *ssl_handle;
161 SSL_CTX *ctx;
162 struct timeval connect_timeout;
163 int enable_on_connect;
164 int is_client;
165 int ssl_active;
166 php_stream_xport_crypt_method_t method;
167 php_openssl_handshake_bucket_t *reneg;
168 php_openssl_sni_cert_t *sni_certs;
169 unsigned sni_cert_count;
170 #ifdef HAVE_TLS_ALPN
171 php_openssl_alpn_ctx alpn_ctx;
172 #endif
173 char *url_name;
174 unsigned state_set:1;
175 unsigned _spare:31;
176 } php_openssl_netstream_data_t;
177
178 /* it doesn't matter that we do some hash traversal here, since it is done only
179 * in an error condition arising from a network connection problem */
php_openssl_is_http_stream_talking_to_iis(php_stream * stream)180 static int php_openssl_is_http_stream_talking_to_iis(php_stream *stream) /* {{{ */
181 {
182 if (Z_TYPE(stream->wrapperdata) == IS_ARRAY &&
183 stream->wrapper &&
184 strcasecmp(stream->wrapper->wops->label, "HTTP") == 0
185 ) {
186 /* the wrapperdata is an array zval containing the headers */
187 zval *tmp;
188
189 #define SERVER_MICROSOFT_IIS "Server: Microsoft-IIS"
190 #define SERVER_GOOGLE "Server: GFE/"
191
192 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(stream->wrapperdata), tmp) {
193 if (strncasecmp(Z_STRVAL_P(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) {
194 return 1;
195 } else if (strncasecmp(Z_STRVAL_P(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) {
196 return 1;
197 }
198 } ZEND_HASH_FOREACH_END();
199 }
200 return 0;
201 }
202 /* }}} */
203
php_openssl_handle_ssl_error(php_stream * stream,int nr_bytes,zend_bool is_init)204 static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init) /* {{{ */
205 {
206 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
207 int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
208 char esbuf[512];
209 smart_str ebuf = {0};
210 unsigned long ecode;
211 int retry = 1;
212
213 switch(err) {
214 case SSL_ERROR_ZERO_RETURN:
215 /* SSL terminated (but socket may still be active) */
216 retry = 0;
217 break;
218 case SSL_ERROR_WANT_READ:
219 case SSL_ERROR_WANT_WRITE:
220 /* re-negotiation, or perhaps the SSL layer needs more
221 * packets: retry in next iteration */
222 errno = EAGAIN;
223 retry = is_init ? 1 : sslsock->s.is_blocked;
224 break;
225 case SSL_ERROR_SYSCALL:
226 if (ERR_peek_error() == 0) {
227 if (nr_bytes == 0) {
228 if (!php_openssl_is_http_stream_talking_to_iis(stream) && ERR_get_error() != 0) {
229 php_error_docref(NULL, E_WARNING, "SSL: fatal protocol error");
230 }
231 SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
232 stream->eof = 1;
233 retry = 0;
234 } else {
235 char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
236
237 php_error_docref(NULL, E_WARNING,
238 "SSL: %s", estr);
239
240 efree(estr);
241 retry = 0;
242 }
243 break;
244 }
245
246
247 /* fall through */
248 default:
249 /* some other error */
250 ecode = ERR_get_error();
251
252 switch (ERR_GET_REASON(ecode)) {
253 case SSL_R_NO_SHARED_CIPHER:
254 php_error_docref(NULL, E_WARNING,
255 "SSL_R_NO_SHARED_CIPHER: no suitable shared cipher could be used. "
256 "This could be because the server is missing an SSL certificate "
257 "(local_cert context option)");
258 retry = 0;
259 break;
260
261 default:
262 do {
263 /* NULL is automatically added */
264 ERR_error_string_n(ecode, esbuf, sizeof(esbuf));
265 if (ebuf.s) {
266 smart_str_appendc(&ebuf, '\n');
267 }
268 smart_str_appends(&ebuf, esbuf);
269 } while ((ecode = ERR_get_error()) != 0);
270
271 smart_str_0(&ebuf);
272
273 php_error_docref(NULL, E_WARNING,
274 "SSL operation failed with code %d. %s%s",
275 err,
276 ebuf.s ? "OpenSSL Error messages:\n" : "",
277 ebuf.s ? ZSTR_VAL(ebuf.s) : "");
278 if (ebuf.s) {
279 smart_str_free(&ebuf);
280 }
281 }
282
283 retry = 0;
284 errno = 0;
285 }
286 return retry;
287 }
288 /* }}} */
289
verify_callback(int preverify_ok,X509_STORE_CTX * ctx)290 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
291 {
292 php_stream *stream;
293 SSL *ssl;
294 int err, depth, ret;
295 zval *val;
296 zend_ulong allowed_depth = OPENSSL_DEFAULT_STREAM_VERIFY_DEPTH;
297
298
299 ret = preverify_ok;
300
301 /* determine the status for the current cert */
302 err = X509_STORE_CTX_get_error(ctx);
303 depth = X509_STORE_CTX_get_error_depth(ctx);
304
305 /* conjure the stream & context to use */
306 ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
307 stream = (php_stream*)SSL_get_ex_data(ssl, php_openssl_get_ssl_stream_data_index());
308
309 /* if allow_self_signed is set, make sure that verification succeeds */
310 if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
311 GET_VER_OPT("allow_self_signed") &&
312 zend_is_true(val)
313 ) {
314 ret = 1;
315 }
316
317 /* check the depth */
318 GET_VER_OPT_LONG("verify_depth", allowed_depth);
319 if ((zend_ulong)depth > allowed_depth) {
320 ret = 0;
321 X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
322 }
323
324 return ret;
325 }
326 /* }}} */
327
php_openssl_x509_fingerprint_cmp(X509 * peer,const char * method,const char * expected)328 static int php_openssl_x509_fingerprint_cmp(X509 *peer, const char *method, const char *expected)
329 {
330 zend_string *fingerprint;
331 int result = -1;
332
333 fingerprint = php_openssl_x509_fingerprint(peer, method, 0);
334 if (fingerprint) {
335 result = strcasecmp(expected, ZSTR_VAL(fingerprint));
336 zend_string_release_ex(fingerprint, 0);
337 }
338
339 return result;
340 }
341
php_openssl_x509_fingerprint_match(X509 * peer,zval * val)342 static zend_bool php_openssl_x509_fingerprint_match(X509 *peer, zval *val)
343 {
344 if (Z_TYPE_P(val) == IS_STRING) {
345 const char *method = NULL;
346
347 switch (Z_STRLEN_P(val)) {
348 case 32:
349 method = "md5";
350 break;
351
352 case 40:
353 method = "sha1";
354 break;
355 }
356
357 return method && php_openssl_x509_fingerprint_cmp(peer, method, Z_STRVAL_P(val)) == 0;
358 } else if (Z_TYPE_P(val) == IS_ARRAY) {
359 zval *current;
360 zend_string *key;
361
362 if (!zend_hash_num_elements(Z_ARRVAL_P(val))) {
363 php_error_docref(NULL, E_WARNING, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
364 return 0;
365 }
366
367 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(val), key, current) {
368 if (key == NULL || Z_TYPE_P(current) != IS_STRING) {
369 php_error_docref(NULL, E_WARNING, "Invalid peer_fingerprint array; [algo => fingerprint] form required");
370 return 0;
371 }
372 if (php_openssl_x509_fingerprint_cmp(peer, ZSTR_VAL(key), Z_STRVAL_P(current)) != 0) {
373 return 0;
374 }
375 } ZEND_HASH_FOREACH_END();
376
377 return 1;
378 } else {
379 php_error_docref(NULL, E_WARNING,
380 "Invalid peer_fingerprint value; fingerprint string or array of the form [algo => fingerprint] required");
381 }
382
383 return 0;
384 }
385
php_openssl_matches_wildcard_name(const char * subjectname,const char * certname)386 static zend_bool php_openssl_matches_wildcard_name(const char *subjectname, const char *certname) /* {{{ */
387 {
388 char *wildcard = NULL;
389 ptrdiff_t prefix_len;
390 size_t suffix_len, subject_len;
391
392 if (strcasecmp(subjectname, certname) == 0) {
393 return 1;
394 }
395
396 /* wildcard, if present, must only be present in the left-most component */
397 if (!(wildcard = strchr(certname, '*')) || memchr(certname, '.', wildcard - certname)) {
398 return 0;
399 }
400
401 /* 1) prefix, if not empty, must match subject */
402 prefix_len = wildcard - certname;
403 if (prefix_len && strncasecmp(subjectname, certname, prefix_len) != 0) {
404 return 0;
405 }
406
407 suffix_len = strlen(wildcard + 1);
408 subject_len = strlen(subjectname);
409 if (suffix_len <= subject_len) {
410 /* 2) suffix must match
411 * 3) no . between prefix and suffix
412 **/
413 return strcasecmp(wildcard + 1, subjectname + subject_len - suffix_len) == 0 &&
414 memchr(subjectname + prefix_len, '.', subject_len - suffix_len - prefix_len) == NULL;
415 }
416
417 return 0;
418 }
419 /* }}} */
420
php_openssl_matches_san_list(X509 * peer,const char * subject_name)421 static zend_bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) /* {{{ */
422 {
423 int i, len;
424 unsigned char *cert_name = NULL;
425 char ipbuffer[64];
426
427 GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
428 int alt_name_count = sk_GENERAL_NAME_num(alt_names);
429
430 for (i = 0; i < alt_name_count; i++) {
431 GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i);
432
433 if (san->type == GEN_DNS) {
434 ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName);
435 if ((size_t)ASN1_STRING_length(san->d.dNSName) != strlen((const char*)cert_name)) {
436 OPENSSL_free(cert_name);
437 /* prevent null-byte poisoning*/
438 continue;
439 }
440
441 /* accommodate valid FQDN entries ending in "." */
442 len = strlen((const char*)cert_name);
443 if (len && strcmp((const char *)&cert_name[len-1], ".") == 0) {
444 cert_name[len-1] = '\0';
445 }
446
447 if (php_openssl_matches_wildcard_name(subject_name, (const char *)cert_name)) {
448 OPENSSL_free(cert_name);
449 sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
450
451 return 1;
452 }
453 OPENSSL_free(cert_name);
454 } else if (san->type == GEN_IPADD) {
455 if (san->d.iPAddress->length == 4) {
456 sprintf(ipbuffer, "%d.%d.%d.%d",
457 san->d.iPAddress->data[0],
458 san->d.iPAddress->data[1],
459 san->d.iPAddress->data[2],
460 san->d.iPAddress->data[3]
461 );
462 if (strcasecmp(subject_name, (const char*)ipbuffer) == 0) {
463 sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
464
465 return 1;
466 }
467 }
468 /* No, we aren't bothering to check IPv6 addresses. Why?
469 * Because IP SAN names are officially deprecated and are
470 * not allowed by CAs starting in 2015. Deal with it.
471 */
472 }
473 }
474
475 sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
476
477 return 0;
478 }
479 /* }}} */
480
php_openssl_matches_common_name(X509 * peer,const char * subject_name)481 static zend_bool php_openssl_matches_common_name(X509 *peer, const char *subject_name) /* {{{ */
482 {
483 char buf[1024];
484 X509_NAME *cert_name;
485 zend_bool is_match = 0;
486 int cert_name_len;
487
488 cert_name = X509_get_subject_name(peer);
489 cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
490
491 if (cert_name_len == -1) {
492 php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
493 } else if ((size_t)cert_name_len != strlen(buf)) {
494 php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
495 } else if (php_openssl_matches_wildcard_name(subject_name, buf)) {
496 is_match = 1;
497 } else {
498 php_error_docref(NULL, E_WARNING,
499 "Peer certificate CN=`%.*s' did not match expected CN=`%s'",
500 cert_name_len, buf, subject_name);
501 }
502
503 return is_match;
504 }
505 /* }}} */
506
php_openssl_apply_peer_verification_policy(SSL * ssl,X509 * peer,php_stream * stream)507 static int php_openssl_apply_peer_verification_policy(SSL *ssl, X509 *peer, php_stream *stream) /* {{{ */
508 {
509 zval *val = NULL;
510 zval *peer_fingerprint;
511 char *peer_name = NULL;
512 int err,
513 must_verify_peer,
514 must_verify_peer_name,
515 must_verify_fingerprint;
516
517 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
518
519 must_verify_peer = GET_VER_OPT("verify_peer")
520 ? zend_is_true(val)
521 : sslsock->is_client;
522
523 must_verify_peer_name = GET_VER_OPT("verify_peer_name")
524 ? zend_is_true(val)
525 : sslsock->is_client;
526
527 must_verify_fingerprint = GET_VER_OPT("peer_fingerprint");
528 peer_fingerprint = val;
529
530 if ((must_verify_peer || must_verify_peer_name || must_verify_fingerprint) && peer == NULL) {
531 php_error_docref(NULL, E_WARNING, "Could not get peer certificate");
532 return FAILURE;
533 }
534
535 /* Verify the peer against using CA file/path settings */
536 if (must_verify_peer) {
537 err = SSL_get_verify_result(ssl);
538 switch (err) {
539 case X509_V_OK:
540 /* fine */
541 break;
542 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
543 if (GET_VER_OPT("allow_self_signed") && zend_is_true(val)) {
544 /* allowed */
545 break;
546 }
547 /* not allowed, so fall through */
548 default:
549 php_error_docref(NULL, E_WARNING,
550 "Could not verify peer: code:%d %s",
551 err,
552 X509_verify_cert_error_string(err)
553 );
554 return FAILURE;
555 }
556 }
557
558 /* If a peer_fingerprint match is required this trumps peer and peer_name verification */
559 if (must_verify_fingerprint) {
560 if (Z_TYPE_P(peer_fingerprint) == IS_STRING || Z_TYPE_P(peer_fingerprint) == IS_ARRAY) {
561 if (!php_openssl_x509_fingerprint_match(peer, peer_fingerprint)) {
562 php_error_docref(NULL, E_WARNING,
563 "peer_fingerprint match failure"
564 );
565 return FAILURE;
566 }
567 } else {
568 php_error_docref(NULL, E_WARNING,
569 "Expected peer fingerprint must be a string or an array"
570 );
571 return FAILURE;
572 }
573 }
574
575 /* verify the host name presented in the peer certificate */
576 if (must_verify_peer_name) {
577 GET_VER_OPT_STRING("peer_name", peer_name);
578
579 /* If no peer name was specified we use the autodetected url name in client environments */
580 if (peer_name == NULL && sslsock->is_client) {
581 peer_name = sslsock->url_name;
582 }
583
584 if (peer_name) {
585 if (php_openssl_matches_san_list(peer, peer_name)) {
586 return SUCCESS;
587 } else if (php_openssl_matches_common_name(peer, peer_name)) {
588 return SUCCESS;
589 } else {
590 return FAILURE;
591 }
592 } else {
593 return FAILURE;
594 }
595 }
596
597 return SUCCESS;
598 }
599 /* }}} */
600
php_openssl_passwd_callback(char * buf,int num,int verify,void * data)601 static int php_openssl_passwd_callback(char *buf, int num, int verify, void *data) /* {{{ */
602 {
603 php_stream *stream = (php_stream *)data;
604 zval *val = NULL;
605 char *passphrase = NULL;
606 /* TODO: could expand this to make a callback into PHP user-space */
607
608 GET_VER_OPT_STRING("passphrase", passphrase);
609
610 if (passphrase) {
611 if (Z_STRLEN_P(val) < (size_t)num - 1) {
612 memcpy(buf, Z_STRVAL_P(val), Z_STRLEN_P(val)+1);
613 return (int)Z_STRLEN_P(val);
614 }
615 }
616 return 0;
617 }
618 /* }}} */
619
620 #ifdef PHP_WIN32
621 #define RETURN_CERT_VERIFY_FAILURE(code) X509_STORE_CTX_set_error(x509_store_ctx, code); return 0;
php_openssl_win_cert_verify_callback(X509_STORE_CTX * x509_store_ctx,void * arg)622 static int php_openssl_win_cert_verify_callback(X509_STORE_CTX *x509_store_ctx, void *arg) /* {{{ */
623 {
624 PCCERT_CONTEXT cert_ctx = NULL;
625 PCCERT_CHAIN_CONTEXT cert_chain_ctx = NULL;
626 #if OPENSSL_VERSION_NUMBER < 0x10100000L
627 X509 *cert = x509_store_ctx->cert;
628 #else
629 X509 *cert = X509_STORE_CTX_get0_cert(x509_store_ctx);
630 #endif
631
632 php_stream *stream;
633 php_openssl_netstream_data_t *sslsock;
634 zval *val;
635 zend_bool is_self_signed = 0;
636
637
638 stream = (php_stream*)arg;
639 sslsock = (php_openssl_netstream_data_t*)stream->abstract;
640
641 { /* First convert the x509 struct back to a DER encoded buffer and let Windows decode it into a form it can work with */
642 unsigned char *der_buf = NULL;
643 int der_len;
644
645 der_len = i2d_X509(cert, &der_buf);
646 if (der_len < 0) {
647 unsigned long err_code, e;
648 char err_buf[512];
649
650 while ((e = ERR_get_error()) != 0) {
651 err_code = e;
652 }
653
654 php_error_docref(NULL, E_WARNING, "Error encoding X509 certificate: %d: %s", err_code, ERR_error_string(err_code, err_buf));
655 RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
656 }
657
658 cert_ctx = CertCreateCertificateContext(X509_ASN_ENCODING, der_buf, der_len);
659 OPENSSL_free(der_buf);
660
661 if (cert_ctx == NULL) {
662 char *err = php_win_err();
663 php_error_docref(NULL, E_WARNING, "Error creating certificate context: %s", err);
664 php_win_err_free(err);
665 RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
666 }
667 }
668
669 { /* Next fetch the relevant cert chain from the store */
670 CERT_ENHKEY_USAGE enhkey_usage = {0};
671 CERT_USAGE_MATCH cert_usage = {0};
672 CERT_CHAIN_PARA chain_params = {sizeof(CERT_CHAIN_PARA)};
673 LPSTR usages[] = {szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE};
674 DWORD chain_flags = 0;
675 unsigned long allowed_depth = OPENSSL_DEFAULT_STREAM_VERIFY_DEPTH;
676 unsigned int i;
677
678 enhkey_usage.cUsageIdentifier = 3;
679 enhkey_usage.rgpszUsageIdentifier = usages;
680 cert_usage.dwType = USAGE_MATCH_TYPE_OR;
681 cert_usage.Usage = enhkey_usage;
682 chain_params.RequestedUsage = cert_usage;
683 chain_flags = CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
684
685 if (!CertGetCertificateChain(NULL, cert_ctx, NULL, NULL, &chain_params, chain_flags, NULL, &cert_chain_ctx)) {
686 char *err = php_win_err();
687 php_error_docref(NULL, E_WARNING, "Error getting certificate chain: %s", err);
688 php_win_err_free(err);
689 CertFreeCertificateContext(cert_ctx);
690 RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
691 }
692
693 /* check if the cert is self-signed */
694 if (cert_chain_ctx->cChain > 0 && cert_chain_ctx->rgpChain[0]->cElement > 0
695 && (cert_chain_ctx->rgpChain[0]->rgpElement[0]->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) != 0) {
696 is_self_signed = 1;
697 }
698
699 /* check the depth */
700 GET_VER_OPT_LONG("verify_depth", allowed_depth);
701
702 for (i = 0; i < cert_chain_ctx->cChain; i++) {
703 if (cert_chain_ctx->rgpChain[i]->cElement > allowed_depth) {
704 CertFreeCertificateChain(cert_chain_ctx);
705 CertFreeCertificateContext(cert_ctx);
706 RETURN_CERT_VERIFY_FAILURE(X509_V_ERR_CERT_CHAIN_TOO_LONG);
707 }
708 }
709 }
710
711 { /* Then verify it against a policy */
712 SSL_EXTRA_CERT_CHAIN_POLICY_PARA ssl_policy_params = {sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA)};
713 CERT_CHAIN_POLICY_PARA chain_policy_params = {sizeof(CERT_CHAIN_POLICY_PARA)};
714 CERT_CHAIN_POLICY_STATUS chain_policy_status = {sizeof(CERT_CHAIN_POLICY_STATUS)};
715 LPWSTR server_name = NULL;
716 BOOL verify_result;
717
718 ssl_policy_params.dwAuthType = (sslsock->is_client) ? AUTHTYPE_SERVER : AUTHTYPE_CLIENT;
719 /* we validate the name ourselves using the peer_name
720 ctx option, so no need to use a server name here */
721 ssl_policy_params.pwszServerName = NULL;
722 chain_policy_params.pvExtraPolicyPara = &ssl_policy_params;
723
724 verify_result = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, cert_chain_ctx, &chain_policy_params, &chain_policy_status);
725
726 CertFreeCertificateChain(cert_chain_ctx);
727 CertFreeCertificateContext(cert_ctx);
728
729 if (!verify_result) {
730 char *err = php_win_err();
731 php_error_docref(NULL, E_WARNING, "Error verifying certificate chain policy: %s", err);
732 php_win_err_free(err);
733 RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
734 }
735
736 if (chain_policy_status.dwError != 0) {
737 /* The chain does not match the policy */
738 if (is_self_signed && chain_policy_status.dwError == CERT_E_UNTRUSTEDROOT
739 && GET_VER_OPT("allow_self_signed") && zend_is_true(val)) {
740 /* allow self-signed certs */
741 X509_STORE_CTX_set_error(x509_store_ctx, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
742 } else {
743 RETURN_CERT_VERIFY_FAILURE(SSL_R_CERTIFICATE_VERIFY_FAILED);
744 }
745 }
746 }
747
748 return 1;
749 }
750 /* }}} */
751 #endif
752
php_openssl_load_stream_cafile(X509_STORE * cert_store,const char * cafile)753 static long php_openssl_load_stream_cafile(X509_STORE *cert_store, const char *cafile) /* {{{ */
754 {
755 php_stream *stream;
756 X509 *cert;
757 BIO *buffer;
758 int buffer_active = 0;
759 char *line = NULL;
760 size_t line_len;
761 long certs_added = 0;
762
763 stream = php_stream_open_wrapper(cafile, "rb", 0, NULL);
764
765 if (stream == NULL) {
766 php_error(E_WARNING, "failed loading cafile stream: `%s'", cafile);
767 return 0;
768 } else if (stream->wrapper->is_url) {
769 php_stream_close(stream);
770 php_error(E_WARNING, "remote cafile streams are disabled for security purposes");
771 return 0;
772 }
773
774 cert_start: {
775 line = php_stream_get_line(stream, NULL, 0, &line_len);
776 if (line == NULL) {
777 goto stream_complete;
778 } else if (!strcmp(line, "-----BEGIN CERTIFICATE-----\n") ||
779 !strcmp(line, "-----BEGIN CERTIFICATE-----\r\n")
780 ) {
781 buffer = BIO_new(BIO_s_mem());
782 buffer_active = 1;
783 goto cert_line;
784 } else {
785 efree(line);
786 goto cert_start;
787 }
788 }
789
790 cert_line: {
791 BIO_puts(buffer, line);
792 efree(line);
793 line = php_stream_get_line(stream, NULL, 0, &line_len);
794 if (line == NULL) {
795 goto stream_complete;
796 } else if (!strcmp(line, "-----END CERTIFICATE-----") ||
797 !strcmp(line, "-----END CERTIFICATE-----\n") ||
798 !strcmp(line, "-----END CERTIFICATE-----\r\n")
799 ) {
800 goto add_cert;
801 } else {
802 goto cert_line;
803 }
804 }
805
806 add_cert: {
807 BIO_puts(buffer, line);
808 efree(line);
809 cert = PEM_read_bio_X509(buffer, NULL, 0, NULL);
810 BIO_free(buffer);
811 buffer_active = 0;
812 if (cert && X509_STORE_add_cert(cert_store, cert)) {
813 ++certs_added;
814 X509_free(cert);
815 }
816 goto cert_start;
817 }
818
819 stream_complete: {
820 php_stream_close(stream);
821 if (buffer_active == 1) {
822 BIO_free(buffer);
823 }
824 }
825
826 if (certs_added == 0) {
827 php_error(E_WARNING, "no valid certs found cafile stream: `%s'", cafile);
828 }
829
830 return certs_added;
831 }
832 /* }}} */
833
php_openssl_enable_peer_verification(SSL_CTX * ctx,php_stream * stream)834 static int php_openssl_enable_peer_verification(SSL_CTX *ctx, php_stream *stream) /* {{{ */
835 {
836 zval *val = NULL;
837 char *cafile = NULL;
838 char *capath = NULL;
839 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
840
841 GET_VER_OPT_STRING("cafile", cafile);
842 GET_VER_OPT_STRING("capath", capath);
843
844 if (cafile == NULL) {
845 cafile = zend_ini_string("openssl.cafile", sizeof("openssl.cafile")-1, 0);
846 cafile = strlen(cafile) ? cafile : NULL;
847 } else if (!sslsock->is_client) {
848 /* Servers need to load and assign CA names from the cafile */
849 STACK_OF(X509_NAME) *cert_names = SSL_load_client_CA_file(cafile);
850 if (cert_names != NULL) {
851 SSL_CTX_set_client_CA_list(ctx, cert_names);
852 } else {
853 php_error(E_WARNING, "SSL: failed loading CA names from cafile");
854 return FAILURE;
855 }
856 }
857
858 if (capath == NULL) {
859 capath = zend_ini_string("openssl.capath", sizeof("openssl.capath")-1, 0);
860 capath = strlen(capath) ? capath : NULL;
861 }
862
863 if (cafile || capath) {
864 if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
865 ERR_clear_error();
866 if (cafile && !php_openssl_load_stream_cafile(SSL_CTX_get_cert_store(ctx), cafile)) {
867 return FAILURE;
868 }
869 }
870 } else {
871 #ifdef PHP_WIN32
872 SSL_CTX_set_cert_verify_callback(ctx, php_openssl_win_cert_verify_callback, (void *)stream);
873 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
874 #else
875 if (sslsock->is_client && !SSL_CTX_set_default_verify_paths(ctx)) {
876 php_error_docref(NULL, E_WARNING,
877 "Unable to set default verify locations and no CA settings specified");
878 return FAILURE;
879 }
880 #endif
881 }
882
883 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
884
885 return SUCCESS;
886 }
887 /* }}} */
888
php_openssl_disable_peer_verification(SSL_CTX * ctx,php_stream * stream)889 static void php_openssl_disable_peer_verification(SSL_CTX *ctx, php_stream *stream) /* {{{ */
890 {
891 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
892 }
893 /* }}} */
894
php_openssl_set_local_cert(SSL_CTX * ctx,php_stream * stream)895 static int php_openssl_set_local_cert(SSL_CTX *ctx, php_stream *stream) /* {{{ */
896 {
897 zval *val = NULL;
898 char *certfile = NULL;
899
900 GET_VER_OPT_STRING("local_cert", certfile);
901
902 if (certfile) {
903 char resolved_path_buff[MAXPATHLEN];
904 const char *private_key = NULL;
905
906 if (VCWD_REALPATH(certfile, resolved_path_buff)) {
907 /* a certificate to use for authentication */
908 if (SSL_CTX_use_certificate_chain_file(ctx, resolved_path_buff) != 1) {
909 php_error_docref(NULL, E_WARNING,
910 "Unable to set local cert chain file `%s'; Check that your cafile/capath "
911 "settings include details of your certificate and its issuer",
912 certfile);
913 return FAILURE;
914 }
915 GET_VER_OPT_STRING("local_pk", private_key);
916
917 if (private_key) {
918 char resolved_path_buff_pk[MAXPATHLEN];
919 if (VCWD_REALPATH(private_key, resolved_path_buff_pk)) {
920 if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff_pk, SSL_FILETYPE_PEM) != 1) {
921 php_error_docref(NULL, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff_pk);
922 return FAILURE;
923 }
924 }
925 } else {
926 if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) {
927 php_error_docref(NULL, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff);
928 return FAILURE;
929 }
930 }
931
932 if (!SSL_CTX_check_private_key(ctx)) {
933 php_error_docref(NULL, E_WARNING, "Private key does not match certificate!");
934 }
935 }
936 }
937
938 return SUCCESS;
939 }
940 /* }}} */
941
942 #if PHP_OPENSSL_API_VERSION < 0x10100
php_openssl_get_crypto_method_ctx_flags(int method_flags)943 static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
944 {
945 int ssl_ctx_options = SSL_OP_ALL;
946
947 #ifdef SSL_OP_NO_SSLv2
948 ssl_ctx_options |= SSL_OP_NO_SSLv2;
949 #endif
950 #ifdef HAVE_SSL3
951 if (!(method_flags & STREAM_CRYPTO_METHOD_SSLv3)) {
952 ssl_ctx_options |= SSL_OP_NO_SSLv3;
953 }
954 #endif
955 #ifdef HAVE_TLS1
956 if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_0)) {
957 ssl_ctx_options |= SSL_OP_NO_TLSv1;
958 }
959 #endif
960 #ifdef HAVE_TLS11
961 if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_1)) {
962 ssl_ctx_options |= SSL_OP_NO_TLSv1_1;
963 }
964 #endif
965 #ifdef HAVE_TLS12
966 if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_2)) {
967 ssl_ctx_options |= SSL_OP_NO_TLSv1_2;
968 }
969 #endif
970 #ifdef HAVE_TLS13
971 if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_3)) {
972 ssl_ctx_options |= SSL_OP_NO_TLSv1_3;
973 }
974 #endif
975
976 return ssl_ctx_options;
977 }
978 /* }}} */
979 #endif
980
php_openssl_get_min_proto_version_flag(int flags)981 static inline int php_openssl_get_min_proto_version_flag(int flags) /* {{{ */
982 {
983 int ver;
984 for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
985 if (flags & ver) {
986 return ver;
987 }
988 }
989 return PHP_OPENSSL_MAX_PROTO_VERSION;
990 }
991 /* }}} */
992
php_openssl_get_max_proto_version_flag(int flags)993 static inline int php_openssl_get_max_proto_version_flag(int flags) /* {{{ */
994 {
995 int ver;
996 for (ver = PHP_OPENSSL_MAX_PROTO_VERSION; ver >= PHP_OPENSSL_MIN_PROTO_VERSION; ver >>= 1) {
997 if (flags & ver) {
998 return ver;
999 }
1000 }
1001 return STREAM_CRYPTO_METHOD_TLSv1_3;
1002 }
1003 /* }}} */
1004
1005 #if PHP_OPENSSL_API_VERSION >= 0x10100
php_openssl_map_proto_version(int flag)1006 static inline int php_openssl_map_proto_version(int flag) /* {{{ */
1007 {
1008 switch (flag) {
1009 #ifdef HAVE_TLS13
1010 case STREAM_CRYPTO_METHOD_TLSv1_3:
1011 return TLS1_3_VERSION;
1012 #endif
1013 case STREAM_CRYPTO_METHOD_TLSv1_2:
1014 return TLS1_2_VERSION;
1015 case STREAM_CRYPTO_METHOD_TLSv1_1:
1016 return TLS1_1_VERSION;
1017 case STREAM_CRYPTO_METHOD_TLSv1_0:
1018 return TLS1_VERSION;
1019 #ifdef HAVE_SSL3
1020 case STREAM_CRYPTO_METHOD_SSLv3:
1021 return SSL3_VERSION;
1022 #endif
1023 default:
1024 return TLS1_2_VERSION;
1025 }
1026 }
1027 /* }}} */
1028
php_openssl_get_min_proto_version(int flags)1029 static int php_openssl_get_min_proto_version(int flags) /* {{{ */
1030 {
1031 return php_openssl_map_proto_version(php_openssl_get_min_proto_version_flag(flags));
1032 }
1033 /* }}} */
1034
php_openssl_get_max_proto_version(int flags)1035 static int php_openssl_get_max_proto_version(int flags) /* {{{ */
1036 {
1037 return php_openssl_map_proto_version(php_openssl_get_max_proto_version_flag(flags));
1038 }
1039 /* }}} */
1040 #endif
1041
php_openssl_get_proto_version_flags(int flags,int min,int max)1042 static int php_openssl_get_proto_version_flags(int flags, int min, int max) /* {{{ */
1043 {
1044 int ver;
1045
1046 if (!min) {
1047 min = php_openssl_get_min_proto_version_flag(flags);
1048 }
1049 if (!max) {
1050 max = php_openssl_get_max_proto_version_flag(flags);
1051 }
1052
1053 for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
1054 if (ver >= min && ver <= max) {
1055 if (!(flags & ver)) {
1056 flags |= ver;
1057 }
1058 } else if (flags & ver) {
1059 flags &= ~ver;
1060 }
1061 }
1062
1063 return flags;
1064 }
1065 /* }}} */
1066
php_openssl_limit_handshake_reneg(const SSL * ssl)1067 static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */
1068 {
1069 php_stream *stream;
1070 php_openssl_netstream_data_t *sslsock;
1071 struct timeval now;
1072 zend_long elapsed_time;
1073
1074 stream = php_openssl_get_stream_from_ssl_handle(ssl);
1075 sslsock = (php_openssl_netstream_data_t*)stream->abstract;
1076 gettimeofday(&now, NULL);
1077
1078 /* The initial handshake is never rate-limited */
1079 if (sslsock->reneg->prev_handshake == 0) {
1080 sslsock->reneg->prev_handshake = now.tv_sec;
1081 return;
1082 }
1083
1084 elapsed_time = (now.tv_sec - sslsock->reneg->prev_handshake);
1085 sslsock->reneg->prev_handshake = now.tv_sec;
1086 sslsock->reneg->tokens -= (elapsed_time * (sslsock->reneg->limit / sslsock->reneg->window));
1087
1088 if (sslsock->reneg->tokens < 0) {
1089 sslsock->reneg->tokens = 0;
1090 }
1091 ++sslsock->reneg->tokens;
1092
1093 /* The token level exceeds our allowed limit */
1094 if (sslsock->reneg->tokens > sslsock->reneg->limit) {
1095 zval *val;
1096
1097
1098 sslsock->reneg->should_close = 1;
1099
1100 if (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream),
1101 "ssl", "reneg_limit_callback")) != NULL
1102 ) {
1103 zval param, retval;
1104
1105 php_stream_to_zval(stream, ¶m);
1106
1107 /* Closing the stream inside this callback would segfault! */
1108 stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
1109 if (FAILURE == call_user_function(NULL, NULL, val, &retval, 1, ¶m)) {
1110 php_error(E_WARNING, "SSL: failed invoking reneg limit notification callback");
1111 }
1112 stream->flags ^= PHP_STREAM_FLAG_NO_FCLOSE;
1113
1114 /* If the reneg_limit_callback returned true don't auto-close */
1115 if (Z_TYPE(retval) == IS_TRUE) {
1116 sslsock->reneg->should_close = 0;
1117 }
1118
1119 zval_ptr_dtor(&retval);
1120 } else {
1121 php_error_docref(NULL, E_WARNING,
1122 "SSL: client-initiated handshake rate limit exceeded by peer");
1123 }
1124 }
1125 }
1126 /* }}} */
1127
php_openssl_info_callback(const SSL * ssl,int where,int ret)1128 static void php_openssl_info_callback(const SSL *ssl, int where, int ret) /* {{{ */
1129 {
1130 /* Rate-limit client-initiated handshake renegotiation to prevent DoS */
1131 if (where & SSL_CB_HANDSHAKE_START) {
1132 php_openssl_limit_handshake_reneg(ssl);
1133 }
1134 }
1135 /* }}} */
1136
php_openssl_init_server_reneg_limit(php_stream * stream,php_openssl_netstream_data_t * sslsock)1137 static void php_openssl_init_server_reneg_limit(php_stream *stream, php_openssl_netstream_data_t *sslsock) /* {{{ */
1138 {
1139 zval *val;
1140 zend_long limit = OPENSSL_DEFAULT_RENEG_LIMIT;
1141 zend_long window = OPENSSL_DEFAULT_RENEG_WINDOW;
1142
1143 if (PHP_STREAM_CONTEXT(stream) &&
1144 NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "reneg_limit"))
1145 ) {
1146 limit = zval_get_long(val);
1147 }
1148
1149 /* No renegotiation rate-limiting */
1150 if (limit < 0) {
1151 return;
1152 }
1153
1154 if (PHP_STREAM_CONTEXT(stream) &&
1155 NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "reneg_window"))
1156 ) {
1157 window = zval_get_long(val);
1158 }
1159
1160 sslsock->reneg = (void*)pemalloc(sizeof(php_openssl_handshake_bucket_t),
1161 php_stream_is_persistent(stream)
1162 );
1163
1164 sslsock->reneg->limit = limit;
1165 sslsock->reneg->window = window;
1166 sslsock->reneg->prev_handshake = 0;
1167 sslsock->reneg->tokens = 0;
1168 sslsock->reneg->should_close = 0;
1169
1170 SSL_set_info_callback(sslsock->ssl_handle, php_openssl_info_callback);
1171 }
1172 /* }}} */
1173
1174 #if PHP_OPENSSL_API_VERSION < 0x10100
php_openssl_tmp_rsa_cb(SSL * s,int is_export,int keylength)1175 static RSA *php_openssl_tmp_rsa_cb(SSL *s, int is_export, int keylength)
1176 {
1177 BIGNUM *bn = NULL;
1178 static RSA *rsa_tmp = NULL;
1179
1180 if (!rsa_tmp && ((bn = BN_new()) == NULL)) {
1181 php_error_docref(NULL, E_WARNING, "allocation error generating RSA key");
1182 }
1183 if (!rsa_tmp && bn) {
1184 if (!BN_set_word(bn, RSA_F4) || ((rsa_tmp = RSA_new()) == NULL) ||
1185 !RSA_generate_key_ex(rsa_tmp, keylength, bn, NULL)) {
1186 if (rsa_tmp) {
1187 RSA_free(rsa_tmp);
1188 }
1189 rsa_tmp = NULL;
1190 }
1191 BN_free(bn);
1192 }
1193
1194 return (rsa_tmp);
1195 }
1196 #endif
1197
php_openssl_set_server_dh_param(php_stream * stream,SSL_CTX * ctx)1198 static int php_openssl_set_server_dh_param(php_stream * stream, SSL_CTX *ctx) /* {{{ */
1199 {
1200 DH *dh;
1201 BIO* bio;
1202 zval *zdhpath;
1203
1204 zdhpath = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "dh_param");
1205 if (zdhpath == NULL) {
1206 #if 0
1207 /* Coming in OpenSSL 1.1 ... eventually we'll want to enable this
1208 * in the absence of an explicit dh_param.
1209 */
1210 SSL_CTX_set_dh_auto(ctx, 1);
1211 #endif
1212 return SUCCESS;
1213 }
1214
1215 if (!try_convert_to_string(zdhpath)) {
1216 return FAILURE;
1217 }
1218
1219 bio = BIO_new_file(Z_STRVAL_P(zdhpath), PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));
1220
1221 if (bio == NULL) {
1222 php_error_docref(NULL, E_WARNING, "Invalid dh_param");
1223 return FAILURE;
1224 }
1225
1226 dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
1227 BIO_free(bio);
1228
1229 if (dh == NULL) {
1230 php_error_docref(NULL, E_WARNING, "Failed reading DH params");
1231 return FAILURE;
1232 }
1233
1234 if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) {
1235 php_error_docref(NULL, E_WARNING, "Failed assigning DH params");
1236 DH_free(dh);
1237 return FAILURE;
1238 }
1239
1240 DH_free(dh);
1241
1242 return SUCCESS;
1243 }
1244 /* }}} */
1245
1246 #if defined(HAVE_ECDH) && PHP_OPENSSL_API_VERSION < 0x10100
php_openssl_set_server_ecdh_curve(php_stream * stream,SSL_CTX * ctx)1247 static int php_openssl_set_server_ecdh_curve(php_stream *stream, SSL_CTX *ctx) /* {{{ */
1248 {
1249 zval *zvcurve;
1250 int curve_nid;
1251 EC_KEY *ecdh;
1252
1253 zvcurve = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "ecdh_curve");
1254 if (zvcurve == NULL) {
1255 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1256 SSL_CTX_set_ecdh_auto(ctx, 1);
1257 return SUCCESS;
1258 #else
1259 curve_nid = NID_X9_62_prime256v1;
1260 #endif
1261 } else {
1262 if (!try_convert_to_string(zvcurve)) {
1263 return FAILURE;
1264 }
1265
1266 curve_nid = OBJ_sn2nid(Z_STRVAL_P(zvcurve));
1267 if (curve_nid == NID_undef) {
1268 php_error_docref(NULL, E_WARNING, "Invalid ecdh_curve specified");
1269 return FAILURE;
1270 }
1271 }
1272
1273 ecdh = EC_KEY_new_by_curve_name(curve_nid);
1274 if (ecdh == NULL) {
1275 php_error_docref(NULL, E_WARNING, "Failed generating ECDH curve");
1276 return FAILURE;
1277 }
1278
1279 SSL_CTX_set_tmp_ecdh(ctx, ecdh);
1280 EC_KEY_free(ecdh);
1281
1282 return SUCCESS;
1283 }
1284 /* }}} */
1285 #endif
1286
php_openssl_set_server_specific_opts(php_stream * stream,SSL_CTX * ctx)1287 static int php_openssl_set_server_specific_opts(php_stream *stream, SSL_CTX *ctx) /* {{{ */
1288 {
1289 zval *zv;
1290 long ssl_ctx_options = SSL_CTX_get_options(ctx);
1291
1292 #if defined(HAVE_ECDH) && PHP_OPENSSL_API_VERSION < 0x10100
1293 if (php_openssl_set_server_ecdh_curve(stream, ctx) == FAILURE) {
1294 return FAILURE;
1295 }
1296 #endif
1297
1298 #if PHP_OPENSSL_API_VERSION < 0x10100
1299 SSL_CTX_set_tmp_rsa_callback(ctx, php_openssl_tmp_rsa_cb);
1300 #endif
1301 /* We now use php_openssl_tmp_rsa_cb to generate a key of appropriate size whenever necessary */
1302 if (php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "rsa_key_size") != NULL) {
1303 php_error_docref(NULL, E_WARNING, "rsa_key_size context option has been removed");
1304 }
1305
1306 php_openssl_set_server_dh_param(stream, ctx);
1307 zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "single_dh_use");
1308 if (zv == NULL || zend_is_true(zv)) {
1309 ssl_ctx_options |= SSL_OP_SINGLE_DH_USE;
1310 }
1311
1312 zv = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", "honor_cipher_order");
1313 if (zv == NULL || zend_is_true(zv)) {
1314 ssl_ctx_options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
1315 }
1316
1317 SSL_CTX_set_options(ctx, ssl_ctx_options);
1318
1319 return SUCCESS;
1320 }
1321 /* }}} */
1322
1323 #ifdef HAVE_TLS_SNI
php_openssl_server_sni_callback(SSL * ssl_handle,int * al,void * arg)1324 static int php_openssl_server_sni_callback(SSL *ssl_handle, int *al, void *arg) /* {{{ */
1325 {
1326 php_stream *stream;
1327 php_openssl_netstream_data_t *sslsock;
1328 unsigned i;
1329 const char *server_name;
1330
1331 server_name = SSL_get_servername(ssl_handle, TLSEXT_NAMETYPE_host_name);
1332
1333 if (!server_name) {
1334 return SSL_TLSEXT_ERR_NOACK;
1335 }
1336
1337 stream = (php_stream*)SSL_get_ex_data(ssl_handle, php_openssl_get_ssl_stream_data_index());
1338 sslsock = (php_openssl_netstream_data_t*)stream->abstract;
1339
1340 if (!(sslsock->sni_cert_count && sslsock->sni_certs)) {
1341 return SSL_TLSEXT_ERR_NOACK;
1342 }
1343
1344 for (i=0; i < sslsock->sni_cert_count; i++) {
1345 if (php_openssl_matches_wildcard_name(server_name, sslsock->sni_certs[i].name)) {
1346 SSL_set_SSL_CTX(ssl_handle, sslsock->sni_certs[i].ctx);
1347 return SSL_TLSEXT_ERR_OK;
1348 }
1349 }
1350
1351 return SSL_TLSEXT_ERR_NOACK;
1352 }
1353 /* }}} */
1354
php_openssl_create_sni_server_ctx(char * cert_path,char * key_path)1355 static SSL_CTX *php_openssl_create_sni_server_ctx(char *cert_path, char *key_path) /* {{{ */
1356 {
1357 /* The hello method is not inherited by SSL structs when assigning a new context
1358 * inside the SNI callback, so the just use SSLv23 */
1359 SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
1360
1361 if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) != 1) {
1362 php_error_docref(NULL, E_WARNING,
1363 "Failed setting local cert chain file `%s'; " \
1364 "check that your cafile/capath settings include " \
1365 "details of your certificate and its issuer",
1366 cert_path
1367 );
1368 SSL_CTX_free(ctx);
1369 return NULL;
1370 } else if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) != 1) {
1371 php_error_docref(NULL, E_WARNING,
1372 "Failed setting private key from file `%s'",
1373 key_path
1374 );
1375 SSL_CTX_free(ctx);
1376 return NULL;
1377 }
1378
1379 return ctx;
1380 }
1381 /* }}} */
1382
php_openssl_enable_server_sni(php_stream * stream,php_openssl_netstream_data_t * sslsock)1383 static int php_openssl_enable_server_sni(php_stream *stream, php_openssl_netstream_data_t *sslsock) /* {{{ */
1384 {
1385 zval *val;
1386 zval *current;
1387 zend_string *key;
1388 zend_ulong key_index;
1389 int i = 0;
1390 char resolved_path_buff[MAXPATHLEN];
1391 SSL_CTX *ctx;
1392
1393 /* If the stream ctx disables SNI we're finished here */
1394 if (GET_VER_OPT("SNI_enabled") && !zend_is_true(val)) {
1395 return SUCCESS;
1396 }
1397
1398 /* If no SNI cert array is specified we're finished here */
1399 if (!GET_VER_OPT("SNI_server_certs")) {
1400 return SUCCESS;
1401 }
1402
1403 if (Z_TYPE_P(val) != IS_ARRAY) {
1404 php_error_docref(NULL, E_WARNING,
1405 "SNI_server_certs requires an array mapping host names to cert paths"
1406 );
1407 return FAILURE;
1408 }
1409
1410 sslsock->sni_cert_count = zend_hash_num_elements(Z_ARRVAL_P(val));
1411 if (sslsock->sni_cert_count == 0) {
1412 php_error_docref(NULL, E_WARNING,
1413 "SNI_server_certs host cert array must not be empty"
1414 );
1415 return FAILURE;
1416 }
1417
1418 sslsock->sni_certs = (php_openssl_sni_cert_t*)safe_pemalloc(sslsock->sni_cert_count,
1419 sizeof(php_openssl_sni_cert_t), 0, php_stream_is_persistent(stream)
1420 );
1421 memset(sslsock->sni_certs, 0, sslsock->sni_cert_count * sizeof(php_openssl_sni_cert_t));
1422
1423 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(val), key_index, key, current) {
1424 (void) key_index;
1425
1426 if (!key) {
1427 php_error_docref(NULL, E_WARNING,
1428 "SNI_server_certs array requires string host name keys"
1429 );
1430 return FAILURE;
1431 }
1432
1433 if (Z_TYPE_P(current) == IS_ARRAY) {
1434 zval *local_pk, *local_cert;
1435 zend_string *local_pk_str, *local_cert_str;
1436 char resolved_cert_path_buff[MAXPATHLEN], resolved_pk_path_buff[MAXPATHLEN];
1437
1438 local_cert = zend_hash_str_find(Z_ARRVAL_P(current), "local_cert", sizeof("local_cert")-1);
1439 if (local_cert == NULL) {
1440 php_error_docref(NULL, E_WARNING,
1441 "local_cert not present in the array"
1442 );
1443 return FAILURE;
1444 }
1445
1446 local_cert_str = zval_try_get_string(local_cert);
1447 if (UNEXPECTED(!local_cert_str)) {
1448 return FAILURE;
1449 }
1450 if (!VCWD_REALPATH(ZSTR_VAL(local_cert_str), resolved_cert_path_buff)) {
1451 php_error_docref(NULL, E_WARNING,
1452 "Failed setting local cert chain file `%s'; file not found",
1453 ZSTR_VAL(local_cert_str)
1454 );
1455 zend_string_release(local_cert_str);
1456 return FAILURE;
1457 }
1458 zend_string_release(local_cert_str);
1459
1460 local_pk = zend_hash_str_find(Z_ARRVAL_P(current), "local_pk", sizeof("local_pk")-1);
1461 if (local_pk == NULL) {
1462 php_error_docref(NULL, E_WARNING,
1463 "local_pk not present in the array"
1464 );
1465 return FAILURE;
1466 }
1467
1468 local_pk_str = zval_try_get_string(local_pk);
1469 if (UNEXPECTED(!local_pk_str)) {
1470 return FAILURE;
1471 }
1472 if (!VCWD_REALPATH(ZSTR_VAL(local_pk_str), resolved_pk_path_buff)) {
1473 php_error_docref(NULL, E_WARNING,
1474 "Failed setting local private key file `%s'; file not found",
1475 ZSTR_VAL(local_pk_str)
1476 );
1477 zend_string_release(local_pk_str);
1478 return FAILURE;
1479 }
1480 zend_string_release(local_pk_str);
1481
1482 ctx = php_openssl_create_sni_server_ctx(resolved_cert_path_buff, resolved_pk_path_buff);
1483
1484 } else if (VCWD_REALPATH(Z_STRVAL_P(current), resolved_path_buff)) {
1485 ctx = php_openssl_create_sni_server_ctx(resolved_path_buff, resolved_path_buff);
1486 } else {
1487 php_error_docref(NULL, E_WARNING,
1488 "Failed setting local cert chain file `%s'; file not found",
1489 Z_STRVAL_P(current)
1490 );
1491 return FAILURE;
1492 }
1493
1494 if (ctx == NULL) {
1495 return FAILURE;
1496 }
1497
1498 sslsock->sni_certs[i].name = pestrdup(ZSTR_VAL(key), php_stream_is_persistent(stream));
1499 sslsock->sni_certs[i].ctx = ctx;
1500 ++i;
1501
1502 } ZEND_HASH_FOREACH_END();
1503
1504 SSL_CTX_set_tlsext_servername_callback(sslsock->ctx, php_openssl_server_sni_callback);
1505
1506 return SUCCESS;
1507 }
1508 /* }}} */
1509
php_openssl_enable_client_sni(php_stream * stream,php_openssl_netstream_data_t * sslsock)1510 static void php_openssl_enable_client_sni(php_stream *stream, php_openssl_netstream_data_t *sslsock) /* {{{ */
1511 {
1512 zval *val;
1513 char *sni_server_name;
1514
1515 /* If SNI is explicitly disabled we're finished here */
1516 if (GET_VER_OPT("SNI_enabled") && !zend_is_true(val)) {
1517 return;
1518 }
1519
1520 sni_server_name = sslsock->url_name;
1521
1522 GET_VER_OPT_STRING("peer_name", sni_server_name);
1523
1524 if (sni_server_name) {
1525 SSL_set_tlsext_host_name(sslsock->ssl_handle, sni_server_name);
1526 }
1527 }
1528 /* }}} */
1529 #endif
1530
1531 #ifdef HAVE_TLS_ALPN
1532 /**
1533 * Parses a comma-separated list of strings into a string suitable for SSL_CTX_set_next_protos_advertised
1534 * outlen: (output) set to the length of the resulting buffer on success.
1535 * err: (maybe NULL) on failure, an error message line is written to this BIO.
1536 * in: a NULL terminated string like "abc,def,ghi"
1537 *
1538 * returns: an emalloced buffer or NULL on failure.
1539 */
php_openssl_alpn_protos_parse(unsigned short * outlen,const char * in)1540 static unsigned char *php_openssl_alpn_protos_parse(unsigned short *outlen, const char *in) /* {{{ */
1541 {
1542 size_t len;
1543 unsigned char *out;
1544 size_t i, start = 0;
1545
1546 len = strlen(in);
1547 if (len >= 65535) {
1548 return NULL;
1549 }
1550
1551 out = emalloc(strlen(in) + 1);
1552
1553 for (i = 0; i <= len; ++i) {
1554 if (i == len || in[i] == ',') {
1555 if (i - start > 255) {
1556 efree(out);
1557 return NULL;
1558 }
1559 out[start] = i - start;
1560 start = i + 1;
1561 } else {
1562 out[i + 1] = in[i];
1563 }
1564 }
1565
1566 *outlen = len + 1;
1567
1568 return out;
1569 }
1570 /* }}} */
1571
php_openssl_server_alpn_callback(SSL * ssl_handle,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)1572 static int php_openssl_server_alpn_callback(SSL *ssl_handle,
1573 const unsigned char **out, unsigned char *outlen,
1574 const unsigned char *in, unsigned int inlen, void *arg) /* {{{ */
1575 {
1576 php_openssl_netstream_data_t *sslsock = arg;
1577
1578 if (SSL_select_next_proto((unsigned char **)out, outlen, sslsock->alpn_ctx.data, sslsock->alpn_ctx.len, in, inlen) != OPENSSL_NPN_NEGOTIATED) {
1579 return SSL_TLSEXT_ERR_NOACK;
1580 }
1581
1582 return SSL_TLSEXT_ERR_OK;
1583 }
1584 /* }}} */
1585
1586 #endif
1587
php_openssl_setup_crypto(php_stream * stream,php_openssl_netstream_data_t * sslsock,php_stream_xport_crypto_param * cparam)1588 int php_openssl_setup_crypto(php_stream *stream,
1589 php_openssl_netstream_data_t *sslsock,
1590 php_stream_xport_crypto_param *cparam) /* {{{ */
1591 {
1592 const SSL_METHOD *method;
1593 int ssl_ctx_options;
1594 int method_flags;
1595 zend_long min_version = 0;
1596 zend_long max_version = 0;
1597 char *cipherlist = NULL;
1598 char *alpn_protocols = NULL;
1599 zval *val;
1600
1601 if (sslsock->ssl_handle) {
1602 if (sslsock->s.is_blocked) {
1603 php_error_docref(NULL, E_WARNING, "SSL/TLS already set-up for this stream");
1604 return FAILURE;
1605 } else {
1606 return SUCCESS;
1607 }
1608 }
1609
1610 ERR_clear_error();
1611
1612 /* We need to do slightly different things based on client/server method
1613 * so lets remember which method was selected */
1614 sslsock->is_client = cparam->inputs.method & STREAM_CRYPTO_IS_CLIENT;
1615 method_flags = ((cparam->inputs.method >> 1) << 1);
1616
1617 method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method();
1618 sslsock->ctx = SSL_CTX_new(method);
1619
1620 GET_VER_OPT_LONG("min_proto_version", min_version);
1621 GET_VER_OPT_LONG("max_proto_version", max_version);
1622 method_flags = php_openssl_get_proto_version_flags(method_flags, min_version, max_version);
1623 #if PHP_OPENSSL_API_VERSION < 0x10100
1624 ssl_ctx_options = php_openssl_get_crypto_method_ctx_flags(method_flags);
1625 #else
1626 ssl_ctx_options = SSL_OP_ALL;
1627 #endif
1628
1629 if (sslsock->ctx == NULL) {
1630 php_error_docref(NULL, E_WARNING, "SSL context creation failure");
1631 return FAILURE;
1632 }
1633
1634 if (GET_VER_OPT("no_ticket") && zend_is_true(val)) {
1635 ssl_ctx_options |= SSL_OP_NO_TICKET;
1636 }
1637
1638 ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
1639
1640 if (!GET_VER_OPT("disable_compression") || zend_is_true(val)) {
1641 ssl_ctx_options |= SSL_OP_NO_COMPRESSION;
1642 }
1643
1644 if (GET_VER_OPT("verify_peer") && !zend_is_true(val)) {
1645 php_openssl_disable_peer_verification(sslsock->ctx, stream);
1646 } else if (FAILURE == php_openssl_enable_peer_verification(sslsock->ctx, stream)) {
1647 return FAILURE;
1648 }
1649
1650 /* callback for the passphrase (for localcert) */
1651 if (GET_VER_OPT("passphrase")) {
1652 SSL_CTX_set_default_passwd_cb_userdata(sslsock->ctx, stream);
1653 SSL_CTX_set_default_passwd_cb(sslsock->ctx, php_openssl_passwd_callback);
1654 }
1655
1656 GET_VER_OPT_STRING("ciphers", cipherlist);
1657 #ifndef USE_OPENSSL_SYSTEM_CIPHERS
1658 if (!cipherlist) {
1659 cipherlist = OPENSSL_DEFAULT_STREAM_CIPHERS;
1660 }
1661 #endif
1662 if (cipherlist) {
1663 if (SSL_CTX_set_cipher_list(sslsock->ctx, cipherlist) != 1) {
1664 return FAILURE;
1665 }
1666 }
1667
1668 if (GET_VER_OPT("security_level")) {
1669 zend_long lval = zval_get_long(val);
1670 if (lval < 0 || lval > 5) {
1671 php_error_docref(NULL, E_WARNING, "Security level must be between 0 and 5");
1672 }
1673 #ifdef HAVE_SEC_LEVEL
1674 SSL_CTX_set_security_level(sslsock->ctx, lval);
1675 #endif
1676 }
1677
1678 GET_VER_OPT_STRING("alpn_protocols", alpn_protocols);
1679 if (alpn_protocols) {
1680 #ifdef HAVE_TLS_ALPN
1681 {
1682 unsigned short alpn_len;
1683 unsigned char *alpn = php_openssl_alpn_protos_parse(&alpn_len, alpn_protocols);
1684
1685 if (alpn == NULL) {
1686 php_error_docref(NULL, E_WARNING, "Failed parsing comma-separated TLS ALPN protocol string");
1687 SSL_CTX_free(sslsock->ctx);
1688 sslsock->ctx = NULL;
1689 return FAILURE;
1690 }
1691 if (sslsock->is_client) {
1692 SSL_CTX_set_alpn_protos(sslsock->ctx, alpn, alpn_len);
1693 } else {
1694 sslsock->alpn_ctx.data = (unsigned char *) pestrndup((const char*)alpn, alpn_len, php_stream_is_persistent(stream));
1695 sslsock->alpn_ctx.len = alpn_len;
1696 SSL_CTX_set_alpn_select_cb(sslsock->ctx, php_openssl_server_alpn_callback, sslsock);
1697 }
1698
1699 efree(alpn);
1700 }
1701 #else
1702 php_error_docref(NULL, E_WARNING,
1703 "alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked");
1704 #endif
1705 }
1706
1707 if (FAILURE == php_openssl_set_local_cert(sslsock->ctx, stream)) {
1708 return FAILURE;
1709 }
1710
1711 SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options);
1712
1713 #if PHP_OPENSSL_API_VERSION >= 0x10100
1714 SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags));
1715 SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags));
1716 #endif
1717
1718 if (sslsock->is_client == 0 &&
1719 PHP_STREAM_CONTEXT(stream) &&
1720 FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx)
1721 ) {
1722 return FAILURE;
1723 }
1724
1725 sslsock->ssl_handle = SSL_new(sslsock->ctx);
1726
1727 if (sslsock->ssl_handle == NULL) {
1728 php_error_docref(NULL, E_WARNING, "SSL handle creation failure");
1729 SSL_CTX_free(sslsock->ctx);
1730 sslsock->ctx = NULL;
1731 #ifdef HAVE_TLS_ALPN
1732 if (sslsock->alpn_ctx.data) {
1733 pefree(sslsock->alpn_ctx.data, php_stream_is_persistent(stream));
1734 sslsock->alpn_ctx.data = NULL;
1735 }
1736 #endif
1737 return FAILURE;
1738 } else {
1739 SSL_set_ex_data(sslsock->ssl_handle, php_openssl_get_ssl_stream_data_index(), stream);
1740 }
1741
1742 if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
1743 php_openssl_handle_ssl_error(stream, 0, 1);
1744 }
1745
1746 #ifdef HAVE_TLS_SNI
1747 /* Enable server-side SNI */
1748 if (!sslsock->is_client && php_openssl_enable_server_sni(stream, sslsock) == FAILURE) {
1749 return FAILURE;
1750 }
1751 #endif
1752
1753 /* Enable server-side handshake renegotiation rate-limiting */
1754 if (!sslsock->is_client) {
1755 php_openssl_init_server_reneg_limit(stream, sslsock);
1756 }
1757
1758 #ifdef SSL_MODE_RELEASE_BUFFERS
1759 SSL_set_mode(sslsock->ssl_handle, SSL_get_mode(sslsock->ssl_handle) | SSL_MODE_RELEASE_BUFFERS);
1760 #endif
1761
1762 if (cparam->inputs.session) {
1763 if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
1764 php_error_docref(NULL, E_WARNING, "Supplied session stream must be an SSL enabled stream");
1765 } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) {
1766 php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not initialized");
1767 } else {
1768 SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
1769 }
1770 }
1771
1772 return SUCCESS;
1773 }
1774 /* }}} */
1775
php_openssl_capture_peer_certs(php_stream * stream,php_openssl_netstream_data_t * sslsock,X509 * peer_cert)1776 static int php_openssl_capture_peer_certs(php_stream *stream,
1777 php_openssl_netstream_data_t *sslsock, X509 *peer_cert) /* {{{ */
1778 {
1779 zval *val, zcert;
1780 php_openssl_certificate_object *cert_object;
1781 int cert_captured = 0;
1782
1783 if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream),
1784 "ssl", "capture_peer_cert")) &&
1785 zend_is_true(val)
1786 ) {
1787 object_init_ex(&zcert, php_openssl_certificate_ce);
1788 cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert);
1789 cert_object->x509 = peer_cert;
1790
1791 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &zcert);
1792 zval_ptr_dtor(&zcert);
1793 cert_captured = 1;
1794 }
1795
1796 if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream),
1797 "ssl", "capture_peer_cert_chain")) &&
1798 zend_is_true(val)
1799 ) {
1800 zval arr;
1801 STACK_OF(X509) *chain;
1802
1803 chain = SSL_get_peer_cert_chain(sslsock->ssl_handle);
1804
1805 if (chain && sk_X509_num(chain) > 0) {
1806 int i;
1807 array_init(&arr);
1808
1809 for (i = 0; i < sk_X509_num(chain); i++) {
1810 X509 *mycert = X509_dup(sk_X509_value(chain, i));
1811
1812 object_init_ex(&zcert, php_openssl_certificate_ce);
1813 cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert);
1814 cert_object->x509 = mycert;
1815 add_next_index_zval(&arr, &zcert);
1816 }
1817
1818 } else {
1819 ZVAL_NULL(&arr);
1820 }
1821
1822 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate_chain", &arr);
1823 zval_ptr_dtor(&arr);
1824 }
1825
1826 return cert_captured;
1827 }
1828 /* }}} */
1829
php_openssl_enable_crypto(php_stream * stream,php_openssl_netstream_data_t * sslsock,php_stream_xport_crypto_param * cparam)1830 static int php_openssl_enable_crypto(php_stream *stream,
1831 php_openssl_netstream_data_t *sslsock,
1832 php_stream_xport_crypto_param *cparam) /* {{{ */
1833 {
1834 int n;
1835 int retry = 1;
1836 int cert_captured = 0;
1837 X509 *peer_cert;
1838
1839 if (cparam->inputs.activate && !sslsock->ssl_active) {
1840 struct timeval start_time, *timeout;
1841 int blocked = sslsock->s.is_blocked, has_timeout = 0;
1842
1843 #ifdef HAVE_TLS_SNI
1844 if (sslsock->is_client) {
1845 php_openssl_enable_client_sni(stream, sslsock);
1846 }
1847 #endif
1848
1849 if (!sslsock->state_set) {
1850 if (sslsock->is_client) {
1851 SSL_set_connect_state(sslsock->ssl_handle);
1852 } else {
1853 SSL_set_accept_state(sslsock->ssl_handle);
1854 }
1855 sslsock->state_set = 1;
1856 }
1857
1858 if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0)) {
1859 sslsock->s.is_blocked = 0;
1860 /* The following mode are added only if we are able to change socket
1861 * to non blocking mode which is also used for read and write */
1862 SSL_set_mode(
1863 sslsock->ssl_handle,
1864 (
1865 SSL_get_mode(sslsock->ssl_handle) |
1866 SSL_MODE_ENABLE_PARTIAL_WRITE |
1867 SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
1868 )
1869 );
1870 }
1871
1872 timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
1873 has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec > 0 || (timeout->tv_sec == 0 && timeout->tv_usec));
1874 /* gettimeofday is not monotonic; using it here is not strictly correct */
1875 if (has_timeout) {
1876 gettimeofday(&start_time, NULL);
1877 }
1878
1879 do {
1880 struct timeval cur_time, elapsed_time;
1881
1882 ERR_clear_error();
1883 if (sslsock->is_client) {
1884 n = SSL_connect(sslsock->ssl_handle);
1885 } else {
1886 n = SSL_accept(sslsock->ssl_handle);
1887 }
1888
1889 if (has_timeout) {
1890 gettimeofday(&cur_time, NULL);
1891 elapsed_time = php_openssl_subtract_timeval(cur_time, start_time);
1892
1893 if (php_openssl_compare_timeval( elapsed_time, *timeout) > 0) {
1894 php_error_docref(NULL, E_WARNING, "SSL: Handshake timed out");
1895 return -1;
1896 }
1897 }
1898
1899 if (n <= 0) {
1900 /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
1901 retry = php_openssl_handle_ssl_error(stream, n, blocked);
1902 if (retry) {
1903 /* wait until something interesting happens in the socket. It may be a
1904 * timeout. Also consider the unlikely of possibility of a write block */
1905 int err = SSL_get_error(sslsock->ssl_handle, n);
1906 struct timeval left_time;
1907
1908 if (has_timeout) {
1909 left_time = php_openssl_subtract_timeval(*timeout, elapsed_time);
1910 }
1911 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
1912 (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
1913 }
1914 } else {
1915 retry = 0;
1916 }
1917 } while (retry);
1918
1919 if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked)) {
1920 sslsock->s.is_blocked = blocked;
1921 }
1922
1923 if (n == 1) {
1924 peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
1925 if (peer_cert && PHP_STREAM_CONTEXT(stream)) {
1926 cert_captured = php_openssl_capture_peer_certs(stream, sslsock, peer_cert);
1927 }
1928
1929 if (FAILURE == php_openssl_apply_peer_verification_policy(sslsock->ssl_handle, peer_cert, stream)) {
1930 SSL_shutdown(sslsock->ssl_handle);
1931 n = -1;
1932 } else {
1933 sslsock->ssl_active = 1;
1934 }
1935 } else if (errno == EAGAIN) {
1936 n = 0;
1937 } else {
1938 n = -1;
1939 /* We want to capture the peer cert even if verification fails*/
1940 peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
1941 if (peer_cert && PHP_STREAM_CONTEXT(stream)) {
1942 cert_captured = php_openssl_capture_peer_certs(stream, sslsock, peer_cert);
1943 }
1944 }
1945
1946 if (n && peer_cert && cert_captured == 0) {
1947 X509_free(peer_cert);
1948 }
1949
1950 return n;
1951
1952 } else if (!cparam->inputs.activate && sslsock->ssl_active) {
1953 /* deactivate - common for server/client */
1954 SSL_shutdown(sslsock->ssl_handle);
1955 sslsock->ssl_active = 0;
1956 }
1957
1958 return -1;
1959 }
1960 /* }}} */
1961
php_openssl_sockop_read(php_stream * stream,char * buf,size_t count)1962 static ssize_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count) /* {{{ */
1963 {
1964 return php_openssl_sockop_io( 1, stream, buf, count );
1965 }
1966 /* }}} */
1967
php_openssl_sockop_write(php_stream * stream,const char * buf,size_t count)1968 static ssize_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
1969 {
1970 return php_openssl_sockop_io( 0, stream, (char*)buf, count );
1971 }
1972 /* }}} */
1973
1974 /**
1975 * Factored out common functionality (blocking, timeout, loop management) for read and write.
1976 * Perform IO (read or write) to an SSL socket. If we have a timeout, we switch to non-blocking mode
1977 * for the duration of the operation, using select to do our waits. If we time out, or we have an error
1978 * report that back to PHP
1979 */
php_openssl_sockop_io(int read,php_stream * stream,char * buf,size_t count)1980 static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, size_t count) /* {{{ */
1981 {
1982 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
1983
1984 /* Only do this if SSL is active. */
1985 if (sslsock->ssl_active) {
1986 int retry = 1;
1987 struct timeval start_time;
1988 struct timeval *timeout = NULL;
1989 int began_blocked = sslsock->s.is_blocked;
1990 int has_timeout = 0;
1991 int nr_bytes = 0;
1992
1993 /* prevent overflow in openssl */
1994 if (count > INT_MAX) {
1995 count = INT_MAX;
1996 }
1997
1998 /* never use a timeout with non-blocking sockets */
1999 if (began_blocked) {
2000 timeout = &sslsock->s.timeout;
2001 }
2002
2003 if (timeout && php_set_sock_blocking(sslsock->s.socket, 0) == SUCCESS) {
2004 sslsock->s.is_blocked = 0;
2005 }
2006
2007 if (!sslsock->s.is_blocked && timeout && (timeout->tv_sec > 0 || (timeout->tv_sec == 0 && timeout->tv_usec))) {
2008 has_timeout = 1;
2009 /* gettimeofday is not monotonic; using it here is not strictly correct */
2010 gettimeofday(&start_time, NULL);
2011 }
2012
2013 /* Main IO loop. */
2014 do {
2015 struct timeval cur_time, elapsed_time, left_time;
2016
2017 /* If we have a timeout to check, figure out how much time has elapsed since we started. */
2018 if (has_timeout) {
2019 gettimeofday(&cur_time, NULL);
2020
2021 /* Determine how much time we've taken so far. */
2022 elapsed_time = php_openssl_subtract_timeval(cur_time, start_time);
2023
2024 /* and return an error if we've taken too long. */
2025 if (php_openssl_compare_timeval(elapsed_time, *timeout) > 0 ) {
2026 /* If the socket was originally blocking, set it back. */
2027 if (began_blocked) {
2028 php_set_sock_blocking(sslsock->s.socket, 1);
2029 sslsock->s.is_blocked = 1;
2030 }
2031 sslsock->s.timeout_event = 1;
2032 return -1;
2033 }
2034 }
2035
2036 /* Now, do the IO operation. Don't block if we can't complete... */
2037 ERR_clear_error();
2038 if (read) {
2039 nr_bytes = SSL_read(sslsock->ssl_handle, buf, (int)count);
2040
2041 if (sslsock->reneg && sslsock->reneg->should_close) {
2042 /* renegotiation rate limiting triggered */
2043 php_stream_xport_shutdown(stream, (stream_shutdown_t)SHUT_RDWR);
2044 nr_bytes = 0;
2045 stream->eof = 1;
2046 break;
2047 }
2048 } else {
2049 nr_bytes = SSL_write(sslsock->ssl_handle, buf, (int)count);
2050 }
2051
2052 /* Now, how much time until we time out? */
2053 if (has_timeout) {
2054 left_time = php_openssl_subtract_timeval( *timeout, elapsed_time );
2055 }
2056
2057 /* If we didn't do anything on the last loop (or an error) check to see if we should retry or exit. */
2058 if (nr_bytes <= 0) {
2059
2060 /* Get the error code from SSL, and check to see if it's an error or not. */
2061 int err = SSL_get_error(sslsock->ssl_handle, nr_bytes );
2062 retry = php_openssl_handle_ssl_error(stream, nr_bytes, 0);
2063
2064 /* If we get this (the above doesn't check) then we'll retry as well. */
2065 if (errno == EAGAIN && err == SSL_ERROR_WANT_READ && read) {
2066 retry = 1;
2067 }
2068 if (errno == EAGAIN && err == SSL_ERROR_WANT_WRITE && read == 0) {
2069 retry = 1;
2070 }
2071
2072 /* Also, on reads, we may get this condition on an EOF. We should check properly. */
2073 if (read) {
2074 stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
2075 }
2076
2077 /* Don't loop indefinitely in non-blocking mode if no data is available */
2078 if (began_blocked == 0) {
2079 break;
2080 }
2081
2082 /* Now, if we have to wait some time, and we're supposed to be blocking, wait for the socket to become
2083 * available. Now, php_pollfd_for uses select to wait up to our time_left value only...
2084 */
2085 if (retry) {
2086 if (read) {
2087 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_WRITE) ?
2088 (POLLOUT|POLLPRI) : (POLLIN|POLLPRI), has_timeout ? &left_time : NULL);
2089 } else {
2090 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
2091 (POLLIN|POLLPRI) : (POLLOUT|POLLPRI), has_timeout ? &left_time : NULL);
2092 }
2093 }
2094 } else {
2095 /* Else, if we got bytes back, check for possible errors. */
2096 int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
2097
2098 /* If we didn't get any error, then let's return it to PHP. */
2099 if (err == SSL_ERROR_NONE) {
2100 break;
2101 }
2102
2103 /* Otherwise, we need to wait again (up to time_left or we get an error) */
2104 if (began_blocked) {
2105 if (read) {
2106 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_WRITE) ?
2107 (POLLOUT|POLLPRI) : (POLLIN|POLLPRI), has_timeout ? &left_time : NULL);
2108 } else {
2109 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
2110 (POLLIN|POLLPRI) : (POLLOUT|POLLPRI), has_timeout ? &left_time : NULL);
2111 }
2112 }
2113 }
2114
2115 /* Finally, we keep going until we got data, and an SSL_ERROR_NONE, unless we had an error. */
2116 } while (retry);
2117
2118 /* Tell PHP if we read / wrote bytes. */
2119 if (nr_bytes > 0) {
2120 php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
2121 }
2122
2123 /* And if we were originally supposed to be blocking, let's reset the socket to that. */
2124 if (began_blocked && php_set_sock_blocking(sslsock->s.socket, 1) == SUCCESS) {
2125 sslsock->s.is_blocked = 1;
2126 }
2127
2128 return 0 > nr_bytes ? 0 : nr_bytes;
2129 } else {
2130 size_t nr_bytes = 0;
2131
2132 /* This block is if we had no timeout... We will just sit and wait forever on the IO operation. */
2133 if (read) {
2134 nr_bytes = php_stream_socket_ops.read(stream, buf, count);
2135 } else {
2136 nr_bytes = php_stream_socket_ops.write(stream, buf, count);
2137 }
2138
2139 return nr_bytes;
2140 }
2141 }
2142 /* }}} */
2143
php_openssl_subtract_timeval(struct timeval a,struct timeval b)2144 static struct timeval php_openssl_subtract_timeval(struct timeval a, struct timeval b) /* {{{ */
2145 {
2146 struct timeval difference;
2147
2148 difference.tv_sec = a.tv_sec - b.tv_sec;
2149 difference.tv_usec = a.tv_usec - b.tv_usec;
2150
2151 if (a.tv_usec < b.tv_usec) {
2152 difference.tv_sec -= 1L;
2153 difference.tv_usec += 1000000L;
2154 }
2155
2156 return difference;
2157 }
2158 /* }}} */
2159
php_openssl_compare_timeval(struct timeval a,struct timeval b)2160 static int php_openssl_compare_timeval( struct timeval a, struct timeval b )
2161 {
2162 if (a.tv_sec > b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_usec > b.tv_usec) ) {
2163 return 1;
2164 } else if( a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec ) {
2165 return 0;
2166 } else {
2167 return -1;
2168 }
2169 }
2170
php_openssl_sockop_close(php_stream * stream,int close_handle)2171 static int php_openssl_sockop_close(php_stream *stream, int close_handle) /* {{{ */
2172 {
2173 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
2174 #ifdef PHP_WIN32
2175 int n;
2176 #endif
2177 unsigned i;
2178
2179 if (close_handle) {
2180 if (sslsock->ssl_active) {
2181 SSL_shutdown(sslsock->ssl_handle);
2182 sslsock->ssl_active = 0;
2183 }
2184 if (sslsock->ssl_handle) {
2185 SSL_free(sslsock->ssl_handle);
2186 sslsock->ssl_handle = NULL;
2187 }
2188 if (sslsock->ctx) {
2189 SSL_CTX_free(sslsock->ctx);
2190 sslsock->ctx = NULL;
2191 }
2192 #ifdef HAVE_TLS_ALPN
2193 if (sslsock->alpn_ctx.data) {
2194 pefree(sslsock->alpn_ctx.data, php_stream_is_persistent(stream));
2195 }
2196 #endif
2197 #ifdef PHP_WIN32
2198 if (sslsock->s.socket == -1)
2199 sslsock->s.socket = SOCK_ERR;
2200 #endif
2201 if (sslsock->s.socket != SOCK_ERR) {
2202 #ifdef PHP_WIN32
2203 /* prevent more data from coming in */
2204 shutdown(sslsock->s.socket, SHUT_RD);
2205
2206 /* try to make sure that the OS sends all data before we close the connection.
2207 * Essentially, we are waiting for the socket to become writeable, which means
2208 * that all pending data has been sent.
2209 * We use a small timeout which should encourage the OS to send the data,
2210 * but at the same time avoid hanging indefinitely.
2211 * */
2212 do {
2213 n = php_pollfd_for_ms(sslsock->s.socket, POLLOUT, 500);
2214 } while (n == -1 && php_socket_errno() == EINTR);
2215 #endif
2216 closesocket(sslsock->s.socket);
2217 sslsock->s.socket = SOCK_ERR;
2218 }
2219 }
2220
2221 if (sslsock->sni_certs) {
2222 for (i = 0; i < sslsock->sni_cert_count; i++) {
2223 if (sslsock->sni_certs[i].ctx) {
2224 SSL_CTX_free(sslsock->sni_certs[i].ctx);
2225 pefree(sslsock->sni_certs[i].name, php_stream_is_persistent(stream));
2226 }
2227 }
2228 pefree(sslsock->sni_certs, php_stream_is_persistent(stream));
2229 sslsock->sni_certs = NULL;
2230 }
2231
2232 if (sslsock->url_name) {
2233 pefree(sslsock->url_name, php_stream_is_persistent(stream));
2234 }
2235
2236 if (sslsock->reneg) {
2237 pefree(sslsock->reneg, php_stream_is_persistent(stream));
2238 }
2239
2240 pefree(sslsock, php_stream_is_persistent(stream));
2241
2242 return 0;
2243 }
2244 /* }}} */
2245
php_openssl_sockop_flush(php_stream * stream)2246 static int php_openssl_sockop_flush(php_stream *stream) /* {{{ */
2247 {
2248 return php_stream_socket_ops.flush(stream);
2249 }
2250 /* }}} */
2251
php_openssl_sockop_stat(php_stream * stream,php_stream_statbuf * ssb)2252 static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
2253 {
2254 return php_stream_socket_ops.stat(stream, ssb);
2255 }
2256 /* }}} */
2257
php_openssl_tcp_sockop_accept(php_stream * stream,php_openssl_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC)2258 static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
2259 php_stream_xport_param *xparam STREAMS_DC) /* {{{ */
2260 {
2261 int clisock;
2262 zend_bool nodelay = 0;
2263 zval *tmpzval = NULL;
2264
2265 xparam->outputs.client = NULL;
2266
2267 if (PHP_STREAM_CONTEXT(stream) &&
2268 (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
2269 zend_is_true(tmpzval)) {
2270 nodelay = 1;
2271 }
2272
2273 clisock = php_network_accept_incoming(sock->s.socket,
2274 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
2275 xparam->want_addr ? &xparam->outputs.addr : NULL,
2276 xparam->want_addr ? &xparam->outputs.addrlen : NULL,
2277 xparam->inputs.timeout,
2278 xparam->want_errortext ? &xparam->outputs.error_text : NULL,
2279 &xparam->outputs.error_code,
2280 nodelay);
2281
2282 if (clisock >= 0) {
2283 php_openssl_netstream_data_t *clisockdata = (php_openssl_netstream_data_t*) emalloc(sizeof(*clisockdata));
2284
2285 /* copy underlying tcp fields */
2286 memset(clisockdata, 0, sizeof(*clisockdata));
2287 memcpy(clisockdata, sock, sizeof(clisockdata->s));
2288
2289 clisockdata->s.socket = clisock;
2290 #ifdef __linux__
2291 /* O_NONBLOCK is not inherited on Linux */
2292 clisockdata->s.is_blocked = 1;
2293 #endif
2294
2295 xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
2296 if (xparam->outputs.client) {
2297 xparam->outputs.client->ctx = stream->ctx;
2298 if (stream->ctx) {
2299 GC_ADDREF(stream->ctx);
2300 }
2301 }
2302
2303 if (xparam->outputs.client && sock->enable_on_connect) {
2304 /* remove the client bit */
2305 if (sock->method & STREAM_CRYPTO_IS_CLIENT) {
2306 sock->method = ((sock->method >> 1) << 1);
2307 }
2308
2309 clisockdata->method = sock->method;
2310
2311 if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
2312 NULL) < 0 || php_stream_xport_crypto_enable(
2313 xparam->outputs.client, 1) < 0) {
2314 php_error_docref(NULL, E_WARNING, "Failed to enable crypto");
2315
2316 php_stream_close(xparam->outputs.client);
2317 xparam->outputs.client = NULL;
2318 xparam->outputs.returncode = -1;
2319 }
2320 }
2321 }
2322
2323 return xparam->outputs.client == NULL ? -1 : 0;
2324 }
2325 /* }}} */
2326
php_openssl_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam)2327 static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
2328 {
2329 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
2330 php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
2331 php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
2332
2333 switch (option) {
2334 case PHP_STREAM_OPTION_META_DATA_API:
2335 if (sslsock->ssl_active) {
2336 zval tmp;
2337 char *proto_str;
2338 const SSL_CIPHER *cipher;
2339
2340 array_init(&tmp);
2341
2342 switch (SSL_version(sslsock->ssl_handle)) {
2343 #ifdef HAVE_TLS13
2344 case TLS1_3_VERSION: proto_str = "TLSv1.3"; break;
2345 #endif
2346 #ifdef HAVE_TLS12
2347 case TLS1_2_VERSION: proto_str = "TLSv1.2"; break;
2348 #endif
2349 #ifdef HAVE_TLS11
2350 case TLS1_1_VERSION: proto_str = "TLSv1.1"; break;
2351 #endif
2352 case TLS1_VERSION: proto_str = "TLSv1"; break;
2353 #ifdef HAVE_SSL3
2354 case SSL3_VERSION: proto_str = "SSLv3"; break;
2355 #endif
2356 default: proto_str = "UNKNOWN";
2357 }
2358
2359 cipher = SSL_get_current_cipher(sslsock->ssl_handle);
2360
2361 add_assoc_string(&tmp, "protocol", proto_str);
2362 add_assoc_string(&tmp, "cipher_name", (char *) SSL_CIPHER_get_name(cipher));
2363 add_assoc_long(&tmp, "cipher_bits", SSL_CIPHER_get_bits(cipher, NULL));
2364 add_assoc_string(&tmp, "cipher_version", SSL_CIPHER_get_version(cipher));
2365
2366 #ifdef HAVE_TLS_ALPN
2367 {
2368 const unsigned char *alpn_proto = NULL;
2369 unsigned int alpn_proto_len = 0;
2370
2371 SSL_get0_alpn_selected(sslsock->ssl_handle, &alpn_proto, &alpn_proto_len);
2372 if (alpn_proto) {
2373 add_assoc_stringl(&tmp, "alpn_protocol", (char *)alpn_proto, alpn_proto_len);
2374 }
2375 }
2376 #endif
2377 add_assoc_zval((zval *)ptrparam, "crypto", &tmp);
2378 }
2379
2380 add_assoc_bool((zval *)ptrparam, "timed_out", sslsock->s.timeout_event);
2381 add_assoc_bool((zval *)ptrparam, "blocked", sslsock->s.is_blocked);
2382 add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
2383
2384 return PHP_STREAM_OPTION_RETURN_OK;
2385
2386 case PHP_STREAM_OPTION_CHECK_LIVENESS:
2387 {
2388 struct timeval tv;
2389 char buf;
2390 int alive = 1;
2391
2392 if (value == -1) {
2393 if (sslsock->s.timeout.tv_sec == -1) {
2394 #ifdef _WIN32
2395 tv.tv_sec = (long)FG(default_socket_timeout);
2396 #else
2397 tv.tv_sec = (time_t)FG(default_socket_timeout);
2398 #endif
2399 tv.tv_usec = 0;
2400 } else {
2401 tv = sslsock->connect_timeout;
2402 }
2403 } else {
2404 tv.tv_sec = value;
2405 tv.tv_usec = 0;
2406 }
2407
2408 if (sslsock->s.socket == -1) {
2409 alive = 0;
2410 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
2411 if (sslsock->ssl_active) {
2412 int n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
2413 if (n <= 0) {
2414 int err = SSL_get_error(sslsock->ssl_handle, n);
2415 switch (err) {
2416 case SSL_ERROR_SYSCALL:
2417 alive = php_socket_errno() == EAGAIN;
2418 break;
2419 case SSL_ERROR_WANT_READ:
2420 case SSL_ERROR_WANT_WRITE:
2421 alive = 1;
2422 break;
2423 default:
2424 /* any other problem is a fatal error */
2425 alive = 0;
2426 }
2427 }
2428 } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
2429 alive = 0;
2430 }
2431 }
2432 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
2433 }
2434
2435 case PHP_STREAM_OPTION_CRYPTO_API:
2436
2437 switch(cparam->op) {
2438
2439 case STREAM_XPORT_CRYPTO_OP_SETUP:
2440 cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam);
2441 return PHP_STREAM_OPTION_RETURN_OK;
2442 break;
2443 case STREAM_XPORT_CRYPTO_OP_ENABLE:
2444 cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam);
2445 return PHP_STREAM_OPTION_RETURN_OK;
2446 break;
2447 default:
2448 /* fall through */
2449 break;
2450 }
2451
2452 break;
2453
2454 case PHP_STREAM_OPTION_XPORT_API:
2455 switch(xparam->op) {
2456
2457 case STREAM_XPORT_OP_CONNECT:
2458 case STREAM_XPORT_OP_CONNECT_ASYNC:
2459 /* TODO: Async connects need to check the enable_on_connect option when
2460 * we notice that the connect has actually been established */
2461 php_stream_socket_ops.set_option(stream, option, value, ptrparam);
2462
2463 if ((sslsock->enable_on_connect) &&
2464 ((xparam->outputs.returncode == 0) ||
2465 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC &&
2466 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
2467 {
2468 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL) < 0 ||
2469 php_stream_xport_crypto_enable(stream, 1) < 0) {
2470 php_error_docref(NULL, E_WARNING, "Failed to enable crypto");
2471 xparam->outputs.returncode = -1;
2472 }
2473 }
2474 return PHP_STREAM_OPTION_RETURN_OK;
2475
2476 case STREAM_XPORT_OP_ACCEPT:
2477 /* we need to copy the additional fields that the underlying tcp transport
2478 * doesn't know about */
2479 xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC);
2480
2481
2482 return PHP_STREAM_OPTION_RETURN_OK;
2483
2484 default:
2485 /* fall through */
2486 break;
2487 }
2488 }
2489
2490 return php_stream_socket_ops.set_option(stream, option, value, ptrparam);
2491 }
2492 /* }}} */
2493
php_openssl_sockop_cast(php_stream * stream,int castas,void ** ret)2494 static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret) /* {{{ */
2495 {
2496 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
2497
2498 switch(castas) {
2499 case PHP_STREAM_AS_STDIO:
2500 if (sslsock->ssl_active) {
2501 return FAILURE;
2502 }
2503 if (ret) {
2504 *ret = fdopen(sslsock->s.socket, stream->mode);
2505 if (*ret) {
2506 return SUCCESS;
2507 }
2508 return FAILURE;
2509 }
2510 return SUCCESS;
2511
2512 case PHP_STREAM_AS_FD_FOR_SELECT:
2513 if (ret) {
2514 size_t pending;
2515 if (stream->writepos == stream->readpos
2516 && sslsock->ssl_active
2517 && (pending = (size_t)SSL_pending(sslsock->ssl_handle)) > 0) {
2518 php_stream_fill_read_buffer(stream, pending < stream->chunk_size
2519 ? pending
2520 : stream->chunk_size);
2521 }
2522
2523 *(php_socket_t *)ret = sslsock->s.socket;
2524 }
2525 return SUCCESS;
2526
2527 case PHP_STREAM_AS_FD:
2528 case PHP_STREAM_AS_SOCKETD:
2529 if (sslsock->ssl_active) {
2530 return FAILURE;
2531 }
2532 if (ret) {
2533 *(php_socket_t *)ret = sslsock->s.socket;
2534 }
2535 return SUCCESS;
2536 default:
2537 return FAILURE;
2538 }
2539 }
2540 /* }}} */
2541
2542 const php_stream_ops php_openssl_socket_ops = {
2543 php_openssl_sockop_write, php_openssl_sockop_read,
2544 php_openssl_sockop_close, php_openssl_sockop_flush,
2545 "tcp_socket/ssl",
2546 NULL, /* seek */
2547 php_openssl_sockop_cast,
2548 php_openssl_sockop_stat,
2549 php_openssl_sockop_set_option,
2550 };
2551
php_openssl_get_crypto_method(php_stream_context * ctx,zend_long crypto_method)2552 static zend_long php_openssl_get_crypto_method(
2553 php_stream_context *ctx, zend_long crypto_method) /* {{{ */
2554 {
2555 zval *val;
2556
2557 if (ctx && (val = php_stream_context_get_option(ctx, "ssl", "crypto_method")) != NULL) {
2558 crypto_method = zval_get_long(val);
2559 crypto_method |= STREAM_CRYPTO_IS_CLIENT;
2560 }
2561
2562 return crypto_method;
2563 }
2564 /* }}} */
2565
php_openssl_get_url_name(const char * resourcename,size_t resourcenamelen,int is_persistent)2566 static char *php_openssl_get_url_name(const char *resourcename,
2567 size_t resourcenamelen, int is_persistent) /* {{{ */
2568 {
2569 php_url *url;
2570
2571 if (!resourcename) {
2572 return NULL;
2573 }
2574
2575 url = php_url_parse_ex(resourcename, resourcenamelen);
2576 if (!url) {
2577 return NULL;
2578 }
2579
2580 if (url->host) {
2581 const char * host = ZSTR_VAL(url->host);
2582 char * url_name = NULL;
2583 size_t len = ZSTR_LEN(url->host);
2584
2585 /* skip trailing dots */
2586 while (len && host[len-1] == '.') {
2587 --len;
2588 }
2589
2590 if (len) {
2591 url_name = pestrndup(host, len, is_persistent);
2592 }
2593
2594 php_url_free(url);
2595 return url_name;
2596 }
2597
2598 php_url_free(url);
2599 return NULL;
2600 }
2601 /* }}} */
2602
php_openssl_ssl_socket_factory(const char * proto,size_t protolen,const char * resourcename,size_t resourcenamelen,const char * persistent_id,int options,int flags,struct timeval * timeout,php_stream_context * context STREAMS_DC)2603 php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
2604 const char *resourcename, size_t resourcenamelen,
2605 const char *persistent_id, int options, int flags,
2606 struct timeval *timeout,
2607 php_stream_context *context STREAMS_DC) /* {{{ */
2608 {
2609 php_stream *stream = NULL;
2610 php_openssl_netstream_data_t *sslsock = NULL;
2611
2612 sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
2613 memset(sslsock, 0, sizeof(*sslsock));
2614
2615 sslsock->s.is_blocked = 1;
2616 /* this timeout is used by standard stream funcs, therefor it should use the default value */
2617 #ifdef _WIN32
2618 sslsock->s.timeout.tv_sec = (long)FG(default_socket_timeout);
2619 #else
2620 sslsock->s.timeout.tv_sec = (time_t)FG(default_socket_timeout);
2621 #endif
2622 sslsock->s.timeout.tv_usec = 0;
2623
2624 /* use separate timeout for our private funcs */
2625 sslsock->connect_timeout.tv_sec = timeout->tv_sec;
2626 sslsock->connect_timeout.tv_usec = timeout->tv_usec;
2627
2628 /* we don't know the socket until we have determined if we are binding or
2629 * connecting */
2630 sslsock->s.socket = -1;
2631
2632 /* Initialize context as NULL */
2633 sslsock->ctx = NULL;
2634
2635 stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
2636
2637 if (stream == NULL) {
2638 pefree(sslsock, persistent_id ? 1 : 0);
2639 return NULL;
2640 }
2641
2642 if (strncmp(proto, "ssl", protolen) == 0) {
2643 sslsock->enable_on_connect = 1;
2644 sslsock->method = php_openssl_get_crypto_method(context, STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT);
2645 } else if (strncmp(proto, "sslv2", protolen) == 0) {
2646 php_error_docref(NULL, E_WARNING, "SSLv2 unavailable in this PHP version");
2647 php_stream_close(stream);
2648 return NULL;
2649 } else if (strncmp(proto, "sslv3", protolen) == 0) {
2650 #ifdef HAVE_SSL3
2651 sslsock->enable_on_connect = 1;
2652 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
2653 #else
2654 php_error_docref(NULL, E_WARNING,
2655 "SSLv3 support is not compiled into the OpenSSL library against which PHP is linked");
2656 php_stream_close(stream);
2657 return NULL;
2658 #endif
2659 } else if (strncmp(proto, "tls", protolen) == 0) {
2660 sslsock->enable_on_connect = 1;
2661 sslsock->method = php_openssl_get_crypto_method(context, STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT);
2662 } else if (strncmp(proto, "tlsv1.0", protolen) == 0) {
2663 sslsock->enable_on_connect = 1;
2664 sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
2665 } else if (strncmp(proto, "tlsv1.1", protolen) == 0) {
2666 #ifdef HAVE_TLS11
2667 sslsock->enable_on_connect = 1;
2668 sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
2669 #else
2670 php_error_docref(NULL, E_WARNING,
2671 "TLSv1.1 support is not compiled into the OpenSSL library against which PHP is linked");
2672 php_stream_close(stream);
2673 return NULL;
2674 #endif
2675 } else if (strncmp(proto, "tlsv1.2", protolen) == 0) {
2676 #ifdef HAVE_TLS12
2677 sslsock->enable_on_connect = 1;
2678 sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
2679 #else
2680 php_error_docref(NULL, E_WARNING,
2681 "TLSv1.2 support is not compiled into the OpenSSL library against which PHP is linked");
2682 php_stream_close(stream);
2683 return NULL;
2684 #endif
2685 } else if (strncmp(proto, "tlsv1.3", protolen) == 0) {
2686 #ifdef HAVE_TLS13
2687 sslsock->enable_on_connect = 1;
2688 sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
2689 #else
2690 php_error_docref(NULL, E_WARNING,
2691 "TLSv1.3 support is not compiled into the OpenSSL library against which PHP is linked");
2692 php_stream_close(stream);
2693 return NULL;
2694 #endif
2695 }
2696
2697 sslsock->url_name = php_openssl_get_url_name(resourcename, resourcenamelen, !!persistent_id);
2698
2699 return stream;
2700 }
2701 /* }}} */
2702