xref: /PHP-5.4/ext/openssl/xp_ssl.c (revision 408b1720)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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   | Author: Wez Furlong <wez@thebrainroom.com>                           |
16   +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #include "php.h"
22 #include "ext/standard/file.h"
23 #include "ext/standard/url.h"
24 #include "streams/php_streams_int.h"
25 #include "ext/standard/php_smart_str.h"
26 #include "php_network.h"
27 #include "php_openssl.h"
28 #include <openssl/ssl.h>
29 #include <openssl/x509.h>
30 #include <openssl/err.h>
31 
32 #ifdef PHP_WIN32
33 #include "win32/time.h"
34 #endif
35 
36 #ifdef NETWARE
37 #include <sys/select.h>
38 #endif
39 
40 int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC);
41 SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC);
42 int php_openssl_get_x509_list_id(void);
43 
44 /* This implementation is very closely tied to the that of the native
45  * sockets implemented in the core.
46  * Don't try this technique in other extensions!
47  * */
48 
49 typedef struct _php_openssl_netstream_data_t {
50 	php_netstream_data_t s;
51 	SSL *ssl_handle;
52 	SSL_CTX *ctx;
53 	struct timeval connect_timeout;
54 	int enable_on_connect;
55 	int is_client;
56 	int ssl_active;
57 	php_stream_xport_crypt_method_t method;
58 	char *sni;
59 	unsigned state_set:1;
60 	unsigned _spare:31;
61 } php_openssl_netstream_data_t;
62 
63 php_stream_ops php_openssl_socket_ops;
64 
65 /* it doesn't matter that we do some hash traversal here, since it is done only
66  * in an error condition arising from a network connection problem */
is_http_stream_talking_to_iis(php_stream * stream TSRMLS_DC)67 static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC)
68 {
69 	if (stream->wrapperdata && stream->wrapper && strcasecmp(stream->wrapper->wops->label, "HTTP") == 0) {
70 		/* the wrapperdata is an array zval containing the headers */
71 		zval **tmp;
72 
73 #define SERVER_MICROSOFT_IIS	"Server: Microsoft-IIS"
74 #define SERVER_GOOGLE "Server: GFE/"
75 
76 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata));
77 		while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) {
78 
79 			if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) {
80 				return 1;
81 			} else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) {
82 				return 1;
83 			}
84 
85 			zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata));
86 		}
87 	}
88 	return 0;
89 }
90 
handle_ssl_error(php_stream * stream,int nr_bytes,zend_bool is_init TSRMLS_DC)91 static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC)
92 {
93 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
94 	int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
95 	char esbuf[512];
96 	smart_str ebuf = {0};
97 	unsigned long ecode;
98 	int retry = 1;
99 
100 	switch(err) {
101 		case SSL_ERROR_ZERO_RETURN:
102 			/* SSL terminated (but socket may still be active) */
103 			retry = 0;
104 			break;
105 		case SSL_ERROR_WANT_READ:
106 		case SSL_ERROR_WANT_WRITE:
107 			/* re-negotiation, or perhaps the SSL layer needs more
108 			 * packets: retry in next iteration */
109 			errno = EAGAIN;
110 			retry = is_init ? 1 : sslsock->s.is_blocked;
111 			break;
112 		case SSL_ERROR_SYSCALL:
113 			if (ERR_peek_error() == 0) {
114 				if (nr_bytes == 0) {
115 					if (!is_http_stream_talking_to_iis(stream TSRMLS_CC) && ERR_get_error() != 0) {
116 						php_error_docref(NULL TSRMLS_CC, E_WARNING,
117 								"SSL: fatal protocol error");
118 					}
119 					SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
120 					stream->eof = 1;
121 					retry = 0;
122 				} else {
123 					char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
124 
125 					php_error_docref(NULL TSRMLS_CC, E_WARNING,
126 							"SSL: %s", estr);
127 
128 					efree(estr);
129 					retry = 0;
130 				}
131 				break;
132 			}
133 
134 
135 			/* fall through */
136 		default:
137 			/* some other error */
138 			ecode = ERR_get_error();
139 
140 			switch (ERR_GET_REASON(ecode)) {
141 				case SSL_R_NO_SHARED_CIPHER:
142 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL_R_NO_SHARED_CIPHER: no suitable shared cipher could be used.  This could be because the server is missing an SSL certificate (local_cert context option)");
143 					retry = 0;
144 					break;
145 
146 				default:
147 					do {
148 						/* NULL is automatically added */
149 						ERR_error_string_n(ecode, esbuf, sizeof(esbuf));
150 						if (ebuf.c) {
151 							smart_str_appendc(&ebuf, '\n');
152 						}
153 						smart_str_appends(&ebuf, esbuf);
154 					} while ((ecode = ERR_get_error()) != 0);
155 
156 					smart_str_0(&ebuf);
157 
158 					php_error_docref(NULL TSRMLS_CC, E_WARNING,
159 							"SSL operation failed with code %d. %s%s",
160 							err,
161 							ebuf.c ? "OpenSSL Error messages:\n" : "",
162 							ebuf.c ? ebuf.c : "");
163 					if (ebuf.c) {
164 						smart_str_free(&ebuf);
165 					}
166 			}
167 
168 			retry = 0;
169 			errno = 0;
170 	}
171 	return retry;
172 }
173 
174 
php_openssl_sockop_write(php_stream * stream,const char * buf,size_t count TSRMLS_DC)175 static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
176 {
177 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
178 	int didwrite;
179 
180 	if (sslsock->ssl_active) {
181 		int retry = 1;
182 
183 		do {
184 			didwrite = SSL_write(sslsock->ssl_handle, buf, count);
185 
186 			if (didwrite <= 0) {
187 				retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC);
188 			} else {
189 				break;
190 			}
191 		} while(retry);
192 
193 		if (didwrite > 0) {
194 			php_stream_notify_progress_increment(stream->context, didwrite, 0);
195 		}
196 	} else {
197 		didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
198 	}
199 
200 	if (didwrite < 0) {
201 		didwrite = 0;
202 	}
203 
204 	return didwrite;
205 }
206 
php_openssl_sockop_read(php_stream * stream,char * buf,size_t count TSRMLS_DC)207 static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
208 {
209 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
210 	int nr_bytes = 0;
211 
212 	if (sslsock->ssl_active) {
213 		int retry = 1;
214 
215 		do {
216 			nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
217 
218 			if (nr_bytes <= 0) {
219 				retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
220 				stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
221 
222 			} else {
223 				/* we got the data */
224 				break;
225 			}
226 		} while (retry);
227 
228 		if (nr_bytes > 0) {
229 			php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
230 		}
231 	}
232 	else
233 	{
234 		nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
235 	}
236 
237 	if (nr_bytes < 0) {
238 		nr_bytes = 0;
239 	}
240 
241 	return nr_bytes;
242 }
243 
244 
php_openssl_sockop_close(php_stream * stream,int close_handle TSRMLS_DC)245 static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
246 {
247 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
248 #ifdef PHP_WIN32
249 	int n;
250 #endif
251 	if (close_handle) {
252 		if (sslsock->ssl_active) {
253 			SSL_shutdown(sslsock->ssl_handle);
254 			sslsock->ssl_active = 0;
255 		}
256 		if (sslsock->ssl_handle) {
257 			SSL_free(sslsock->ssl_handle);
258 			sslsock->ssl_handle = NULL;
259 		}
260 		if (sslsock->ctx) {
261 			SSL_CTX_free(sslsock->ctx);
262 			sslsock->ctx = NULL;
263 		}
264 #ifdef PHP_WIN32
265 		if (sslsock->s.socket == -1)
266 			sslsock->s.socket = SOCK_ERR;
267 #endif
268 		if (sslsock->s.socket != SOCK_ERR) {
269 #ifdef PHP_WIN32
270 			/* prevent more data from coming in */
271 			shutdown(sslsock->s.socket, SHUT_RD);
272 
273 			/* try to make sure that the OS sends all data before we close the connection.
274 			 * Essentially, we are waiting for the socket to become writeable, which means
275 			 * that all pending data has been sent.
276 			 * We use a small timeout which should encourage the OS to send the data,
277 			 * but at the same time avoid hanging indefinitely.
278 			 * */
279 			do {
280 				n = php_pollfd_for_ms(sslsock->s.socket, POLLOUT, 500);
281 			} while (n == -1 && php_socket_errno() == EINTR);
282 #endif
283 			closesocket(sslsock->s.socket);
284 			sslsock->s.socket = SOCK_ERR;
285 		}
286 	}
287 
288 	if (sslsock->sni) {
289 		pefree(sslsock->sni, php_stream_is_persistent(stream));
290 	}
291 	pefree(sslsock, php_stream_is_persistent(stream));
292 
293 	return 0;
294 }
295 
php_openssl_sockop_flush(php_stream * stream TSRMLS_DC)296 static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
297 {
298 	return php_stream_socket_ops.flush(stream TSRMLS_CC);
299 }
300 
php_openssl_sockop_stat(php_stream * stream,php_stream_statbuf * ssb TSRMLS_DC)301 static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
302 {
303 	return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
304 }
305 
306 
php_openssl_setup_crypto(php_stream * stream,php_openssl_netstream_data_t * sslsock,php_stream_xport_crypto_param * cparam TSRMLS_DC)307 static inline int php_openssl_setup_crypto(php_stream *stream,
308 		php_openssl_netstream_data_t *sslsock,
309 		php_stream_xport_crypto_param *cparam
310 		TSRMLS_DC)
311 {
312 	SSL_METHOD *method;
313 	long ssl_ctx_options = SSL_OP_ALL;
314 
315 	if (sslsock->ssl_handle) {
316 		if (sslsock->s.is_blocked) {
317 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
318 			return -1;
319 		} else {
320 			return 0;
321 		}
322 	}
323 
324 	/* need to do slightly different things, based on client/server method,
325 	 * so lets remember which method was selected */
326 
327 	switch (cparam->inputs.method) {
328 		case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
329 			sslsock->is_client = 1;
330 			method = SSLv23_client_method();
331 			break;
332 		case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
333 #ifdef OPENSSL_NO_SSL2
334 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
335 			return -1;
336 #else
337 			sslsock->is_client = 1;
338 			method = SSLv2_client_method();
339 			break;
340 #endif
341 		case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
342 			sslsock->is_client = 1;
343 			method = SSLv3_client_method();
344 			break;
345 		case STREAM_CRYPTO_METHOD_TLS_CLIENT:
346 			sslsock->is_client = 1;
347 			method = TLSv1_client_method();
348 			break;
349 		case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
350 			sslsock->is_client = 0;
351 			method = SSLv23_server_method();
352 			break;
353 		case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
354 			sslsock->is_client = 0;
355 			method = SSLv3_server_method();
356 			break;
357 		case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
358 #ifdef OPENSSL_NO_SSL2
359 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
360 			return -1;
361 #else
362 			sslsock->is_client = 0;
363 			method = SSLv2_server_method();
364 			break;
365 #endif
366 		case STREAM_CRYPTO_METHOD_TLS_SERVER:
367 			sslsock->is_client = 0;
368 			method = TLSv1_server_method();
369 			break;
370 		default:
371 			return -1;
372 
373 	}
374 
375 	sslsock->ctx = SSL_CTX_new(method);
376 	if (sslsock->ctx == NULL) {
377 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
378 		return -1;
379 	}
380 
381 #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
382 	ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
383 #endif
384 	SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options);
385 
386 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL
387 	{
388 		zval **val;
389 
390 		if (stream->context && SUCCESS == php_stream_context_get_option(
391 					stream->context, "ssl", "no_ticket", &val) &&
392 				zval_is_true(*val)) {
393 			SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET);
394 		}
395 	}
396 #endif
397 
398 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
399 	{
400 		zval **val;
401 
402 		if (stream->context && SUCCESS == php_stream_context_get_option(
403 					stream->context, "ssl", "disable_compression", &val) &&
404 				zval_is_true(*val)) {
405 			SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_COMPRESSION);
406 		}
407 	}
408 #endif
409 
410 	sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
411 	if (sslsock->ssl_handle == NULL) {
412 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
413 		SSL_CTX_free(sslsock->ctx);
414 		sslsock->ctx = NULL;
415 		return -1;
416 	}
417 
418 	if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
419 		handle_ssl_error(stream, 0, 1 TSRMLS_CC);
420 	}
421 
422 	if (cparam->inputs.session) {
423 		if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
424 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
425  		} else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) {
426  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized");
427 		} else {
428 			SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
429 		}
430 	}
431 	return 0;
432 }
433 
php_openssl_enable_crypto(php_stream * stream,php_openssl_netstream_data_t * sslsock,php_stream_xport_crypto_param * cparam TSRMLS_DC)434 static inline int php_openssl_enable_crypto(php_stream *stream,
435 		php_openssl_netstream_data_t *sslsock,
436 		php_stream_xport_crypto_param *cparam
437 		TSRMLS_DC)
438 {
439 	int n, retry = 1;
440 
441 	if (cparam->inputs.activate && !sslsock->ssl_active) {
442 		struct timeval	start_time,
443 						*timeout;
444 		int				blocked		= sslsock->s.is_blocked,
445 						has_timeout = 0;
446 
447 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
448 		if (sslsock->is_client && sslsock->sni) {
449 			SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
450 		}
451 #endif
452 
453 		if (!sslsock->state_set) {
454 			if (sslsock->is_client) {
455 				SSL_set_connect_state(sslsock->ssl_handle);
456 			} else {
457 				SSL_set_accept_state(sslsock->ssl_handle);
458 			}
459 			sslsock->state_set = 1;
460 		}
461 
462 		if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
463 			sslsock->s.is_blocked = 0;
464 		}
465 
466 		timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
467 		has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
468 		/* gettimeofday is not monotonic; using it here is not strictly correct */
469 		if (has_timeout) {
470 			gettimeofday(&start_time, NULL);
471 		}
472 
473 		do {
474 			struct timeval	cur_time,
475 							elapsed_time = {0};
476 
477 			if (sslsock->is_client) {
478 				n = SSL_connect(sslsock->ssl_handle);
479 			} else {
480 				n = SSL_accept(sslsock->ssl_handle);
481 			}
482 
483 			if (has_timeout) {
484 				gettimeofday(&cur_time, NULL);
485 				elapsed_time.tv_sec  = cur_time.tv_sec  - start_time.tv_sec;
486 				elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
487 				if (cur_time.tv_usec < start_time.tv_usec) {
488 					elapsed_time.tv_sec  -= 1L;
489 					elapsed_time.tv_usec += 1000000L;
490 				}
491 
492 				if (elapsed_time.tv_sec > timeout->tv_sec ||
493 						(elapsed_time.tv_sec == timeout->tv_sec &&
494 						elapsed_time.tv_usec > timeout->tv_usec)) {
495 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout");
496 					return -1;
497 				}
498 			}
499 
500 			if (n <= 0) {
501 				/* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
502 				retry = handle_ssl_error(stream, n, blocked TSRMLS_CC);
503 				if (retry) {
504 					/* wait until something interesting happens in the socket. It may be a
505 					 * timeout. Also consider the unlikely of possibility of a write block  */
506 					int err = SSL_get_error(sslsock->ssl_handle, n);
507 					struct timeval left_time;
508 
509 					if (has_timeout) {
510 						left_time.tv_sec  = timeout->tv_sec  - elapsed_time.tv_sec;
511 						left_time.tv_usec =	timeout->tv_usec - elapsed_time.tv_usec;
512 						if (timeout->tv_usec < elapsed_time.tv_usec) {
513 							left_time.tv_sec  -= 1L;
514 							left_time.tv_usec += 1000000L;
515 						}
516 					}
517 					php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
518 						(POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
519 				}
520 			} else {
521 				retry = 0;
522 			}
523 		} while (retry);
524 
525 		if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
526 			sslsock->s.is_blocked = blocked;
527 		}
528 
529 		if (n == 1) {
530 			X509 *peer_cert;
531 
532 			peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
533 
534 			if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
535 				SSL_shutdown(sslsock->ssl_handle);
536 				n = -1;
537 			} else {
538 				sslsock->ssl_active = 1;
539 
540 				/* allow the script to capture the peer cert
541 				 * and/or the certificate chain */
542 				if (stream->context) {
543 					zval **val, *zcert;
544 
545 					if (SUCCESS == php_stream_context_get_option(
546 								stream->context, "ssl",
547 								"capture_peer_cert", &val) &&
548 							zval_is_true(*val)) {
549 						MAKE_STD_ZVAL(zcert);
550 						ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert,
551 									php_openssl_get_x509_list_id() TSRMLS_CC));
552 						php_stream_context_set_option(stream->context,
553 								"ssl", "peer_certificate",
554 								zcert);
555 						peer_cert = NULL;
556 						FREE_ZVAL(zcert);
557 					}
558 
559 					if (SUCCESS == php_stream_context_get_option(
560 								stream->context, "ssl",
561 								"capture_peer_cert_chain", &val) &&
562 							zval_is_true(*val)) {
563 						zval *arr;
564 						STACK_OF(X509) *chain;
565 
566 						MAKE_STD_ZVAL(arr);
567 						chain = SSL_get_peer_cert_chain(
568 									sslsock->ssl_handle);
569 
570 						if (chain && sk_X509_num(chain) > 0) {
571 							int i;
572 							array_init(arr);
573 
574 							for (i = 0; i < sk_X509_num(chain); i++) {
575 								X509 *mycert = X509_dup(
576 										sk_X509_value(chain, i));
577 								MAKE_STD_ZVAL(zcert);
578 								ZVAL_RESOURCE(zcert,
579 										zend_list_insert(mycert,
580 											php_openssl_get_x509_list_id() TSRMLS_CC));
581 								add_next_index_zval(arr, zcert);
582 							}
583 
584 						} else {
585 							ZVAL_NULL(arr);
586 						}
587 
588 						php_stream_context_set_option(stream->context,
589 								"ssl", "peer_certificate_chain",
590 								arr);
591 						zval_dtor(arr);
592 						efree(arr);
593 					}
594 				}
595 			}
596 
597 			if (peer_cert) {
598 				X509_free(peer_cert);
599 			}
600 		} else  {
601 			n = errno == EAGAIN ? 0 : -1;
602 		}
603 
604 		return n;
605 
606 	} else if (!cparam->inputs.activate && sslsock->ssl_active) {
607 		/* deactivate - common for server/client */
608 		SSL_shutdown(sslsock->ssl_handle);
609 		sslsock->ssl_active = 0;
610 	}
611 	return -1;
612 }
613 
php_openssl_tcp_sockop_accept(php_stream * stream,php_openssl_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC TSRMLS_DC)614 static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
615 		php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
616 {
617 	int clisock;
618 
619 	xparam->outputs.client = NULL;
620 
621 	clisock = php_network_accept_incoming(sock->s.socket,
622 			xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
623 			xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
624 			xparam->want_addr ? &xparam->outputs.addr : NULL,
625 			xparam->want_addr ? &xparam->outputs.addrlen : NULL,
626 			xparam->inputs.timeout,
627 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
628 			&xparam->outputs.error_code
629 			TSRMLS_CC);
630 
631 	if (clisock >= 0) {
632 		php_openssl_netstream_data_t *clisockdata;
633 
634 		clisockdata = emalloc(sizeof(*clisockdata));
635 
636 		if (clisockdata == NULL) {
637 			closesocket(clisock);
638 			/* technically a fatal error */
639 		} else {
640 			/* copy underlying tcp fields */
641 			memset(clisockdata, 0, sizeof(*clisockdata));
642 			memcpy(clisockdata, sock, sizeof(clisockdata->s));
643 
644 			clisockdata->s.socket = clisock;
645 
646 			xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
647 			if (xparam->outputs.client) {
648 				xparam->outputs.client->context = stream->context;
649 				if (stream->context) {
650 					zend_list_addref(stream->context->rsrc_id);
651 				}
652 			}
653 		}
654 
655 		if (xparam->outputs.client && sock->enable_on_connect) {
656 			/* apply crypto */
657 			switch (sock->method) {
658 				case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
659 					sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
660 					break;
661 				case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
662 					sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
663 					break;
664 				case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
665 					sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
666 					break;
667 				case STREAM_CRYPTO_METHOD_TLS_CLIENT:
668 					sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
669 					break;
670 				default:
671 					break;
672 			}
673 
674 			clisockdata->method = sock->method;
675 
676 			if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
677 					NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
678 					xparam->outputs.client, 1 TSRMLS_CC) < 0) {
679 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
680 
681 				php_stream_close(xparam->outputs.client);
682 				xparam->outputs.client = NULL;
683 				xparam->outputs.returncode = -1;
684 			}
685 		}
686 	}
687 
688 	return xparam->outputs.client == NULL ? -1 : 0;
689 }
php_openssl_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)690 static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
691 {
692 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
693 	php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
694 	php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
695 
696 	switch (option) {
697 		case PHP_STREAM_OPTION_CHECK_LIVENESS:
698 			{
699 				struct timeval tv;
700 				char buf;
701 				int alive = 1;
702 
703 				if (value == -1) {
704 					if (sslsock->s.timeout.tv_sec == -1) {
705 						tv.tv_sec = FG(default_socket_timeout);
706 						tv.tv_usec = 0;
707 					} else {
708 						tv = sslsock->connect_timeout;
709 					}
710 				} else {
711 					tv.tv_sec = value;
712 					tv.tv_usec = 0;
713 				}
714 
715 				if (sslsock->s.socket == -1) {
716 					alive = 0;
717 				} else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
718 					if (sslsock->ssl_active) {
719 						int n;
720 
721 						do {
722 							n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
723 							if (n <= 0) {
724 								int err = SSL_get_error(sslsock->ssl_handle, n);
725 
726 								if (err == SSL_ERROR_SYSCALL) {
727 									alive = php_socket_errno() == EAGAIN;
728 									break;
729 								}
730 
731 								if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
732 									/* re-negotiate */
733 									continue;
734 								}
735 
736 								/* any other problem is a fatal error */
737 								alive = 0;
738 							}
739 							/* either peek succeeded or there was an error; we
740 							 * have set the alive flag appropriately */
741 							break;
742 						} while (1);
743 					} else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
744 						alive = 0;
745 					}
746 				}
747 				return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
748 			}
749 
750 		case PHP_STREAM_OPTION_CRYPTO_API:
751 
752 			switch(cparam->op) {
753 
754 				case STREAM_XPORT_CRYPTO_OP_SETUP:
755 					cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
756 					return PHP_STREAM_OPTION_RETURN_OK;
757 					break;
758 				case STREAM_XPORT_CRYPTO_OP_ENABLE:
759 					cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
760 					return PHP_STREAM_OPTION_RETURN_OK;
761 					break;
762 				default:
763 					/* fall through */
764 					break;
765 			}
766 
767 			break;
768 
769 		case PHP_STREAM_OPTION_XPORT_API:
770 			switch(xparam->op) {
771 
772 				case STREAM_XPORT_OP_CONNECT:
773 				case STREAM_XPORT_OP_CONNECT_ASYNC:
774 					/* TODO: Async connects need to check the enable_on_connect option when
775 					 * we notice that the connect has actually been established */
776 					php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
777 
778 					if ((sslsock->enable_on_connect) &&
779 						((xparam->outputs.returncode == 0) ||
780 						(xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC &&
781 						xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
782 					{
783 						if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
784 								php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
785 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
786 							xparam->outputs.returncode = -1;
787 						}
788 					}
789 					return PHP_STREAM_OPTION_RETURN_OK;
790 
791 				case STREAM_XPORT_OP_ACCEPT:
792 					/* we need to copy the additional fields that the underlying tcp transport
793 					 * doesn't know about */
794 					xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
795 
796 
797 					return PHP_STREAM_OPTION_RETURN_OK;
798 
799 				default:
800 					/* fall through */
801 					break;
802 			}
803 	}
804 
805 	return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
806 }
807 
php_openssl_sockop_cast(php_stream * stream,int castas,void ** ret TSRMLS_DC)808 static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
809 {
810 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
811 
812 	switch(castas)	{
813 		case PHP_STREAM_AS_STDIO:
814 			if (sslsock->ssl_active) {
815 				return FAILURE;
816 			}
817 			if (ret)	{
818 				*ret = fdopen(sslsock->s.socket, stream->mode);
819 				if (*ret) {
820 					return SUCCESS;
821 				}
822 				return FAILURE;
823 			}
824 			return SUCCESS;
825 
826 		case PHP_STREAM_AS_FD_FOR_SELECT:
827 			if (ret) {
828 				*(int *)ret = sslsock->s.socket;
829 			}
830 			return SUCCESS;
831 
832 		case PHP_STREAM_AS_FD:
833 		case PHP_STREAM_AS_SOCKETD:
834 			if (sslsock->ssl_active) {
835 				return FAILURE;
836 			}
837 			if (ret) {
838 				*(int *)ret = sslsock->s.socket;
839 			}
840 			return SUCCESS;
841 		default:
842 			return FAILURE;
843 	}
844 }
845 
846 php_stream_ops php_openssl_socket_ops = {
847 	php_openssl_sockop_write, php_openssl_sockop_read,
848 	php_openssl_sockop_close, php_openssl_sockop_flush,
849 	"tcp_socket/ssl",
850 	NULL, /* seek */
851 	php_openssl_sockop_cast,
852 	php_openssl_sockop_stat,
853 	php_openssl_sockop_set_option,
854 };
855 
get_sni(php_stream_context * ctx,char * resourcename,long resourcenamelen,int is_persistent TSRMLS_DC)856 static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
857 
858 	php_url *url;
859 
860 	if (ctx) {
861 		zval **val = NULL;
862 
863 		if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
864 			return NULL;
865 		}
866 		if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
867 			convert_to_string_ex(val);
868 			return pestrdup(Z_STRVAL_PP(val), is_persistent);
869 		}
870 	}
871 
872 	if (!resourcename) {
873 		return NULL;
874 	}
875 
876 	url = php_url_parse_ex(resourcename, resourcenamelen);
877 	if (!url) {
878 		return NULL;
879 	}
880 
881 	if (url->host) {
882 		const char * host = url->host;
883 		char * sni = NULL;
884 		size_t len = strlen(host);
885 
886 		/* skip trailing dots */
887 		while (len && host[len-1] == '.') {
888 			--len;
889 		}
890 
891 		if (len) {
892 			sni = pestrndup(host, len, is_persistent);
893 		}
894 
895 		php_url_free(url);
896 		return sni;
897 	}
898 
899 	php_url_free(url);
900 	return NULL;
901 }
902 
php_openssl_ssl_socket_factory(const char * proto,long protolen,char * resourcename,long resourcenamelen,const char * persistent_id,int options,int flags,struct timeval * timeout,php_stream_context * context STREAMS_DC TSRMLS_DC)903 php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
904 		char *resourcename, long resourcenamelen,
905 		const char *persistent_id, int options, int flags,
906 		struct timeval *timeout,
907 		php_stream_context *context STREAMS_DC TSRMLS_DC)
908 {
909 	php_stream *stream = NULL;
910 	php_openssl_netstream_data_t *sslsock = NULL;
911 
912 	sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
913 	memset(sslsock, 0, sizeof(*sslsock));
914 
915 	sslsock->s.is_blocked = 1;
916 	/* this timeout is used by standard stream funcs, therefor it should use the default value */
917 	sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
918 	sslsock->s.timeout.tv_usec = 0;
919 
920 	/* use separate timeout for our private funcs */
921 	sslsock->connect_timeout.tv_sec = timeout->tv_sec;
922 	sslsock->connect_timeout.tv_usec = timeout->tv_usec;
923 
924 	/* we don't know the socket until we have determined if we are binding or
925 	 * connecting */
926 	sslsock->s.socket = -1;
927 
928 	/* Initialize context as NULL */
929 	sslsock->ctx = NULL;
930 
931 	stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
932 
933 	if (stream == NULL)	{
934 		pefree(sslsock, persistent_id ? 1 : 0);
935 		return NULL;
936 	}
937 
938 	sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
939 
940 	if (strncmp(proto, "ssl", protolen) == 0) {
941 		sslsock->enable_on_connect = 1;
942 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
943 	} else if (strncmp(proto, "sslv2", protolen) == 0) {
944 #ifdef OPENSSL_NO_SSL2
945 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
946 		return NULL;
947 #else
948 		sslsock->enable_on_connect = 1;
949 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
950 #endif
951 	} else if (strncmp(proto, "sslv3", protolen) == 0) {
952 		sslsock->enable_on_connect = 1;
953 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
954 	} else if (strncmp(proto, "tls", protolen) == 0) {
955 		sslsock->enable_on_connect = 1;
956 		sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
957 	}
958 
959 	return stream;
960 }
961 
962 
963 
964 /*
965  * Local variables:
966  * tab-width: 4
967  * c-basic-offset: 4
968  * End:
969  * vim600: noet sw=4 ts=4 fdm=marker
970  * vim<600: noet sw=4 ts=4
971  */
972