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
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
418static 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
598static 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}
674static 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
792static 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
830php_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
840static 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
887php_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