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