Annotation of embedaddon/php/ext/openssl/xp_ssl.c, revision 1.1.1.3
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.3 ! misho 5: | Copyright (c) 1997-2013 The PHP Group |
1.1 misho 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:
1.1.1.2 misho 19: /* $Id$ */
1.1 misho 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 */
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:
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:
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:
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:
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,
1.1.1.3 ! misho 277: * but at the same time avoid hanging indefinitely.
1.1 misho 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:
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:
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:
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;
1.1.1.2 misho 313: long ssl_ctx_options = SSL_OP_ALL;
1.1 misho 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:
1.1.1.2 misho 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);
1.1 misho 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:
1.1.1.3 ! misho 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:
1.1 misho 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:
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;
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,
1.1.1.2 misho 551: php_openssl_get_x509_list_id() TSRMLS_CC));
1.1 misho 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,
1.1.1.2 misho 580: php_openssl_get_x509_list_id() TSRMLS_CC));
1.1 misho 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:
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: }
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:
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:
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:
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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>