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

LTP GCOV extension - code coverage report
Current view: directory - openssl - xp_ssl.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 367
Code covered: 55.6 % Executed lines: 204
Legend: not executed executed

       1                 : /*
       2                 :   +----------------------------------------------------------------------+
       3                 :   | PHP Version 6                                                        |
       4                 :   +----------------------------------------------------------------------+
       5                 :   | Copyright (c) 1997-2009 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: xp_ssl.c 289860 2009-10-22 18:48:38Z lbarnaud $ */
      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               0 : {
      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                 : static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC)
      92           10085 : {
      93           10085 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
      94           10085 :         int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
      95                 :         char esbuf[512];
      96           10085 :         smart_str ebuf = {0};
      97                 :         unsigned long ecode;
      98           10085 :         int retry = 1;
      99                 : 
     100           10085 :         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           10085 :                         errno = EAGAIN;
     110           10085 :                         retry = is_init ? 1 : sslsock->s.is_blocked;
     111           10085 :                         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)) {
     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                 :                                                         ebuf.c ? "OpenSSL Error messages:\n" : "",
     162                 :                                                         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           10085 :         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             143 : {
     177             143 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     178                 :         int didwrite;
     179                 :         
     180             143 :         if (sslsock->ssl_active) {
     181               3 :                 int retry = 1;
     182                 : 
     183                 :                 do {
     184               3 :                         didwrite = SSL_write(sslsock->ssl_handle, buf, count);
     185                 : 
     186               3 :                         if (didwrite <= 0) {
     187               0 :                                 retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC); 
     188                 :                         } else {
     189               3 :                                 break;
     190                 :                         }
     191               0 :                 } while(retry);
     192                 : 
     193               3 :                 if (didwrite > 0) {
     194               3 :                         php_stream_notify_progress_increment(stream->context, didwrite, 0);
     195                 :                 }
     196                 :         } else {
     197             140 :                 didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
     198                 :         }
     199                 : 
     200             143 :         if (didwrite < 0) {
     201               0 :                 didwrite = 0;
     202                 :         }
     203                 :         
     204             143 :         return didwrite;
     205                 : }
     206                 : 
     207                 : static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
     208             129 : {
     209             129 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     210             129 :         int nr_bytes = 0;
     211                 : 
     212             129 :         if (sslsock->ssl_active) {
     213               3 :                 int retry = 1;
     214                 : 
     215                 :                 do {
     216               3 :                         nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
     217                 : 
     218               3 :                         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               3 :                                 break;
     225                 :                         }
     226               0 :                 } while (retry);
     227                 : 
     228               3 :                 if (nr_bytes > 0) {
     229               3 :                         php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
     230                 :                 }
     231                 :         }
     232                 :         else
     233                 :         {
     234             126 :                 nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
     235                 :         }
     236                 : 
     237             129 :         if (nr_bytes < 0) {
     238               0 :                 nr_bytes = 0;
     239                 :         }
     240                 : 
     241             129 :         return nr_bytes;
     242                 : }
     243                 : 
     244                 : 
     245                 : static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
     246             174 : {
     247             174 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     248                 : #ifdef PHP_WIN32
     249                 :         int n;
     250                 : #endif
     251             174 :         if (close_handle) {
     252             174 :                 if (sslsock->ssl_active) {
     253               4 :                         SSL_shutdown(sslsock->ssl_handle);
     254               4 :                         sslsock->ssl_active = 0;
     255                 :                 }
     256             174 :                 if (sslsock->ssl_handle) {
     257               4 :                         SSL_free(sslsock->ssl_handle);
     258               4 :                         sslsock->ssl_handle = NULL;
     259                 :                 }
     260             174 :                 if (sslsock->ctx) {
     261               4 :                         SSL_CTX_free(sslsock->ctx);
     262               4 :                         sslsock->ctx = NULL;
     263                 :                 }
     264                 : #ifdef PHP_WIN32
     265                 :                 if (sslsock->s.socket == -1)
     266                 :                         sslsock->s.socket = SOCK_ERR;
     267                 : #endif
     268             174 :                 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             155 :                         closesocket(sslsock->s.socket);
     284             155 :                         sslsock->s.socket = SOCK_ERR;
     285                 :                 }
     286                 :         }
     287                 : 
     288             174 :         if (sslsock->sni) {
     289             120 :                 pefree(sslsock->sni, php_stream_is_persistent(stream));
     290                 :         }
     291             174 :         pefree(sslsock, php_stream_is_persistent(stream));
     292                 :         
     293             174 :         return 0;
     294                 : }
     295                 : 
     296                 : static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
     297             174 : {
     298             174 :         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               0 : {
     303               0 :         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               4 : {
     312                 :         SSL_METHOD *method;
     313                 :         
     314               4 :         if (sslsock->ssl_handle) {
     315               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
     316               0 :                 return -1;
     317                 :         }
     318                 : 
     319                 :         /* need to do slightly different things, based on client/server method,
     320                 :          * so lets remember which method was selected */
     321                 : 
     322               4 :         switch (cparam->inputs.method) {
     323                 :                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     324               2 :                         sslsock->is_client = 1;
     325               2 :                         method = SSLv23_client_method();
     326               2 :                         break;
     327                 :                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     328               0 :                         sslsock->is_client = 1;
     329               0 :                         method = SSLv2_client_method();
     330               0 :                         break;
     331                 :                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     332               0 :                         sslsock->is_client = 1;
     333               0 :                         method = SSLv3_client_method();
     334               0 :                         break;
     335                 :                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     336               0 :                         sslsock->is_client = 1;
     337               0 :                         method = TLSv1_client_method();
     338               0 :                         break;
     339                 :                 case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
     340               2 :                         sslsock->is_client = 0;
     341               2 :                         method = SSLv23_server_method();
     342               2 :                         break;
     343                 :                 case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
     344               0 :                         sslsock->is_client = 0;
     345               0 :                         method = SSLv3_server_method();
     346               0 :                         break;
     347                 :                 case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
     348               0 :                         sslsock->is_client = 0;
     349               0 :                         method = SSLv2_server_method();
     350               0 :                         break;
     351                 :                 case STREAM_CRYPTO_METHOD_TLS_SERVER:
     352               0 :                         sslsock->is_client = 0;
     353               0 :                         method = TLSv1_server_method();
     354               0 :                         break;
     355                 :                 default:
     356               0 :                         return -1;
     357                 : 
     358                 :         }
     359                 : 
     360               4 :         sslsock->ctx = SSL_CTX_new(method);
     361               4 :         if (sslsock->ctx == NULL) {
     362               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
     363               0 :                 return -1;
     364                 :         }
     365                 : 
     366               4 :         SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL);
     367                 : 
     368               4 :         sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
     369               4 :         if (sslsock->ssl_handle == NULL) {
     370               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
     371               0 :                 SSL_CTX_free(sslsock->ctx);
     372               0 :                 sslsock->ctx = NULL;
     373               0 :                 return -1;
     374                 :         }
     375                 : 
     376               4 :         if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
     377               0 :                 handle_ssl_error(stream, 0, 1 TSRMLS_CC);
     378                 :         }
     379                 : 
     380               4 :         if (cparam->inputs.session) {
     381               0 :                 if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
     382               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
     383                 :                 } else {
     384               0 :                         SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
     385                 :                 }
     386                 :         }
     387               4 :         return 0;
     388                 : }
     389                 : 
     390                 : static inline int php_openssl_enable_crypto(php_stream *stream,
     391                 :                 php_openssl_netstream_data_t *sslsock,
     392                 :                 php_stream_xport_crypto_param *cparam
     393                 :                 TSRMLS_DC)
     394               4 : {
     395               4 :         int n, retry = 1;
     396                 : 
     397               4 :         if (cparam->inputs.activate && !sslsock->ssl_active) {
     398               4 :                 float timeout = sslsock->connect_timeout.tv_sec + sslsock->connect_timeout.tv_usec / 1000000;
     399               4 :                 int blocked = sslsock->s.is_blocked;
     400                 : 
     401                 : #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
     402                 :                 if (sslsock->is_client && sslsock->sni) {
     403                 :                         SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
     404                 :                 }
     405                 : #endif
     406                 : 
     407               4 :                 if (!sslsock->state_set) {
     408               4 :                         if (sslsock->is_client) {
     409               2 :                                 SSL_set_connect_state(sslsock->ssl_handle);
     410                 :                         } else {
     411               2 :                                 SSL_set_accept_state(sslsock->ssl_handle);
     412                 :                         }
     413               4 :                         sslsock->state_set = 1;
     414                 :                 }
     415                 :         
     416               4 :                 if (sslsock->is_client && SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
     417               2 :                         sslsock->s.is_blocked = 0;
     418                 :                 }
     419                 :                 do {
     420           10089 :                         if (sslsock->is_client) {
     421                 :                                 struct timeval tvs, tve;
     422                 :                                 struct timezone tz;
     423                 : 
     424           10087 :                                 gettimeofday(&tvs, &tz);
     425           10087 :                                 n = SSL_connect(sslsock->ssl_handle);
     426           10087 :                                 gettimeofday(&tve, &tz);
     427                 : 
     428           10087 :                                 timeout -= (tve.tv_sec + (float) tve.tv_usec / 1000000) - (tvs.tv_sec + (float) tvs.tv_usec / 1000000);
     429           10087 :                                 if (timeout < 0) {
     430               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: connection timeout");
     431               0 :                                         return -1;
     432                 :                                 }
     433                 :                         } else {
     434               2 :                                 n = SSL_accept(sslsock->ssl_handle);
     435                 :                         }
     436                 : 
     437           10089 :                         if (n <= 0) {
     438           10085 :                                 retry = handle_ssl_error(stream, n, 1 TSRMLS_CC); 
     439                 :                         } else {
     440               4 :                                 break;
     441                 :                         }
     442           10085 :                 } while (retry);
     443                 : 
     444               4 :                 if (sslsock->is_client && sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
     445               2 :                         sslsock->s.is_blocked = blocked;
     446                 :                 }
     447                 : 
     448               4 :                 if (n == 1) {
     449                 :                         X509 *peer_cert;
     450                 : 
     451               4 :                         peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
     452                 : 
     453               4 :                         if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
     454               0 :                                 SSL_shutdown(sslsock->ssl_handle);
     455                 :                         } else {        
     456               4 :                                 sslsock->ssl_active = 1;
     457                 : 
     458                 :                                 /* allow the script to capture the peer cert
     459                 :                                  * and/or the certificate chain */
     460               4 :                                 if (stream->context) {
     461                 :                                         zval **val, *zcert;
     462                 : 
     463               3 :                                         if (SUCCESS == php_stream_context_get_option(
     464                 :                                                                 stream->context, "ssl",
     465                 :                                                                 "capture_peer_cert", &val) &&
     466                 :                                                         zval_is_true(*val)) {
     467               0 :                                                 MAKE_STD_ZVAL(zcert);
     468               0 :                                                 ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 
     469                 :                                                                         php_openssl_get_x509_list_id()));
     470               0 :                                                 php_stream_context_set_option(stream->context,
     471                 :                                                                 "ssl", "peer_certificate",
     472                 :                                                                 zcert);
     473               0 :                                                 peer_cert = NULL;
     474               0 :                                                 FREE_ZVAL(zcert);
     475                 :                                         }
     476                 : 
     477               3 :                                         if (SUCCESS == php_stream_context_get_option(
     478                 :                                                                 stream->context, "ssl",
     479                 :                                                                 "capture_peer_cert_chain", &val) &&
     480                 :                                                         zval_is_true(*val)) {
     481                 :                                                 zval *arr;
     482                 :                                                 STACK_OF(X509) *chain;
     483                 : 
     484               0 :                                                 MAKE_STD_ZVAL(arr);
     485               0 :                                                 chain = SSL_get_peer_cert_chain(
     486                 :                                                                         sslsock->ssl_handle);
     487                 : 
     488               0 :                                                 if (chain && sk_X509_num(chain) > 0) {
     489                 :                                                         int i;
     490               0 :                                                         array_init(arr);
     491                 : 
     492               0 :                                                         for (i = 0; i < sk_X509_num(chain); i++) {
     493                 :                                                                 X509 *mycert = X509_dup(
     494               0 :                                                                                 sk_X509_value(chain, i));
     495               0 :                                                                 MAKE_STD_ZVAL(zcert);
     496               0 :                                                                 ZVAL_RESOURCE(zcert,
     497                 :                                                                                 zend_list_insert(mycert,
     498                 :                                                                                         php_openssl_get_x509_list_id()));
     499               0 :                                                                 add_next_index_zval(arr, zcert);
     500               0 :                                                                 FREE_ZVAL(zcert);
     501                 :                                                         }
     502                 :                                                 } else {
     503               0 :                                                         ZVAL_NULL(arr);
     504                 :                                                 }
     505                 : 
     506               0 :                                                 php_stream_context_set_option(stream->context,
     507                 :                                                                 "ssl", "peer_certificate_chain",
     508                 :                                                                 arr);
     509               0 :                                                 zval_dtor(arr);
     510               0 :                                                 efree(arr);
     511                 :                                         }
     512                 :                                 }
     513                 :                         }
     514                 : 
     515               4 :                         if (peer_cert) {
     516               2 :                                 X509_free(peer_cert);
     517                 :                         }
     518                 :                 } else  {
     519               0 :                         n = errno == EAGAIN ? 0 : -1;
     520                 :                 }
     521                 : 
     522               4 :                 return n;
     523                 : 
     524               0 :         } else if (!cparam->inputs.activate && sslsock->ssl_active) {
     525                 :                 /* deactivate - common for server/client */
     526               0 :                 SSL_shutdown(sslsock->ssl_handle);
     527               0 :                 sslsock->ssl_active = 0;
     528                 :         }
     529               0 :         return -1;
     530                 : }
     531                 : 
     532                 : static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
     533                 :                 php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
     534              40 : {
     535                 :         int clisock;
     536                 : 
     537              40 :         xparam->outputs.client = NULL;
     538                 : 
     539              40 :         clisock = php_network_accept_incoming(sock->s.socket,
     540                 :                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     541                 :                         xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
     542                 :                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     543                 :                         xparam->want_addr ? &xparam->outputs.addrlen : NULL,
     544                 :                         xparam->inputs.timeout,
     545                 :                         xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     546                 :                         &xparam->outputs.error_code
     547                 :                         TSRMLS_CC);
     548                 : 
     549              40 :         if (clisock >= 0) {
     550                 :                 php_openssl_netstream_data_t *clisockdata;
     551                 : 
     552              40 :                 clisockdata = emalloc(sizeof(*clisockdata));
     553                 : 
     554              40 :                 if (clisockdata == NULL) {
     555               0 :                         closesocket(clisock);
     556                 :                         /* technically a fatal error */
     557                 :                 } else {
     558                 :                         /* copy underlying tcp fields */
     559              40 :                         memset(clisockdata, 0, sizeof(*clisockdata));
     560              40 :                         memcpy(clisockdata, sock, sizeof(clisockdata->s));
     561                 : 
     562              40 :                         clisockdata->s.socket = clisock;
     563                 :                         
     564              40 :                         xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
     565              40 :                         if (xparam->outputs.client) {
     566              40 :                                 xparam->outputs.client->context = stream->context;
     567              40 :                                 if (stream->context) {
     568              40 :                                         zend_list_addref(stream->context->rsrc_id);
     569                 :                                 }
     570                 :                         }
     571                 :                 }
     572                 : 
     573              40 :                 if (xparam->outputs.client && sock->enable_on_connect) {
     574                 :                         /* apply crypto */
     575               2 :                         switch (sock->method) {
     576                 :                                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     577               2 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
     578               2 :                                         break;
     579                 :                                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     580               0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
     581               0 :                                         break;
     582                 :                                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     583               0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
     584               0 :                                         break;
     585                 :                                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     586               0 :                                         sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
     587                 :                                         break;
     588                 :                         }
     589                 : 
     590               2 :                         clisockdata->method = sock->method;
     591                 : 
     592               2 :                         if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
     593                 :                                         NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
     594                 :                                         xparam->outputs.client, 1 TSRMLS_CC) < 0) {
     595               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     596                 : 
     597               0 :                                 php_stream_close(xparam->outputs.client);
     598               0 :                                 xparam->outputs.client = NULL;
     599               0 :                                 xparam->outputs.returncode = -1;
     600                 :                         }
     601                 :                 }
     602                 :         }
     603                 :         
     604              40 :         return xparam->outputs.client == NULL ? -1 : 0;
     605                 : }
     606                 : static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
     607             243 : {
     608             243 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     609             243 :         php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
     610             243 :         php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
     611                 : 
     612             243 :         switch (option) {
     613                 :                 case PHP_STREAM_OPTION_CHECK_LIVENESS:
     614                 :                         {
     615                 :                                 struct timeval tv;
     616                 :                                 char buf;
     617               3 :                                 int alive = 1;
     618                 : 
     619               3 :                                 if (value == -1) {
     620               0 :                                         if (sslsock->s.timeout.tv_sec == -1) {
     621               0 :                                                 tv.tv_sec = FG(default_socket_timeout);
     622               0 :                                                 tv.tv_usec = 0;
     623                 :                                         } else {
     624               0 :                                                 tv = sslsock->connect_timeout;
     625                 :                                         }
     626                 :                                 } else {
     627               3 :                                         tv.tv_sec = value;
     628               3 :                                         tv.tv_usec = 0;
     629                 :                                 }
     630                 : 
     631               3 :                                 if (sslsock->s.socket == -1) {
     632               0 :                                         alive = 0;
     633               3 :                                 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
     634               3 :                                         if (sslsock->ssl_active) {
     635                 :                                                 int n;
     636                 : 
     637                 :                                                 do {
     638               0 :                                                         n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
     639               0 :                                                         if (n <= 0) {
     640               0 :                                                                 int err = SSL_get_error(sslsock->ssl_handle, n);
     641                 : 
     642               0 :                                                                 if (err == SSL_ERROR_SYSCALL) {
     643               0 :                                                                         alive = php_socket_errno() == EAGAIN;
     644               0 :                                                                         break;
     645                 :                                                                 }
     646                 : 
     647               0 :                                                                 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
     648                 :                                                                         /* re-negotiate */
     649                 :                                                                         continue;
     650                 :                                                                 }
     651                 : 
     652                 :                                                                 /* any other problem is a fatal error */
     653               0 :                                                                 alive = 0;
     654                 :                                                         }
     655                 :                                                         /* either peek succeeded or there was an error; we
     656                 :                                                          * have set the alive flag appropriately */
     657               0 :                                                         break;
     658               0 :                                                 } while (1);
     659               3 :                                         } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
     660               3 :                                                 alive = 0;
     661                 :                                         }
     662                 :                                 }
     663               3 :                                 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
     664                 :                         }
     665                 :                         
     666                 :                 case PHP_STREAM_OPTION_CRYPTO_API:
     667                 : 
     668               8 :                         switch(cparam->op) {
     669                 : 
     670                 :                                 case STREAM_XPORT_CRYPTO_OP_SETUP:
     671               4 :                                         cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
     672               4 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     673                 :                                         break;
     674                 :                                 case STREAM_XPORT_CRYPTO_OP_ENABLE:
     675               4 :                                         cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
     676               4 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     677                 :                                         break;
     678                 :                                 default:
     679                 :                                         /* fall through */
     680                 :                                         break;
     681                 :                         }
     682                 : 
     683               0 :                         break;
     684                 : 
     685                 :                 case PHP_STREAM_OPTION_XPORT_API:
     686             211 :                         switch(xparam->op) {
     687                 : 
     688                 :                                 case STREAM_XPORT_OP_CONNECT:
     689                 :                                 case STREAM_XPORT_OP_CONNECT_ASYNC:
     690                 :                                         /* TODO: Async connects need to check the enable_on_connect option when
     691                 :                                          * we notice that the connect has actually been established */
     692              43 :                                         php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     693                 : 
     694              43 :                                         if ((sslsock->enable_on_connect) &&
     695                 :                                                 ((xparam->outputs.returncode == 0) ||
     696                 :                                                 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 
     697                 :                                                 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
     698                 :                                         {
     699               2 :                                                 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
     700                 :                                                                 php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
     701               0 :                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     702               0 :                                                         xparam->outputs.returncode = -1;
     703                 :                                                 }
     704                 :                                         }
     705              43 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     706                 : 
     707                 :                                 case STREAM_XPORT_OP_ACCEPT:
     708                 :                                         /* we need to copy the additional fields that the underlying tcp transport
     709                 :                                          * doesn't know about */
     710              40 :                                         xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
     711                 : 
     712                 :                                         
     713              40 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     714                 : 
     715                 :                                 default:
     716                 :                                         /* fall through */
     717                 :                                         break;
     718                 :                         }
     719                 :         }
     720                 : 
     721             149 :         return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     722                 : }
     723                 : 
     724                 : static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
     725               6 : {
     726               6 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     727                 : 
     728               6 :         switch(castas)  {
     729                 :                 case PHP_STREAM_AS_STDIO:
     730               0 :                         if (sslsock->ssl_active) {
     731               0 :                                 return FAILURE;
     732                 :                         }
     733               0 :                         if (ret)        {
     734               0 :                                 *ret = fdopen(sslsock->s.socket, stream->mode);
     735               0 :                                 if (*ret) {
     736               0 :                                         return SUCCESS;
     737                 :                                 }
     738               0 :                                 return FAILURE;
     739                 :                         }
     740               0 :                         return SUCCESS;
     741                 : 
     742                 :                 case PHP_STREAM_AS_FD_FOR_SELECT:
     743               6 :                         if (ret) {
     744               6 :                                 *(int *)ret = sslsock->s.socket;
     745                 :                         }
     746               6 :                         return SUCCESS;
     747                 : 
     748                 :                 case PHP_STREAM_AS_FD:
     749                 :                 case PHP_STREAM_AS_SOCKETD:
     750               0 :                         if (sslsock->ssl_active) {
     751               0 :                                 return FAILURE;
     752                 :                         }
     753               0 :                         if (ret) {
     754               0 :                                 *(int *)ret = sslsock->s.socket;
     755                 :                         }
     756               0 :                         return SUCCESS;
     757                 :                 default:
     758               0 :                         return FAILURE;
     759                 :         }
     760                 : }
     761                 : 
     762                 : php_stream_ops php_openssl_socket_ops = {
     763                 :         php_openssl_sockop_write, php_openssl_sockop_read,
     764                 :         php_openssl_sockop_close, php_openssl_sockop_flush,
     765                 :         "tcp_socket/ssl",
     766                 :         NULL, /* seek */
     767                 :         php_openssl_sockop_cast,
     768                 :         php_openssl_sockop_stat,
     769                 :         php_openssl_sockop_set_option,
     770                 : };
     771                 : 
     772             106 : static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
     773                 : 
     774                 :         php_url *url;
     775                 : 
     776             106 :         if (ctx) {
     777              77 :                 zval **val = NULL;
     778                 : 
     779              77 :                 if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
     780               0 :                         return NULL;
     781                 :                 }
     782              77 :                 if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
     783               0 :                         convert_to_string_with_converter_ex(val, UG(utf8_conv));
     784               0 :                         return pestrdup(Z_STRVAL_PP(val), is_persistent);
     785                 :                 }
     786                 :         }
     787                 : 
     788             106 :         if (!resourcename) {
     789               0 :                 return NULL;
     790                 :         }
     791                 : 
     792             106 :         url = php_url_parse_ex(resourcename, resourcenamelen);
     793             106 :         if (!url) {
     794               0 :                 return NULL;
     795                 :         }
     796                 : 
     797             106 :         if (url->host) {
     798              92 :                 const char * host = url->host;
     799              92 :                 char * sni = NULL;
     800              92 :                 size_t len = strlen(host);
     801                 : 
     802                 :                 /* skip trailing dots */
     803             184 :                 while (len && host[len-1] == '.') {
     804               0 :                         --len;
     805                 :                 }
     806                 : 
     807              92 :                 if (len) {
     808              92 :                         sni = pestrndup(host, len, is_persistent);
     809                 :                 }
     810                 : 
     811              92 :                 php_url_free(url);
     812              92 :                 return sni;
     813                 :         }
     814                 : 
     815              14 :         php_url_free(url);
     816              14 :         return NULL;
     817                 : }
     818                 : 
     819                 : php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
     820                 :                 char *resourcename, long resourcenamelen,
     821                 :                 const char *persistent_id, int options, int flags,
     822                 :                 struct timeval *timeout,
     823                 :                 php_stream_context *context STREAMS_DC TSRMLS_DC)
     824             106 : {
     825             106 :         php_stream *stream = NULL;
     826             106 :         php_openssl_netstream_data_t *sslsock = NULL;
     827                 :         
     828             106 :         sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
     829             106 :         memset(sslsock, 0, sizeof(*sslsock));
     830                 : 
     831             106 :         sslsock->s.is_blocked = 1;
     832                 :         /* this timeout is used by standard stream funcs, therefor it should use the default value */
     833             106 :         sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
     834             106 :         sslsock->s.timeout.tv_usec = 0;
     835                 : 
     836                 :         /* use separate timeout for our private funcs */
     837             106 :         sslsock->connect_timeout.tv_sec = timeout->tv_sec;
     838             106 :         sslsock->connect_timeout.tv_usec = timeout->tv_usec;
     839                 : 
     840                 :         /* we don't know the socket until we have determined if we are binding or
     841                 :          * connecting */
     842             106 :         sslsock->s.socket = -1;
     843                 :         
     844                 :         /* Initialize context as NULL */
     845             106 :         sslsock->ctx = NULL;
     846                 :         
     847             106 :         stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
     848                 : 
     849             106 :         if (stream == NULL)     {
     850               0 :                 pefree(sslsock, persistent_id ? 1 : 0);
     851               0 :                 return NULL;
     852                 :         }
     853                 : 
     854             106 :         sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
     855                 :         
     856             106 :         if (strncmp(proto, "ssl", protolen) == 0) {
     857               4 :                 sslsock->enable_on_connect = 1;
     858               4 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
     859             102 :         } else if (strncmp(proto, "sslv2", protolen) == 0) {
     860               0 :                 sslsock->enable_on_connect = 1;
     861               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
     862             102 :         } else if (strncmp(proto, "sslv3", protolen) == 0) {
     863               0 :                 sslsock->enable_on_connect = 1;
     864               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
     865             102 :         } else if (strncmp(proto, "tls", protolen) == 0) {
     866               0 :                 sslsock->enable_on_connect = 1;
     867               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
     868                 :         }
     869                 : 
     870             106 :         return stream;
     871                 : }
     872                 : 
     873                 : 
     874                 : 
     875                 : /*
     876                 :  * Local variables:
     877                 :  * tab-width: 4
     878                 :  * c-basic-offset: 4
     879                 :  * End:
     880                 :  * vim600: noet sw=4 ts=4 fdm=marker
     881                 :  * vim<600: noet sw=4 ts=4
     882                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:34 +0000 (33 hours ago)

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