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
40int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC);
41SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC);
42int 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
49typedef 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
63php_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 */
67static 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
91static 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
175static 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
207static 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
245static 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
296static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
297{
298    return php_stream_socket_ops.flush(stream TSRMLS_CC);
299}
300
301static 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
307static 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
434static 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,
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
614static 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}
690static 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
808static 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
846php_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
856static 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
903php_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