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