PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/openssl - xp_ssl.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 258 416 62.0 %
Date: 2014-04-18 Functions: 13 14 92.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   +----------------------------------------------------------------------+
       3             :   | PHP Version 5                                                        |
       4             :   +----------------------------------------------------------------------+
       5             :   | Copyright (c) 1997-2013 The PHP Group                                |
       6             :   +----------------------------------------------------------------------+
       7             :   | This source file is subject to version 3.01 of the PHP license,      |
       8             :   | that is bundled with this package in the file LICENSE, and is        |
       9             :   | available through the world-wide-web at the following url:           |
      10             :   | http://www.php.net/license/3_01.txt                                  |
      11             :   | If you did not receive a copy of the PHP license and are unable to   |
      12             :   | obtain it through the world-wide-web, please send a note to          |
      13             :   | license@php.net so we can mail you a copy immediately.               |
      14             :   +----------------------------------------------------------------------+
      15             :   | Author: Wez Furlong <wez@thebrainroom.com>                           |
      16             :   +----------------------------------------------------------------------+
      17             : */
      18             : 
      19             : /* $Id$ */
      20             : 
      21             : #include "php.h"
      22             : #include "ext/standard/file.h"
      23             : #include "ext/standard/url.h"
      24             : #include "streams/php_streams_int.h"
      25             : #include "ext/standard/php_smart_str.h"
      26             : #include "php_network.h"
      27             : #include "php_openssl.h"
      28             : #include <openssl/ssl.h>
      29             : #include <openssl/x509.h>
      30             : #include <openssl/err.h>
      31             : 
      32             : #ifdef PHP_WIN32
      33             : #include "win32/time.h"
      34             : #endif
      35             : 
      36             : #ifdef NETWARE
      37             : #include <sys/select.h>
      38             : #endif
      39             : 
      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           0 : static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC)
      68             : {
      69           0 :         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           0 :                 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata));
      77           0 :                 while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) {
      78             : 
      79           0 :                         if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) {
      80           0 :                                 return 1;
      81           0 :                         } else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) {
      82           0 :                                 return 1;
      83             :                         }
      84             :                         
      85           0 :                         zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata));
      86             :                 }
      87             :         }
      88           0 :         return 0;
      89             : }
      90             : 
      91          27 : static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC)
      92             : {
      93          27 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
      94          27 :         int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
      95             :         char esbuf[512];
      96          27 :         smart_str ebuf = {0};
      97             :         unsigned long ecode;
      98          27 :         int retry = 1;
      99             : 
     100          27 :         switch(err) {
     101             :                 case SSL_ERROR_ZERO_RETURN:
     102             :                         /* SSL terminated (but socket may still be active) */
     103           0 :                         retry = 0;
     104           0 :                         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          27 :                         errno = EAGAIN;
     110          27 :                         retry = is_init ? 1 : sslsock->s.is_blocked;
     111          27 :                         break;
     112             :                 case SSL_ERROR_SYSCALL:
     113           0 :                         if (ERR_peek_error() == 0) {
     114           0 :                                 if (nr_bytes == 0) {
     115           0 :                                         if (!is_http_stream_talking_to_iis(stream TSRMLS_CC) && ERR_get_error() != 0) {
     116           0 :                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING,
     117             :                                                                 "SSL: fatal protocol error");
     118             :                                         }
     119           0 :                                         SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
     120           0 :                                         stream->eof = 1;
     121           0 :                                         retry = 0;
     122             :                                 } else {
     123           0 :                                         char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
     124             : 
     125           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
     126             :                                                         "SSL: %s", estr);
     127             : 
     128           0 :                                         efree(estr);
     129           0 :                                         retry = 0;
     130             :                                 }
     131           0 :                                 break;
     132             :                         }
     133             : 
     134             :                         
     135             :                         /* fall through */
     136             :                 default:
     137             :                         /* some other error */
     138           0 :                         ecode = ERR_get_error();
     139             : 
     140           0 :                         switch (ERR_GET_REASON(ecode)) {
     141             :                                 case SSL_R_NO_SHARED_CIPHER:
     142           0 :                                         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           0 :                                         retry = 0;
     144           0 :                                         break;
     145             : 
     146             :                                 default:
     147             :                                         do {
     148             :                                                 /* NULL is automatically added */
     149           0 :                                                 ERR_error_string_n(ecode, esbuf, sizeof(esbuf));
     150           0 :                                                 if (ebuf.c) {
     151           0 :                                                         smart_str_appendc(&ebuf, '\n');
     152             :                                                 }
     153           0 :                                                 smart_str_appends(&ebuf, esbuf);
     154           0 :                                         } while ((ecode = ERR_get_error()) != 0);
     155             : 
     156           0 :                                         smart_str_0(&ebuf);
     157             : 
     158           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
     159             :                                                         "SSL operation failed with code %d. %s%s",
     160             :                                                         err,
     161           0 :                                                         ebuf.c ? "OpenSSL Error messages:\n" : "",
     162           0 :                                                         ebuf.c ? ebuf.c : "");
     163           0 :                                         if (ebuf.c) {
     164           0 :                                                 smart_str_free(&ebuf);
     165             :                                         }
     166             :                         }
     167             :                                 
     168           0 :                         retry = 0;
     169           0 :                         errno = 0;
     170             :         }
     171          27 :         return retry;
     172             : }
     173             : 
     174             : 
     175         244 : static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
     176             : {
     177         244 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     178             :         int didwrite;
     179             :         
     180         244 :         if (sslsock->ssl_active) {
     181          12 :                 int retry = 1;
     182             : 
     183             :                 do {
     184          12 :                         didwrite = SSL_write(sslsock->ssl_handle, buf, count);
     185             : 
     186          12 :                         if (didwrite <= 0) {
     187           0 :                                 retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC);
     188             :                         } else {
     189          12 :                                 break;
     190             :                         }
     191           0 :                 } while(retry);
     192             : 
     193          12 :                 if (didwrite > 0) {
     194          12 :                         php_stream_notify_progress_increment(stream->context, didwrite, 0);
     195             :                 }
     196             :         } else {
     197         232 :                 didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
     198             :         }
     199             : 
     200         244 :         if (didwrite < 0) {
     201           0 :                 didwrite = 0;
     202             :         }
     203             :         
     204         244 :         return didwrite;
     205             : }
     206             : 
     207         221 : static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
     208             : {
     209         221 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     210         221 :         int nr_bytes = 0;
     211             : 
     212         221 :         if (sslsock->ssl_active) {
     213          10 :                 int retry = 1;
     214             : 
     215             :                 do {
     216          10 :                         nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
     217             : 
     218          10 :                         if (nr_bytes <= 0) {
     219           0 :                                 retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
     220           0 :                                 stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
     221             :                                 
     222             :                         } else {
     223             :                                 /* we got the data */
     224          10 :                                 break;
     225             :                         }
     226           0 :                 } while (retry);
     227             : 
     228          10 :                 if (nr_bytes > 0) {
     229          10 :                         php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
     230             :                 }
     231             :         }
     232             :         else
     233             :         {
     234         211 :                 nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
     235             :         }
     236             : 
     237         221 :         if (nr_bytes < 0) {
     238           0 :                 nr_bytes = 0;
     239             :         }
     240             : 
     241         221 :         return nr_bytes;
     242             : }
     243             : 
     244             : 
     245         209 : static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
     246             : {
     247         209 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     248             : #ifdef PHP_WIN32
     249             :         int n;
     250             : #endif
     251         209 :         if (close_handle) {
     252         209 :                 if (sslsock->ssl_active) {
     253          15 :                         SSL_shutdown(sslsock->ssl_handle);
     254          15 :                         sslsock->ssl_active = 0;
     255             :                 }
     256         209 :                 if (sslsock->ssl_handle) {
     257          16 :                         SSL_free(sslsock->ssl_handle);
     258          16 :                         sslsock->ssl_handle = NULL;
     259             :                 }
     260         209 :                 if (sslsock->ctx) {
     261          16 :                         SSL_CTX_free(sslsock->ctx);
     262          16 :                         sslsock->ctx = NULL;
     263             :                 }
     264             : #ifdef PHP_WIN32
     265             :                 if (sslsock->s.socket == -1)
     266             :                         sslsock->s.socket = SOCK_ERR;
     267             : #endif
     268         209 :                 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         188 :                         closesocket(sslsock->s.socket);
     284         188 :                         sslsock->s.socket = SOCK_ERR;
     285             :                 }
     286             :         }
     287             : 
     288         209 :         if (sslsock->sni) {
     289         150 :                 pefree(sslsock->sni, php_stream_is_persistent(stream));
     290             :         }
     291         209 :         pefree(sslsock, php_stream_is_persistent(stream));
     292             :         
     293         209 :         return 0;
     294             : }
     295             : 
     296         209 : static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
     297             : {
     298         209 :         return php_stream_socket_ops.flush(stream TSRMLS_CC);
     299             : }
     300             : 
     301           2 : static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
     302             : {
     303           2 :         return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
     304             : }
     305             : 
     306             : 
     307          16 : 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;
     313             :         
     314          16 :         if (sslsock->ssl_handle) {
     315           0 :                 if (sslsock->s.is_blocked) {
     316           0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
     317           0 :                         return -1;
     318             :                 } else {
     319           0 :                         return 0;
     320             :                 }
     321             :         }
     322             : 
     323             :         /* need to do slightly different things, based on client/server method,
     324             :          * so lets remember which method was selected */
     325             : 
     326          16 :         switch (cparam->inputs.method) {
     327             :                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     328           7 :                         sslsock->is_client = 1;
     329           7 :                         method = SSLv23_client_method();
     330           7 :                         break;
     331             :                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     332             : #ifdef OPENSSL_NO_SSL2
     333             :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
     334             :                         return -1;
     335             : #else
     336           0 :                         sslsock->is_client = 1;
     337           0 :                         method = SSLv2_client_method();
     338           0 :                         break;
     339             : #endif
     340             :                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     341           0 :                         sslsock->is_client = 1;
     342           0 :                         method = SSLv3_client_method();
     343           0 :                         break;
     344             :                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     345           5 :                         sslsock->is_client = 1;
     346           5 :                         method = TLSv1_client_method();
     347           5 :                         break;
     348             :                 case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
     349           4 :                         sslsock->is_client = 0;
     350           4 :                         method = SSLv23_server_method();
     351           4 :                         break;
     352             :                 case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
     353           0 :                         sslsock->is_client = 0;
     354           0 :                         method = SSLv3_server_method();
     355           0 :                         break;
     356             :                 case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
     357             : #ifdef OPENSSL_NO_SSL2
     358             :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
     359             :                         return -1;
     360             : #else
     361           0 :                         sslsock->is_client = 0;
     362           0 :                         method = SSLv2_server_method();
     363           0 :                         break;
     364             : #endif
     365             :                 case STREAM_CRYPTO_METHOD_TLS_SERVER:
     366           0 :                         sslsock->is_client = 0;
     367           0 :                         method = TLSv1_server_method();
     368           0 :                         break;
     369             :                 default:
     370           0 :                         return -1;
     371             : 
     372             :         }
     373             : 
     374          16 :         sslsock->ctx = SSL_CTX_new(method);
     375          16 :         if (sslsock->ctx == NULL) {
     376           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
     377           0 :                 return -1;
     378             :         }
     379             : 
     380          16 :         SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL);
     381             : 
     382             : #if OPENSSL_VERSION_NUMBER >= 0x0090806fL
     383             :         {
     384             :                 zval **val;
     385             : 
     386          31 :                 if (stream->context && SUCCESS == php_stream_context_get_option(
     387          15 :                                         stream->context, "ssl", "no_ticket", &val) && 
     388           0 :                                 zval_is_true(*val)) {
     389           0 :                         SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET);
     390             :                 }
     391             :         }
     392             : #endif
     393             : 
     394          16 :         sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
     395          16 :         if (sslsock->ssl_handle == NULL) {
     396           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
     397           0 :                 SSL_CTX_free(sslsock->ctx);
     398           0 :                 sslsock->ctx = NULL;
     399           0 :                 return -1;
     400             :         }
     401             : 
     402          16 :         if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
     403           0 :                 handle_ssl_error(stream, 0, 1 TSRMLS_CC);
     404             :         }
     405             : 
     406          16 :         if (cparam->inputs.session) {
     407           0 :                 if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
     408           0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
     409           0 :                 } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) {
     410           0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized");
     411             :                 } else {
     412           0 :                         SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
     413             :                 }
     414             :         }
     415          16 :         return 0;
     416             : }
     417             : 
     418          16 : static inline int php_openssl_enable_crypto(php_stream *stream,
     419             :                 php_openssl_netstream_data_t *sslsock,
     420             :                 php_stream_xport_crypto_param *cparam
     421             :                 TSRMLS_DC)
     422             : {
     423          16 :         int n, retry = 1;
     424             : 
     425          16 :         if (cparam->inputs.activate && !sslsock->ssl_active) {
     426             :                 struct timeval  start_time,
     427             :                                                 *timeout;
     428          16 :                 int                             blocked         = sslsock->s.is_blocked,
     429          16 :                                                 has_timeout = 0;
     430             : 
     431             : #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
     432          16 :                 if (sslsock->is_client && sslsock->sni) {
     433          10 :                         SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
     434             :                 }
     435             : #endif
     436             : 
     437          16 :                 if (!sslsock->state_set) {
     438          16 :                         if (sslsock->is_client) {
     439          12 :                                 SSL_set_connect_state(sslsock->ssl_handle);
     440             :                         } else {
     441           4 :                                 SSL_set_accept_state(sslsock->ssl_handle);
     442             :                         }
     443          16 :                         sslsock->state_set = 1;
     444             :                 }
     445             :         
     446          16 :                 if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
     447          16 :                         sslsock->s.is_blocked = 0;
     448             :                 }
     449             :                 
     450          16 :                 timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
     451          16 :                 has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
     452             :                 /* gettimeofday is not monotonic; using it here is not strictly correct */
     453          16 :                 if (has_timeout) {
     454          16 :                         gettimeofday(&start_time, NULL);
     455             :                 }
     456             :                 
     457             :                 do {
     458             :                         struct timeval  cur_time,
     459             :                                                         elapsed_time;
     460             :                         
     461          43 :                         if (sslsock->is_client) {
     462          35 :                                 n = SSL_connect(sslsock->ssl_handle);
     463             :                         } else {
     464           8 :                                 n = SSL_accept(sslsock->ssl_handle);
     465             :                         }
     466             : 
     467          43 :                         if (has_timeout) {
     468          43 :                                 gettimeofday(&cur_time, NULL);
     469          43 :                                 elapsed_time.tv_sec  = cur_time.tv_sec  - start_time.tv_sec;
     470          43 :                                 elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
     471          43 :                                 if (cur_time.tv_usec < start_time.tv_usec) {
     472          16 :                                         elapsed_time.tv_sec  -= 1L;
     473          16 :                                         elapsed_time.tv_usec += 1000000L;
     474             :                                 }
     475             :                         
     476          87 :                                 if (elapsed_time.tv_sec > timeout->tv_sec ||
     477          43 :                                                 (elapsed_time.tv_sec == timeout->tv_sec &&
     478           1 :                                                 elapsed_time.tv_usec > timeout->tv_usec)) {
     479           1 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout");
     480           1 :                                         return -1;
     481             :                                 }
     482             :                         }
     483             : 
     484          42 :                         if (n <= 0) {
     485             :                                 /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
     486          27 :                                 retry = handle_ssl_error(stream, n, blocked TSRMLS_CC);
     487          27 :                                 if (retry) {
     488             :                                         /* wait until something interesting happens in the socket. It may be a
     489             :                                          * timeout. Also consider the unlikely of possibility of a write block  */
     490          27 :                                         int err = SSL_get_error(sslsock->ssl_handle, n);
     491             :                                         struct timeval left_time;
     492             :                                         
     493          27 :                                         if (has_timeout) {
     494          27 :                                                 left_time.tv_sec  = timeout->tv_sec  - elapsed_time.tv_sec;
     495          27 :                                                 left_time.tv_usec =     timeout->tv_usec - elapsed_time.tv_usec;
     496          27 :                                                 if (timeout->tv_usec < elapsed_time.tv_usec) {
     497          27 :                                                         left_time.tv_sec  -= 1L;
     498          27 :                                                         left_time.tv_usec += 1000000L;
     499             :                                                 }
     500             :                                         }
     501          27 :                                         php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
     502             :                                                 (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
     503             :                                 }
     504             :                         } else {
     505          15 :                                 retry = 0;
     506             :                         }
     507          42 :                 } while (retry);
     508             : 
     509          15 :                 if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
     510          15 :                         sslsock->s.is_blocked = blocked;
     511             :                 }
     512             : 
     513          15 :                 if (n == 1) {
     514             :                         X509 *peer_cert;
     515             : 
     516          15 :                         peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
     517             : 
     518          15 :                         if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
     519           0 :                                 SSL_shutdown(sslsock->ssl_handle);
     520           0 :                                 n = -1;
     521             :                         } else {        
     522          15 :                                 sslsock->ssl_active = 1;
     523             : 
     524             :                                 /* allow the script to capture the peer cert
     525             :                                  * and/or the certificate chain */
     526          15 :                                 if (stream->context) {
     527             :                                         zval **val, *zcert;
     528             : 
     529          23 :                                         if (SUCCESS == php_stream_context_get_option(
     530             :                                                                 stream->context, "ssl",
     531          14 :                                                                 "capture_peer_cert", &val) &&
     532           9 :                                                         zval_is_true(*val)) {
     533           9 :                                                 MAKE_STD_ZVAL(zcert);
     534           9 :                                                 ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 
     535             :                                                                         php_openssl_get_x509_list_id()));
     536           9 :                                                 php_stream_context_set_option(stream->context,
     537             :                                                                 "ssl", "peer_certificate",
     538             :                                                                 zcert);
     539           9 :                                                 peer_cert = NULL;
     540           9 :                                                 FREE_ZVAL(zcert);
     541             :                                         }
     542             : 
     543          14 :                                         if (SUCCESS == php_stream_context_get_option(
     544             :                                                                 stream->context, "ssl",
     545          14 :                                                                 "capture_peer_cert_chain", &val) &&
     546           0 :                                                         zval_is_true(*val)) {
     547             :                                                 zval *arr;
     548             :                                                 STACK_OF(X509) *chain;
     549             : 
     550           0 :                                                 MAKE_STD_ZVAL(arr);
     551           0 :                                                 chain = SSL_get_peer_cert_chain(
     552           0 :                                                                         sslsock->ssl_handle);
     553             : 
     554           0 :                                                 if (chain && sk_X509_num(chain) > 0) {
     555             :                                                         int i;
     556           0 :                                                         array_init(arr);
     557             : 
     558           0 :                                                         for (i = 0; i < sk_X509_num(chain); i++) {
     559           0 :                                                                 X509 *mycert = X509_dup(
     560           0 :                                                                                 sk_X509_value(chain, i));
     561           0 :                                                                 MAKE_STD_ZVAL(zcert);
     562           0 :                                                                 ZVAL_RESOURCE(zcert,
     563             :                                                                                 zend_list_insert(mycert,
     564             :                                                                                         php_openssl_get_x509_list_id()));
     565           0 :                                                                 add_next_index_zval(arr, zcert);
     566             :                                                         }
     567             : 
     568             :                                                 } else {
     569           0 :                                                         ZVAL_NULL(arr);
     570             :                                                 }
     571             : 
     572           0 :                                                 php_stream_context_set_option(stream->context,
     573             :                                                                 "ssl", "peer_certificate_chain",
     574             :                                                                 arr);
     575           0 :                                                 zval_dtor(arr);
     576           0 :                                                 efree(arr);
     577             :                                         }
     578             :                                 }
     579             :                         }
     580             : 
     581          15 :                         if (peer_cert) {
     582           2 :                                 X509_free(peer_cert);
     583             :                         }
     584             :                 } else  {
     585           0 :                         n = errno == EAGAIN ? 0 : -1;
     586             :                 }
     587             : 
     588          15 :                 return n;
     589             : 
     590           0 :         } else if (!cparam->inputs.activate && sslsock->ssl_active) {
     591             :                 /* deactivate - common for server/client */
     592           0 :                 SSL_shutdown(sslsock->ssl_handle);
     593           0 :                 sslsock->ssl_active = 0;
     594             :         }
     595           0 :         return -1;
     596             : }
     597             : 
     598          41 : static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
     599             :                 php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
     600             : {
     601             :         int clisock;
     602             : 
     603          41 :         xparam->outputs.client = NULL;
     604             : 
     605         246 :         clisock = php_network_accept_incoming(sock->s.socket,
     606          41 :                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     607          41 :                         xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
     608          41 :                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     609          41 :                         xparam->want_addr ? &xparam->outputs.addrlen : NULL,
     610             :                         xparam->inputs.timeout,
     611          41 :                         xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     612             :                         &xparam->outputs.error_code
     613             :                         TSRMLS_CC);
     614             : 
     615          41 :         if (clisock >= 0) {
     616             :                 php_openssl_netstream_data_t *clisockdata;
     617             : 
     618          41 :                 clisockdata = emalloc(sizeof(*clisockdata));
     619             : 
     620          41 :                 if (clisockdata == NULL) {
     621           0 :                         closesocket(clisock);
     622             :                         /* technically a fatal error */
     623             :                 } else {
     624             :                         /* copy underlying tcp fields */
     625          41 :                         memset(clisockdata, 0, sizeof(*clisockdata));
     626          41 :                         memcpy(clisockdata, sock, sizeof(clisockdata->s));
     627             : 
     628          41 :                         clisockdata->s.socket = clisock;
     629             :                         
     630          41 :                         xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
     631          41 :                         if (xparam->outputs.client) {
     632          41 :                                 xparam->outputs.client->context = stream->context;
     633          41 :                                 if (stream->context) {
     634          41 :                                         zend_list_addref(stream->context->rsrc_id);
     635             :                                 }
     636             :                         }
     637             :                 }
     638             : 
     639          41 :                 if (xparam->outputs.client && sock->enable_on_connect) {
     640             :                         /* apply crypto */
     641           3 :                         switch (sock->method) {
     642             :                                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     643           3 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
     644           3 :                                         break;
     645             :                                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     646           0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
     647           0 :                                         break;
     648             :                                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     649           0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
     650           0 :                                         break;
     651             :                                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     652           0 :                                         sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
     653             :                                         break;
     654             :                                 default:
     655             :                                         break;
     656             :                         }
     657             : 
     658           3 :                         clisockdata->method = sock->method;
     659             : 
     660           6 :                         if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
     661           3 :                                         NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
     662           3 :                                         xparam->outputs.client, 1 TSRMLS_CC) < 0) {
     663           0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     664             : 
     665           0 :                                 php_stream_close(xparam->outputs.client);
     666           0 :                                 xparam->outputs.client = NULL;
     667           0 :                                 xparam->outputs.returncode = -1;
     668             :                         }
     669             :                 }
     670             :         }
     671             :         
     672          41 :         return xparam->outputs.client == NULL ? -1 : 0;
     673             : }
     674         307 : static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
     675             : {
     676         307 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     677         307 :         php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
     678         307 :         php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
     679             : 
     680         307 :         switch (option) {
     681             :                 case PHP_STREAM_OPTION_CHECK_LIVENESS:
     682             :                         {
     683             :                                 struct timeval tv;
     684             :                                 char buf;
     685           3 :                                 int alive = 1;
     686             : 
     687           3 :                                 if (value == -1) {
     688           0 :                                         if (sslsock->s.timeout.tv_sec == -1) {
     689           0 :                                                 tv.tv_sec = FG(default_socket_timeout);
     690           0 :                                                 tv.tv_usec = 0;
     691             :                                         } else {
     692           0 :                                                 tv = sslsock->connect_timeout;
     693             :                                         }
     694             :                                 } else {
     695           3 :                                         tv.tv_sec = value;
     696           3 :                                         tv.tv_usec = 0;
     697             :                                 }
     698             : 
     699           3 :                                 if (sslsock->s.socket == -1) {
     700           0 :                                         alive = 0;
     701           3 :                                 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
     702           3 :                                         if (sslsock->ssl_active) {
     703             :                                                 int n;
     704             : 
     705             :                                                 do {
     706           0 :                                                         n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
     707           0 :                                                         if (n <= 0) {
     708           0 :                                                                 int err = SSL_get_error(sslsock->ssl_handle, n);
     709             : 
     710           0 :                                                                 if (err == SSL_ERROR_SYSCALL) {
     711           0 :                                                                         alive = php_socket_errno() == EAGAIN;
     712           0 :                                                                         break;
     713             :                                                                 }
     714             : 
     715           0 :                                                                 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
     716             :                                                                         /* re-negotiate */
     717           0 :                                                                         continue;
     718             :                                                                 }
     719             : 
     720             :                                                                 /* any other problem is a fatal error */
     721           0 :                                                                 alive = 0;
     722             :                                                         }
     723             :                                                         /* either peek succeeded or there was an error; we
     724             :                                                          * have set the alive flag appropriately */
     725           0 :                                                         break;
     726           0 :                                                 } while (1);
     727           3 :                                         } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
     728           3 :                                                 alive = 0;
     729             :                                         }
     730             :                                 }
     731           3 :                                 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
     732             :                         }
     733             :                         
     734             :                 case PHP_STREAM_OPTION_CRYPTO_API:
     735             : 
     736          32 :                         switch(cparam->op) {
     737             : 
     738             :                                 case STREAM_XPORT_CRYPTO_OP_SETUP:
     739          16 :                                         cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
     740          16 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     741             :                                         break;
     742             :                                 case STREAM_XPORT_CRYPTO_OP_ENABLE:
     743          16 :                                         cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
     744          16 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     745             :                                         break;
     746             :                                 default:
     747             :                                         /* fall through */
     748             :                                         break;
     749             :                         }
     750             : 
     751           0 :                         break;
     752             : 
     753             :                 case PHP_STREAM_OPTION_XPORT_API:
     754         249 :                         switch(xparam->op) {
     755             : 
     756             :                                 case STREAM_XPORT_OP_CONNECT:
     757             :                                 case STREAM_XPORT_OP_CONNECT_ASYNC:
     758             :                                         /* TODO: Async connects need to check the enable_on_connect option when
     759             :                                          * we notice that the connect has actually been established */
     760          70 :                                         php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     761             : 
     762          80 :                                         if ((sslsock->enable_on_connect) &&
     763           7 :                                                 ((xparam->outputs.returncode == 0) ||
     764           1 :                                                 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 
     765           2 :                                                 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
     766             :                                         {
     767          14 :                                                 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
     768           7 :                                                                 php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
     769           1 :                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     770           1 :                                                         xparam->outputs.returncode = -1;
     771             :                                                 }
     772             :                                         }
     773          70 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     774             : 
     775             :                                 case STREAM_XPORT_OP_ACCEPT:
     776             :                                         /* we need to copy the additional fields that the underlying tcp transport
     777             :                                          * doesn't know about */
     778          41 :                                         xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
     779             : 
     780             :                                         
     781          41 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     782             : 
     783             :                                 default:
     784             :                                         /* fall through */
     785             :                                         break;
     786             :                         }
     787             :         }
     788             : 
     789         161 :         return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     790             : }
     791             : 
     792           6 : static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
     793             : {
     794           6 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     795             : 
     796           6 :         switch(castas)  {
     797             :                 case PHP_STREAM_AS_STDIO:
     798           0 :                         if (sslsock->ssl_active) {
     799           0 :                                 return FAILURE;
     800             :                         }
     801           0 :                         if (ret)        {
     802           0 :                                 *ret = fdopen(sslsock->s.socket, stream->mode);
     803           0 :                                 if (*ret) {
     804           0 :                                         return SUCCESS;
     805             :                                 }
     806           0 :                                 return FAILURE;
     807             :                         }
     808           0 :                         return SUCCESS;
     809             : 
     810             :                 case PHP_STREAM_AS_FD_FOR_SELECT:
     811           6 :                         if (ret) {
     812           6 :                                 *(int *)ret = sslsock->s.socket;
     813             :                         }
     814           6 :                         return SUCCESS;
     815             : 
     816             :                 case PHP_STREAM_AS_FD:
     817             :                 case PHP_STREAM_AS_SOCKETD:
     818           0 :                         if (sslsock->ssl_active) {
     819           0 :                                 return FAILURE;
     820             :                         }
     821           0 :                         if (ret) {
     822           0 :                                 *(int *)ret = sslsock->s.socket;
     823             :                         }
     824           0 :                         return SUCCESS;
     825             :                 default:
     826           0 :                         return FAILURE;
     827             :         }
     828             : }
     829             : 
     830             : php_stream_ops php_openssl_socket_ops = {
     831             :         php_openssl_sockop_write, php_openssl_sockop_read,
     832             :         php_openssl_sockop_close, php_openssl_sockop_flush,
     833             :         "tcp_socket/ssl",
     834             :         NULL, /* seek */
     835             :         php_openssl_sockop_cast,
     836             :         php_openssl_sockop_stat,
     837             :         php_openssl_sockop_set_option,
     838             : };
     839             : 
     840         139 : static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
     841             : 
     842             :         php_url *url;
     843             : 
     844         139 :         if (ctx) {
     845         111 :                 zval **val = NULL;
     846             : 
     847         111 :                 if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
     848           2 :                         return NULL;
     849             :                 }
     850         109 :                 if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
     851           3 :                         convert_to_string_ex(val);
     852           3 :                         return pestrdup(Z_STRVAL_PP(val), is_persistent);
     853             :                 }
     854             :         }
     855             : 
     856         134 :         if (!resourcename) {
     857           0 :                 return NULL;
     858             :         }
     859             : 
     860         134 :         url = php_url_parse_ex(resourcename, resourcenamelen);
     861         134 :         if (!url) {
     862           0 :                 return NULL;
     863             :         }
     864             : 
     865         134 :         if (url->host) {
     866         118 :                 const char * host = url->host;
     867         118 :                 char * sni = NULL;
     868         118 :                 size_t len = strlen(host);
     869             : 
     870             :                 /* skip trailing dots */
     871         236 :                 while (len && host[len-1] == '.') {
     872           0 :                         --len;
     873             :                 }
     874             : 
     875         118 :                 if (len) {
     876         118 :                         sni = pestrndup(host, len, is_persistent);
     877             :                 }
     878             : 
     879         118 :                 php_url_free(url);
     880         118 :                 return sni;
     881             :         }
     882             : 
     883          16 :         php_url_free(url);
     884          16 :         return NULL;
     885             : }
     886             : 
     887         139 : php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
     888             :                 char *resourcename, long resourcenamelen,
     889             :                 const char *persistent_id, int options, int flags,
     890             :                 struct timeval *timeout,
     891             :                 php_stream_context *context STREAMS_DC TSRMLS_DC)
     892             : {
     893         139 :         php_stream *stream = NULL;
     894         139 :         php_openssl_netstream_data_t *sslsock = NULL;
     895             :         
     896         139 :         sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
     897         139 :         memset(sslsock, 0, sizeof(*sslsock));
     898             : 
     899         139 :         sslsock->s.is_blocked = 1;
     900             :         /* this timeout is used by standard stream funcs, therefor it should use the default value */
     901         139 :         sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
     902         139 :         sslsock->s.timeout.tv_usec = 0;
     903             : 
     904             :         /* use separate timeout for our private funcs */
     905         139 :         sslsock->connect_timeout.tv_sec = timeout->tv_sec;
     906         139 :         sslsock->connect_timeout.tv_usec = timeout->tv_usec;
     907             : 
     908             :         /* we don't know the socket until we have determined if we are binding or
     909             :          * connecting */
     910         139 :         sslsock->s.socket = -1;
     911             :         
     912             :         /* Initialize context as NULL */
     913         139 :         sslsock->ctx = NULL; 
     914             :         
     915         139 :         stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
     916             : 
     917         139 :         if (stream == NULL)     {
     918           0 :                 pefree(sslsock, persistent_id ? 1 : 0);
     919           0 :                 return NULL;
     920             :         }
     921             : 
     922         139 :         sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
     923             :         
     924         139 :         if (strncmp(proto, "ssl", protolen) == 0) {
     925          10 :                 sslsock->enable_on_connect = 1;
     926          10 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
     927         129 :         } else if (strncmp(proto, "sslv2", protolen) == 0) {
     928             : #ifdef OPENSSL_NO_SSL2
     929             :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against");
     930             :                 return NULL;
     931             : #else
     932           0 :                 sslsock->enable_on_connect = 1;
     933           0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
     934             : #endif
     935         129 :         } else if (strncmp(proto, "sslv3", protolen) == 0) {
     936           0 :                 sslsock->enable_on_connect = 1;
     937           0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
     938         129 :         } else if (strncmp(proto, "tls", protolen) == 0) {
     939           0 :                 sslsock->enable_on_connect = 1;
     940           0 :                 sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
     941             :         }
     942             : 
     943         139 :         return stream;
     944             : }
     945             : 
     946             : 
     947             : 
     948             : /*
     949             :  * Local variables:
     950             :  * tab-width: 4
     951             :  * c-basic-offset: 4
     952             :  * End:
     953             :  * vim600: noet sw=4 ts=4 fdm=marker
     954             :  * vim<600: noet sw=4 ts=4
     955             :  */

Generated by: LCOV version 1.10

Generated at Fri, 18 Apr 2014 07:01:31 +0000 (4 hours ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.