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