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

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
                      5:   | Copyright (c) 1997-2012 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: 
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,
                    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: 
                    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: 
                    398:        sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
                    399:        if (sslsock->ssl_handle == NULL) {
                    400:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
                    401:                SSL_CTX_free(sslsock->ctx);
                    402:                sslsock->ctx = NULL;
                    403:                return -1;
                    404:        }
                    405: 
                    406:        if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
                    407:                handle_ssl_error(stream, 0, 1 TSRMLS_CC);
                    408:        }
                    409: 
                    410:        if (cparam->inputs.session) {
                    411:                if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
                    412:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
                    413:                } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) {
                    414:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized");
                    415:                } else {
                    416:                        SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
                    417:                }
                    418:        }
                    419:        return 0;
                    420: }
                    421: 
                    422: static inline int php_openssl_enable_crypto(php_stream *stream,
                    423:                php_openssl_netstream_data_t *sslsock,
                    424:                php_stream_xport_crypto_param *cparam
                    425:                TSRMLS_DC)
                    426: {
                    427:        int n, retry = 1;
                    428: 
                    429:        if (cparam->inputs.activate && !sslsock->ssl_active) {
                    430:                struct timeval  start_time,
                    431:                                                *timeout;
                    432:                int                             blocked         = sslsock->s.is_blocked,
                    433:                                                has_timeout = 0;
                    434: 
                    435: #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
                    436:                if (sslsock->is_client && sslsock->sni) {
                    437:                        SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
                    438:                }
                    439: #endif
                    440: 
                    441:                if (!sslsock->state_set) {
                    442:                        if (sslsock->is_client) {
                    443:                                SSL_set_connect_state(sslsock->ssl_handle);
                    444:                        } else {
                    445:                                SSL_set_accept_state(sslsock->ssl_handle);
                    446:                        }
                    447:                        sslsock->state_set = 1;
                    448:                }
                    449:        
                    450:                if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
                    451:                        sslsock->s.is_blocked = 0;
                    452:                }
                    453:                
                    454:                timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
                    455:                has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
                    456:                /* gettimeofday is not monotonic; using it here is not strictly correct */
                    457:                if (has_timeout) {
                    458:                        gettimeofday(&start_time, NULL);
                    459:                }
                    460:                
                    461:                do {
                    462:                        struct timeval  cur_time,
                    463:                                                        elapsed_time;
                    464:                        
                    465:                        if (sslsock->is_client) {
                    466:                                n = SSL_connect(sslsock->ssl_handle);
                    467:                        } else {
                    468:                                n = SSL_accept(sslsock->ssl_handle);
                    469:                        }
                    470: 
                    471:                        if (has_timeout) {
                    472:                                gettimeofday(&cur_time, NULL);
                    473:                                elapsed_time.tv_sec  = cur_time.tv_sec  - start_time.tv_sec;
                    474:                                elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
                    475:                                if (cur_time.tv_usec < start_time.tv_usec) {
                    476:                                        elapsed_time.tv_sec  -= 1L;
                    477:                                        elapsed_time.tv_usec += 1000000L;
                    478:                                }
                    479:                        
                    480:                                if (elapsed_time.tv_sec > timeout->tv_sec ||
                    481:                                                (elapsed_time.tv_sec == timeout->tv_sec &&
                    482:                                                elapsed_time.tv_usec > timeout->tv_usec)) {
                    483:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout");
                    484:                                        return -1;
                    485:                                }
                    486:                        }
                    487: 
                    488:                        if (n <= 0) {
                    489:                                /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
                    490:                                retry = handle_ssl_error(stream, n, blocked TSRMLS_CC);
                    491:                                if (retry) {
                    492:                                        /* wait until something interesting happens in the socket. It may be a
                    493:                                         * timeout. Also consider the unlikely of possibility of a write block  */
                    494:                                        int err = SSL_get_error(sslsock->ssl_handle, n);
                    495:                                        struct timeval left_time;
                    496:                                        
                    497:                                        if (has_timeout) {
                    498:                                                left_time.tv_sec  = timeout->tv_sec  - elapsed_time.tv_sec;
                    499:                                                left_time.tv_usec =     timeout->tv_usec - elapsed_time.tv_usec;
                    500:                                                if (timeout->tv_usec < elapsed_time.tv_usec) {
                    501:                                                        left_time.tv_sec  -= 1L;
                    502:                                                        left_time.tv_usec += 1000000L;
                    503:                                                }
                    504:                                        }
                    505:                                        php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
                    506:                                                (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
                    507:                                }
                    508:                        } else {
                    509:                                retry = 0;
                    510:                        }
                    511:                } while (retry);
                    512: 
                    513:                if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
                    514:                        sslsock->s.is_blocked = blocked;
                    515:                }
                    516: 
                    517:                if (n == 1) {
                    518:                        X509 *peer_cert;
                    519: 
                    520:                        peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
                    521: 
                    522:                        if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
                    523:                                SSL_shutdown(sslsock->ssl_handle);
                    524:                                n = -1;
                    525:                        } else {        
                    526:                                sslsock->ssl_active = 1;
                    527: 
                    528:                                /* allow the script to capture the peer cert
                    529:                                 * and/or the certificate chain */
                    530:                                if (stream->context) {
                    531:                                        zval **val, *zcert;
                    532: 
                    533:                                        if (SUCCESS == php_stream_context_get_option(
                    534:                                                                stream->context, "ssl",
                    535:                                                                "capture_peer_cert", &val) &&
                    536:                                                        zval_is_true(*val)) {
                    537:                                                MAKE_STD_ZVAL(zcert);
                    538:                                                ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 
1.1.1.2 ! misho     539:                                                                        php_openssl_get_x509_list_id() TSRMLS_CC));
1.1       misho     540:                                                php_stream_context_set_option(stream->context,
                    541:                                                                "ssl", "peer_certificate",
                    542:                                                                zcert);
                    543:                                                peer_cert = NULL;
                    544:                                                FREE_ZVAL(zcert);
                    545:                                        }
                    546: 
                    547:                                        if (SUCCESS == php_stream_context_get_option(
                    548:                                                                stream->context, "ssl",
                    549:                                                                "capture_peer_cert_chain", &val) &&
                    550:                                                        zval_is_true(*val)) {
                    551:                                                zval *arr;
                    552:                                                STACK_OF(X509) *chain;
                    553: 
                    554:                                                MAKE_STD_ZVAL(arr);
                    555:                                                chain = SSL_get_peer_cert_chain(
                    556:                                                                        sslsock->ssl_handle);
                    557: 
                    558:                                                if (chain && sk_X509_num(chain) > 0) {
                    559:                                                        int i;
                    560:                                                        array_init(arr);
                    561: 
                    562:                                                        for (i = 0; i < sk_X509_num(chain); i++) {
                    563:                                                                X509 *mycert = X509_dup(
                    564:                                                                                sk_X509_value(chain, i));
                    565:                                                                MAKE_STD_ZVAL(zcert);
                    566:                                                                ZVAL_RESOURCE(zcert,
                    567:                                                                                zend_list_insert(mycert,
1.1.1.2 ! misho     568:                                                                                        php_openssl_get_x509_list_id() TSRMLS_CC));
1.1       misho     569:                                                                add_next_index_zval(arr, zcert);
                    570:                                                        }
                    571: 
                    572:                                                } else {
                    573:                                                        ZVAL_NULL(arr);
                    574:                                                }
                    575: 
                    576:                                                php_stream_context_set_option(stream->context,
                    577:                                                                "ssl", "peer_certificate_chain",
                    578:                                                                arr);
                    579:                                                zval_dtor(arr);
                    580:                                                efree(arr);
                    581:                                        }
                    582:                                }
                    583:                        }
                    584: 
                    585:                        if (peer_cert) {
                    586:                                X509_free(peer_cert);
                    587:                        }
                    588:                } else  {
                    589:                        n = errno == EAGAIN ? 0 : -1;
                    590:                }
                    591: 
                    592:                return n;
                    593: 
                    594:        } else if (!cparam->inputs.activate && sslsock->ssl_active) {
                    595:                /* deactivate - common for server/client */
                    596:                SSL_shutdown(sslsock->ssl_handle);
                    597:                sslsock->ssl_active = 0;
                    598:        }
                    599:        return -1;
                    600: }
                    601: 
                    602: static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
                    603:                php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
                    604: {
                    605:        int clisock;
                    606: 
                    607:        xparam->outputs.client = NULL;
                    608: 
                    609:        clisock = php_network_accept_incoming(sock->s.socket,
                    610:                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
                    611:                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
                    612:                        xparam->want_addr ? &xparam->outputs.addr : NULL,
                    613:                        xparam->want_addr ? &xparam->outputs.addrlen : NULL,
                    614:                        xparam->inputs.timeout,
                    615:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
                    616:                        &xparam->outputs.error_code
                    617:                        TSRMLS_CC);
                    618: 
                    619:        if (clisock >= 0) {
                    620:                php_openssl_netstream_data_t *clisockdata;
                    621: 
                    622:                clisockdata = emalloc(sizeof(*clisockdata));
                    623: 
                    624:                if (clisockdata == NULL) {
                    625:                        closesocket(clisock);
                    626:                        /* technically a fatal error */
                    627:                } else {
                    628:                        /* copy underlying tcp fields */
                    629:                        memset(clisockdata, 0, sizeof(*clisockdata));
                    630:                        memcpy(clisockdata, sock, sizeof(clisockdata->s));
                    631: 
                    632:                        clisockdata->s.socket = clisock;
                    633:                        
                    634:                        xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
                    635:                        if (xparam->outputs.client) {
                    636:                                xparam->outputs.client->context = stream->context;
                    637:                                if (stream->context) {
                    638:                                        zend_list_addref(stream->context->rsrc_id);
                    639:                                }
                    640:                        }
                    641:                }
                    642: 
                    643:                if (xparam->outputs.client && sock->enable_on_connect) {
                    644:                        /* apply crypto */
                    645:                        switch (sock->method) {
                    646:                                case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
                    647:                                        sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
                    648:                                        break;
                    649:                                case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
                    650:                                        sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
                    651:                                        break;
                    652:                                case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
                    653:                                        sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
                    654:                                        break;
                    655:                                case STREAM_CRYPTO_METHOD_TLS_CLIENT:
                    656:                                        sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
                    657:                                        break;
                    658:                                default:
                    659:                                        break;
                    660:                        }
                    661: 
                    662:                        clisockdata->method = sock->method;
                    663: 
                    664:                        if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
                    665:                                        NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
                    666:                                        xparam->outputs.client, 1 TSRMLS_CC) < 0) {
                    667:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
                    668: 
                    669:                                php_stream_close(xparam->outputs.client);
                    670:                                xparam->outputs.client = NULL;
                    671:                                xparam->outputs.returncode = -1;
                    672:                        }
                    673:                }
                    674:        }
                    675:        
                    676:        return xparam->outputs.client == NULL ? -1 : 0;
                    677: }
                    678: static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
                    679: {
                    680:        php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
                    681:        php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
                    682:        php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
                    683: 
                    684:        switch (option) {
                    685:                case PHP_STREAM_OPTION_CHECK_LIVENESS:
                    686:                        {
                    687:                                struct timeval tv;
                    688:                                char buf;
                    689:                                int alive = 1;
                    690: 
                    691:                                if (value == -1) {
                    692:                                        if (sslsock->s.timeout.tv_sec == -1) {
                    693:                                                tv.tv_sec = FG(default_socket_timeout);
                    694:                                                tv.tv_usec = 0;
                    695:                                        } else {
                    696:                                                tv = sslsock->connect_timeout;
                    697:                                        }
                    698:                                } else {
                    699:                                        tv.tv_sec = value;
                    700:                                        tv.tv_usec = 0;
                    701:                                }
                    702: 
                    703:                                if (sslsock->s.socket == -1) {
                    704:                                        alive = 0;
                    705:                                } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
                    706:                                        if (sslsock->ssl_active) {
                    707:                                                int n;
                    708: 
                    709:                                                do {
                    710:                                                        n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
                    711:                                                        if (n <= 0) {
                    712:                                                                int err = SSL_get_error(sslsock->ssl_handle, n);
                    713: 
                    714:                                                                if (err == SSL_ERROR_SYSCALL) {
                    715:                                                                        alive = php_socket_errno() == EAGAIN;
                    716:                                                                        break;
                    717:                                                                }
                    718: 
                    719:                                                                if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
                    720:                                                                        /* re-negotiate */
                    721:                                                                        continue;
                    722:                                                                }
                    723: 
                    724:                                                                /* any other problem is a fatal error */
                    725:                                                                alive = 0;
                    726:                                                        }
                    727:                                                        /* either peek succeeded or there was an error; we
                    728:                                                         * have set the alive flag appropriately */
                    729:                                                        break;
                    730:                                                } while (1);
                    731:                                        } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
                    732:                                                alive = 0;
                    733:                                        }
                    734:                                }
                    735:                                return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
                    736:                        }
                    737:                        
                    738:                case PHP_STREAM_OPTION_CRYPTO_API:
                    739: 
                    740:                        switch(cparam->op) {
                    741: 
                    742:                                case STREAM_XPORT_CRYPTO_OP_SETUP:
                    743:                                        cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
                    744:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    745:                                        break;
                    746:                                case STREAM_XPORT_CRYPTO_OP_ENABLE:
                    747:                                        cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
                    748:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    749:                                        break;
                    750:                                default:
                    751:                                        /* fall through */
                    752:                                        break;
                    753:                        }
                    754: 
                    755:                        break;
                    756: 
                    757:                case PHP_STREAM_OPTION_XPORT_API:
                    758:                        switch(xparam->op) {
                    759: 
                    760:                                case STREAM_XPORT_OP_CONNECT:
                    761:                                case STREAM_XPORT_OP_CONNECT_ASYNC:
                    762:                                        /* TODO: Async connects need to check the enable_on_connect option when
                    763:                                         * we notice that the connect has actually been established */
                    764:                                        php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
                    765: 
                    766:                                        if ((sslsock->enable_on_connect) &&
                    767:                                                ((xparam->outputs.returncode == 0) ||
                    768:                                                (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 
                    769:                                                xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
                    770:                                        {
                    771:                                                if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
                    772:                                                                php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
                    773:                                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
                    774:                                                        xparam->outputs.returncode = -1;
                    775:                                                }
                    776:                                        }
                    777:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    778: 
                    779:                                case STREAM_XPORT_OP_ACCEPT:
                    780:                                        /* we need to copy the additional fields that the underlying tcp transport
                    781:                                         * doesn't know about */
                    782:                                        xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
                    783: 
                    784:                                        
                    785:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    786: 
                    787:                                default:
                    788:                                        /* fall through */
                    789:                                        break;
                    790:                        }
                    791:        }
                    792: 
                    793:        return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
                    794: }
                    795: 
                    796: static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                    797: {
                    798:        php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
                    799: 
                    800:        switch(castas)  {
                    801:                case PHP_STREAM_AS_STDIO:
                    802:                        if (sslsock->ssl_active) {
                    803:                                return FAILURE;
                    804:                        }
                    805:                        if (ret)        {
                    806:                                *ret = fdopen(sslsock->s.socket, stream->mode);
                    807:                                if (*ret) {
                    808:                                        return SUCCESS;
                    809:                                }
                    810:                                return FAILURE;
                    811:                        }
                    812:                        return SUCCESS;
                    813: 
                    814:                case PHP_STREAM_AS_FD_FOR_SELECT:
                    815:                        if (ret) {
                    816:                                *(int *)ret = sslsock->s.socket;
                    817:                        }
                    818:                        return SUCCESS;
                    819: 
                    820:                case PHP_STREAM_AS_FD:
                    821:                case PHP_STREAM_AS_SOCKETD:
                    822:                        if (sslsock->ssl_active) {
                    823:                                return FAILURE;
                    824:                        }
                    825:                        if (ret) {
                    826:                                *(int *)ret = sslsock->s.socket;
                    827:                        }
                    828:                        return SUCCESS;
                    829:                default:
                    830:                        return FAILURE;
                    831:        }
                    832: }
                    833: 
                    834: php_stream_ops php_openssl_socket_ops = {
                    835:        php_openssl_sockop_write, php_openssl_sockop_read,
                    836:        php_openssl_sockop_close, php_openssl_sockop_flush,
                    837:        "tcp_socket/ssl",
                    838:        NULL, /* seek */
                    839:        php_openssl_sockop_cast,
                    840:        php_openssl_sockop_stat,
                    841:        php_openssl_sockop_set_option,
                    842: };
                    843: 
                    844: static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
                    845: 
                    846:        php_url *url;
                    847: 
                    848:        if (ctx) {
                    849:                zval **val = NULL;
                    850: 
                    851:                if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
                    852:                        return NULL;
                    853:                }
                    854:                if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
                    855:                        convert_to_string_ex(val);
                    856:                        return pestrdup(Z_STRVAL_PP(val), is_persistent);
                    857:                }
                    858:        }
                    859: 
                    860:        if (!resourcename) {
                    861:                return NULL;
                    862:        }
                    863: 
                    864:        url = php_url_parse_ex(resourcename, resourcenamelen);
                    865:        if (!url) {
                    866:                return NULL;
                    867:        }
                    868: 
                    869:        if (url->host) {
                    870:                const char * host = url->host;
                    871:                char * sni = NULL;
                    872:                size_t len = strlen(host);
                    873: 
                    874:                /* skip trailing dots */
                    875:                while (len && host[len-1] == '.') {
                    876:                        --len;
                    877:                }
                    878: 
                    879:                if (len) {
                    880:                        sni = pestrndup(host, len, is_persistent);
                    881:                }
                    882: 
                    883:                php_url_free(url);
                    884:                return sni;
                    885:        }
                    886: 
                    887:        php_url_free(url);
                    888:        return NULL;
                    889: }
                    890: 
                    891: php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
                    892:                char *resourcename, long resourcenamelen,
                    893:                const char *persistent_id, int options, int flags,
                    894:                struct timeval *timeout,
                    895:                php_stream_context *context STREAMS_DC TSRMLS_DC)
                    896: {
                    897:        php_stream *stream = NULL;
                    898:        php_openssl_netstream_data_t *sslsock = NULL;
                    899:        
                    900:        sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
                    901:        memset(sslsock, 0, sizeof(*sslsock));
                    902: 
                    903:        sslsock->s.is_blocked = 1;
                    904:        /* this timeout is used by standard stream funcs, therefor it should use the default value */
                    905:        sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
                    906:        sslsock->s.timeout.tv_usec = 0;
                    907: 
                    908:        /* use separate timeout for our private funcs */
                    909:        sslsock->connect_timeout.tv_sec = timeout->tv_sec;
                    910:        sslsock->connect_timeout.tv_usec = timeout->tv_usec;
                    911: 
                    912:        /* we don't know the socket until we have determined if we are binding or
                    913:         * connecting */
                    914:        sslsock->s.socket = -1;
                    915:        
                    916:        /* Initialize context as NULL */
                    917:        sslsock->ctx = NULL;    
                    918:        
                    919:        stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
                    920: 
                    921:        if (stream == NULL)     {
                    922:                pefree(sslsock, persistent_id ? 1 : 0);
                    923:                return NULL;
                    924:        }
                    925: 
                    926:        sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
                    927:        
                    928:        if (strncmp(proto, "ssl", protolen) == 0) {
                    929:                sslsock->enable_on_connect = 1;
                    930:                sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
                    931:        } else if (strncmp(proto, "sslv2", protolen) == 0) {
                    932: #ifdef OPENSSL_NO_SSL2
                    933:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
                    934:                return NULL;
                    935: #else
                    936:                sslsock->enable_on_connect = 1;
                    937:                sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
                    938: #endif
                    939:        } else if (strncmp(proto, "sslv3", protolen) == 0) {
                    940:                sslsock->enable_on_connect = 1;
                    941:                sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
                    942:        } else if (strncmp(proto, "tls", protolen) == 0) {
                    943:                sslsock->enable_on_connect = 1;
                    944:                sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
                    945:        }
                    946: 
                    947:        return stream;
                    948: }
                    949: 
                    950: 
                    951: 
                    952: /*
                    953:  * Local variables:
                    954:  * tab-width: 4
                    955:  * c-basic-offset: 4
                    956:  * End:
                    957:  * vim600: noet sw=4 ts=4 fdm=marker
                    958:  * vim<600: noet sw=4 ts=4
                    959:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>