xref: /PHP-5.6/ext/openssl/xp_ssl.c (revision 05baa927)
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 **)&current, &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] = &param;
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 **)&current, &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