xref: /PHP-5.3/ext/openssl/xp_ssl.c (revision a2045ff3)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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 indefintely.
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 
314 	if (sslsock->ssl_handle) {
315 		if (sslsock->s.is_blocked) {
316 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
317 			return -1;
318 		} else {
319 			return 0;
320 		}
321 	}
322 
323 	/* need to do slightly different things, based on client/server method,
324 	 * so lets remember which method was selected */
325 
326 	switch (cparam->inputs.method) {
327 		case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
328 			sslsock->is_client = 1;
329 			method = SSLv23_client_method();
330 			break;
331 		case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
332 #ifdef OPENSSL_NO_SSL2
333 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
334 			return -1;
335 #else
336 			sslsock->is_client = 1;
337 			method = SSLv2_client_method();
338 			break;
339 #endif
340 		case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
341 			sslsock->is_client = 1;
342 			method = SSLv3_client_method();
343 			break;
344 		case STREAM_CRYPTO_METHOD_TLS_CLIENT:
345 			sslsock->is_client = 1;
346 			method = TLSv1_client_method();
347 			break;
348 		case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
349 			sslsock->is_client = 0;
350 			method = SSLv23_server_method();
351 			break;
352 		case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
353 			sslsock->is_client = 0;
354 			method = SSLv3_server_method();
355 			break;
356 		case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
357 #ifdef OPENSSL_NO_SSL2
358 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
359 			return -1;
360 #else
361 			sslsock->is_client = 0;
362 			method = SSLv2_server_method();
363 			break;
364 #endif
365 		case STREAM_CRYPTO_METHOD_TLS_SERVER:
366 			sslsock->is_client = 0;
367 			method = TLSv1_server_method();
368 			break;
369 		default:
370 			return -1;
371 
372 	}
373 
374 	sslsock->ctx = SSL_CTX_new(method);
375 	if (sslsock->ctx == NULL) {
376 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
377 		return -1;
378 	}
379 
380 	SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL);
381 
382 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL
383 	{
384 		zval **val;
385 
386 		if (stream->context && SUCCESS == php_stream_context_get_option(
387 					stream->context, "ssl", "no_ticket", &val) &&
388 				zval_is_true(*val)) {
389 			SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET);
390 		}
391 	}
392 #endif
393 
394 	sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
395 	if (sslsock->ssl_handle == NULL) {
396 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
397 		SSL_CTX_free(sslsock->ctx);
398 		sslsock->ctx = NULL;
399 		return -1;
400 	}
401 
402 	if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
403 		handle_ssl_error(stream, 0, 1 TSRMLS_CC);
404 	}
405 
406 	if (cparam->inputs.session) {
407 		if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
408 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
409  		} else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) {
410  			php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized");
411 		} else {
412 			SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
413 		}
414 	}
415 	return 0;
416 }
417 
php_openssl_enable_crypto(php_stream * stream,php_openssl_netstream_data_t * sslsock,php_stream_xport_crypto_param * cparam TSRMLS_DC)418 static inline int php_openssl_enable_crypto(php_stream *stream,
419 		php_openssl_netstream_data_t *sslsock,
420 		php_stream_xport_crypto_param *cparam
421 		TSRMLS_DC)
422 {
423 	int n, retry = 1;
424 
425 	if (cparam->inputs.activate && !sslsock->ssl_active) {
426 		struct timeval	start_time,
427 						*timeout;
428 		int				blocked		= sslsock->s.is_blocked,
429 						has_timeout = 0;
430 
431 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
432 		if (sslsock->is_client && sslsock->sni) {
433 			SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
434 		}
435 #endif
436 
437 		if (!sslsock->state_set) {
438 			if (sslsock->is_client) {
439 				SSL_set_connect_state(sslsock->ssl_handle);
440 			} else {
441 				SSL_set_accept_state(sslsock->ssl_handle);
442 			}
443 			sslsock->state_set = 1;
444 		}
445 
446 		if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
447 			sslsock->s.is_blocked = 0;
448 		}
449 
450 		timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
451 		has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
452 		/* gettimeofday is not monotonic; using it here is not strictly correct */
453 		if (has_timeout) {
454 			gettimeofday(&start_time, NULL);
455 		}
456 
457 		do {
458 			struct timeval	cur_time,
459 							elapsed_time;
460 
461 			if (sslsock->is_client) {
462 				n = SSL_connect(sslsock->ssl_handle);
463 			} else {
464 				n = SSL_accept(sslsock->ssl_handle);
465 			}
466 
467 			if (has_timeout) {
468 				gettimeofday(&cur_time, NULL);
469 				elapsed_time.tv_sec  = cur_time.tv_sec  - start_time.tv_sec;
470 				elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
471 				if (cur_time.tv_usec < start_time.tv_usec) {
472 					elapsed_time.tv_sec  -= 1L;
473 					elapsed_time.tv_usec += 1000000L;
474 				}
475 
476 				if (elapsed_time.tv_sec > timeout->tv_sec ||
477 						(elapsed_time.tv_sec == timeout->tv_sec &&
478 						elapsed_time.tv_usec > timeout->tv_usec)) {
479 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout");
480 					return -1;
481 				}
482 			}
483 
484 			if (n <= 0) {
485 				/* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
486 				retry = handle_ssl_error(stream, n, blocked TSRMLS_CC);
487 				if (retry) {
488 					/* wait until something interesting happens in the socket. It may be a
489 					 * timeout. Also consider the unlikely of possibility of a write block  */
490 					int err = SSL_get_error(sslsock->ssl_handle, n);
491 					struct timeval left_time;
492 
493 					if (has_timeout) {
494 						left_time.tv_sec  = timeout->tv_sec  - elapsed_time.tv_sec;
495 						left_time.tv_usec =	timeout->tv_usec - elapsed_time.tv_usec;
496 						if (timeout->tv_usec < elapsed_time.tv_usec) {
497 							left_time.tv_sec  -= 1L;
498 							left_time.tv_usec += 1000000L;
499 						}
500 					}
501 					php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
502 						(POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
503 				}
504 			} else {
505 				retry = 0;
506 			}
507 		} while (retry);
508 
509 		if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
510 			sslsock->s.is_blocked = blocked;
511 		}
512 
513 		if (n == 1) {
514 			X509 *peer_cert;
515 
516 			peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
517 
518 			if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
519 				SSL_shutdown(sslsock->ssl_handle);
520 				n = -1;
521 			} else {
522 				sslsock->ssl_active = 1;
523 
524 				/* allow the script to capture the peer cert
525 				 * and/or the certificate chain */
526 				if (stream->context) {
527 					zval **val, *zcert;
528 
529 					if (SUCCESS == php_stream_context_get_option(
530 								stream->context, "ssl",
531 								"capture_peer_cert", &val) &&
532 							zval_is_true(*val)) {
533 						MAKE_STD_ZVAL(zcert);
534 						ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert,
535 									php_openssl_get_x509_list_id()));
536 						php_stream_context_set_option(stream->context,
537 								"ssl", "peer_certificate",
538 								zcert);
539 						peer_cert = NULL;
540 						FREE_ZVAL(zcert);
541 					}
542 
543 					if (SUCCESS == php_stream_context_get_option(
544 								stream->context, "ssl",
545 								"capture_peer_cert_chain", &val) &&
546 							zval_is_true(*val)) {
547 						zval *arr;
548 						STACK_OF(X509) *chain;
549 
550 						MAKE_STD_ZVAL(arr);
551 						chain = SSL_get_peer_cert_chain(
552 									sslsock->ssl_handle);
553 
554 						if (chain && sk_X509_num(chain) > 0) {
555 							int i;
556 							array_init(arr);
557 
558 							for (i = 0; i < sk_X509_num(chain); i++) {
559 								X509 *mycert = X509_dup(
560 										sk_X509_value(chain, i));
561 								MAKE_STD_ZVAL(zcert);
562 								ZVAL_RESOURCE(zcert,
563 										zend_list_insert(mycert,
564 											php_openssl_get_x509_list_id()));
565 								add_next_index_zval(arr, zcert);
566 							}
567 
568 						} else {
569 							ZVAL_NULL(arr);
570 						}
571 
572 						php_stream_context_set_option(stream->context,
573 								"ssl", "peer_certificate_chain",
574 								arr);
575 						zval_dtor(arr);
576 						efree(arr);
577 					}
578 				}
579 			}
580 
581 			if (peer_cert) {
582 				X509_free(peer_cert);
583 			}
584 		} else  {
585 			n = errno == EAGAIN ? 0 : -1;
586 		}
587 
588 		return n;
589 
590 	} else if (!cparam->inputs.activate && sslsock->ssl_active) {
591 		/* deactivate - common for server/client */
592 		SSL_shutdown(sslsock->ssl_handle);
593 		sslsock->ssl_active = 0;
594 	}
595 	return -1;
596 }
597 
php_openssl_tcp_sockop_accept(php_stream * stream,php_openssl_netstream_data_t * sock,php_stream_xport_param * xparam STREAMS_DC TSRMLS_DC)598 static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
599 		php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
600 {
601 	int clisock;
602 
603 	xparam->outputs.client = NULL;
604 
605 	clisock = php_network_accept_incoming(sock->s.socket,
606 			xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
607 			xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
608 			xparam->want_addr ? &xparam->outputs.addr : NULL,
609 			xparam->want_addr ? &xparam->outputs.addrlen : NULL,
610 			xparam->inputs.timeout,
611 			xparam->want_errortext ? &xparam->outputs.error_text : NULL,
612 			&xparam->outputs.error_code
613 			TSRMLS_CC);
614 
615 	if (clisock >= 0) {
616 		php_openssl_netstream_data_t *clisockdata;
617 
618 		clisockdata = emalloc(sizeof(*clisockdata));
619 
620 		if (clisockdata == NULL) {
621 			closesocket(clisock);
622 			/* technically a fatal error */
623 		} else {
624 			/* copy underlying tcp fields */
625 			memset(clisockdata, 0, sizeof(*clisockdata));
626 			memcpy(clisockdata, sock, sizeof(clisockdata->s));
627 
628 			clisockdata->s.socket = clisock;
629 
630 			xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
631 			if (xparam->outputs.client) {
632 				xparam->outputs.client->context = stream->context;
633 				if (stream->context) {
634 					zend_list_addref(stream->context->rsrc_id);
635 				}
636 			}
637 		}
638 
639 		if (xparam->outputs.client && sock->enable_on_connect) {
640 			/* apply crypto */
641 			switch (sock->method) {
642 				case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
643 					sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
644 					break;
645 				case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
646 					sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
647 					break;
648 				case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
649 					sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
650 					break;
651 				case STREAM_CRYPTO_METHOD_TLS_CLIENT:
652 					sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
653 					break;
654 				default:
655 					break;
656 			}
657 
658 			clisockdata->method = sock->method;
659 
660 			if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
661 					NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
662 					xparam->outputs.client, 1 TSRMLS_CC) < 0) {
663 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
664 
665 				php_stream_close(xparam->outputs.client);
666 				xparam->outputs.client = NULL;
667 				xparam->outputs.returncode = -1;
668 			}
669 		}
670 	}
671 
672 	return xparam->outputs.client == NULL ? -1 : 0;
673 }
php_openssl_sockop_set_option(php_stream * stream,int option,int value,void * ptrparam TSRMLS_DC)674 static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
675 {
676 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
677 	php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
678 	php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
679 
680 	switch (option) {
681 		case PHP_STREAM_OPTION_CHECK_LIVENESS:
682 			{
683 				struct timeval tv;
684 				char buf;
685 				int alive = 1;
686 
687 				if (value == -1) {
688 					if (sslsock->s.timeout.tv_sec == -1) {
689 						tv.tv_sec = FG(default_socket_timeout);
690 						tv.tv_usec = 0;
691 					} else {
692 						tv = sslsock->connect_timeout;
693 					}
694 				} else {
695 					tv.tv_sec = value;
696 					tv.tv_usec = 0;
697 				}
698 
699 				if (sslsock->s.socket == -1) {
700 					alive = 0;
701 				} else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
702 					if (sslsock->ssl_active) {
703 						int n;
704 
705 						do {
706 							n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
707 							if (n <= 0) {
708 								int err = SSL_get_error(sslsock->ssl_handle, n);
709 
710 								if (err == SSL_ERROR_SYSCALL) {
711 									alive = php_socket_errno() == EAGAIN;
712 									break;
713 								}
714 
715 								if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
716 									/* re-negotiate */
717 									continue;
718 								}
719 
720 								/* any other problem is a fatal error */
721 								alive = 0;
722 							}
723 							/* either peek succeeded or there was an error; we
724 							 * have set the alive flag appropriately */
725 							break;
726 						} while (1);
727 					} else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
728 						alive = 0;
729 					}
730 				}
731 				return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
732 			}
733 
734 		case PHP_STREAM_OPTION_CRYPTO_API:
735 
736 			switch(cparam->op) {
737 
738 				case STREAM_XPORT_CRYPTO_OP_SETUP:
739 					cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
740 					return PHP_STREAM_OPTION_RETURN_OK;
741 					break;
742 				case STREAM_XPORT_CRYPTO_OP_ENABLE:
743 					cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
744 					return PHP_STREAM_OPTION_RETURN_OK;
745 					break;
746 				default:
747 					/* fall through */
748 					break;
749 			}
750 
751 			break;
752 
753 		case PHP_STREAM_OPTION_XPORT_API:
754 			switch(xparam->op) {
755 
756 				case STREAM_XPORT_OP_CONNECT:
757 				case STREAM_XPORT_OP_CONNECT_ASYNC:
758 					/* TODO: Async connects need to check the enable_on_connect option when
759 					 * we notice that the connect has actually been established */
760 					php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
761 
762 					if ((sslsock->enable_on_connect) &&
763 						((xparam->outputs.returncode == 0) ||
764 						(xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC &&
765 						xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
766 					{
767 						if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
768 								php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
769 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
770 							xparam->outputs.returncode = -1;
771 						}
772 					}
773 					return PHP_STREAM_OPTION_RETURN_OK;
774 
775 				case STREAM_XPORT_OP_ACCEPT:
776 					/* we need to copy the additional fields that the underlying tcp transport
777 					 * doesn't know about */
778 					xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
779 
780 
781 					return PHP_STREAM_OPTION_RETURN_OK;
782 
783 				default:
784 					/* fall through */
785 					break;
786 			}
787 	}
788 
789 	return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
790 }
791 
php_openssl_sockop_cast(php_stream * stream,int castas,void ** ret TSRMLS_DC)792 static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
793 {
794 	php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
795 
796 	switch(castas)	{
797 		case PHP_STREAM_AS_STDIO:
798 			if (sslsock->ssl_active) {
799 				return FAILURE;
800 			}
801 			if (ret)	{
802 				*ret = fdopen(sslsock->s.socket, stream->mode);
803 				if (*ret) {
804 					return SUCCESS;
805 				}
806 				return FAILURE;
807 			}
808 			return SUCCESS;
809 
810 		case PHP_STREAM_AS_FD_FOR_SELECT:
811 			if (ret) {
812 				*(int *)ret = sslsock->s.socket;
813 			}
814 			return SUCCESS;
815 
816 		case PHP_STREAM_AS_FD:
817 		case PHP_STREAM_AS_SOCKETD:
818 			if (sslsock->ssl_active) {
819 				return FAILURE;
820 			}
821 			if (ret) {
822 				*(int *)ret = sslsock->s.socket;
823 			}
824 			return SUCCESS;
825 		default:
826 			return FAILURE;
827 	}
828 }
829 
830 php_stream_ops php_openssl_socket_ops = {
831 	php_openssl_sockop_write, php_openssl_sockop_read,
832 	php_openssl_sockop_close, php_openssl_sockop_flush,
833 	"tcp_socket/ssl",
834 	NULL, /* seek */
835 	php_openssl_sockop_cast,
836 	php_openssl_sockop_stat,
837 	php_openssl_sockop_set_option,
838 };
839 
get_sni(php_stream_context * ctx,char * resourcename,long resourcenamelen,int is_persistent TSRMLS_DC)840 static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
841 
842 	php_url *url;
843 
844 	if (ctx) {
845 		zval **val = NULL;
846 
847 		if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
848 			return NULL;
849 		}
850 		if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
851 			convert_to_string_ex(val);
852 			return pestrdup(Z_STRVAL_PP(val), is_persistent);
853 		}
854 	}
855 
856 	if (!resourcename) {
857 		return NULL;
858 	}
859 
860 	url = php_url_parse_ex(resourcename, resourcenamelen);
861 	if (!url) {
862 		return NULL;
863 	}
864 
865 	if (url->host) {
866 		const char * host = url->host;
867 		char * sni = NULL;
868 		size_t len = strlen(host);
869 
870 		/* skip trailing dots */
871 		while (len && host[len-1] == '.') {
872 			--len;
873 		}
874 
875 		if (len) {
876 			sni = pestrndup(host, len, is_persistent);
877 		}
878 
879 		php_url_free(url);
880 		return sni;
881 	}
882 
883 	php_url_free(url);
884 	return NULL;
885 }
886 
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)887 php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
888 		char *resourcename, long resourcenamelen,
889 		const char *persistent_id, int options, int flags,
890 		struct timeval *timeout,
891 		php_stream_context *context STREAMS_DC TSRMLS_DC)
892 {
893 	php_stream *stream = NULL;
894 	php_openssl_netstream_data_t *sslsock = NULL;
895 
896 	sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
897 	memset(sslsock, 0, sizeof(*sslsock));
898 
899 	sslsock->s.is_blocked = 1;
900 	/* this timeout is used by standard stream funcs, therefor it should use the default value */
901 	sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
902 	sslsock->s.timeout.tv_usec = 0;
903 
904 	/* use separate timeout for our private funcs */
905 	sslsock->connect_timeout.tv_sec = timeout->tv_sec;
906 	sslsock->connect_timeout.tv_usec = timeout->tv_usec;
907 
908 	/* we don't know the socket until we have determined if we are binding or
909 	 * connecting */
910 	sslsock->s.socket = -1;
911 
912 	/* Initialize context as NULL */
913 	sslsock->ctx = NULL;
914 
915 	stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
916 
917 	if (stream == NULL)	{
918 		pefree(sslsock, persistent_id ? 1 : 0);
919 		return NULL;
920 	}
921 
922 	sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
923 
924 	if (strncmp(proto, "ssl", protolen) == 0) {
925 		sslsock->enable_on_connect = 1;
926 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
927 	} else if (strncmp(proto, "sslv2", protolen) == 0) {
928 #ifdef OPENSSL_NO_SSL2
929 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
930 		return NULL;
931 #else
932 		sslsock->enable_on_connect = 1;
933 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
934 #endif
935 	} else if (strncmp(proto, "sslv3", protolen) == 0) {
936 		sslsock->enable_on_connect = 1;
937 		sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
938 	} else if (strncmp(proto, "tls", protolen) == 0) {
939 		sslsock->enable_on_connect = 1;
940 		sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
941 	}
942 
943 	return stream;
944 }
945 
946 
947 
948 /*
949  * Local variables:
950  * tab-width: 4
951  * c-basic-offset: 4
952  * End:
953  * vim600: noet sw=4 ts=4 fdm=marker
954  * vim<600: noet sw=4 ts=4
955  */
956