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