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