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