Annotation of embedaddon/php/ext/openssl/xp_ssl.c, revision 1.1.1.4

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,
1.1.1.4 ! misho     475:                                                        elapsed_time = {0};
1.1       misho     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>