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-19 Instrumented lines: 348
Code covered: 53.2 % Executed lines: 185
Legend: not executed executed

       1                 : /*
       2                 :   +----------------------------------------------------------------------+
       3                 :   | PHP Version 5                                                        |
       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 288034 2009-09-04 07:59:48Z srinatar $ */
      20                 : 
      21                 : #include "php.h"
      22                 : #include "ext/standard/file.h"
      23                 : #include "streams/php_streams_int.h"
      24                 : #include "php_network.h"
      25                 : #include "php_openssl.h"
      26                 : #include <openssl/ssl.h>
      27                 : #include <openssl/x509.h>
      28                 : #include <openssl/err.h>
      29                 : 
      30                 : #ifdef PHP_WIN32
      31                 : #include "win32/time.h"
      32                 : #endif
      33                 : 
      34                 : #ifdef NETWARE
      35                 : #include <sys/select.h>
      36                 : #endif
      37                 : 
      38                 : int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC);
      39                 : SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC);
      40                 : int php_openssl_get_x509_list_id(void);
      41                 : 
      42                 : /* This implementation is very closely tied to the that of the native
      43                 :  * sockets implemented in the core.
      44                 :  * Don't try this technique in other extensions!
      45                 :  * */
      46                 : 
      47                 : typedef struct _php_openssl_netstream_data_t {
      48                 :         php_netstream_data_t s;
      49                 :         SSL *ssl_handle;
      50                 :         SSL_CTX *ctx;
      51                 :         struct timeval connect_timeout;
      52                 :         int enable_on_connect;
      53                 :         int is_client;
      54                 :         int ssl_active;
      55                 :         php_stream_xport_crypt_method_t method;
      56                 :         unsigned state_set:1;
      57                 :         unsigned _spare:31;
      58                 : } php_openssl_netstream_data_t;
      59                 : 
      60                 : php_stream_ops php_openssl_socket_ops;
      61                 : 
      62                 : /* it doesn't matter that we do some hash traversal here, since it is done only
      63                 :  * in an error condition arising from a network connection problem */
      64                 : static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC)
      65               0 : {
      66               0 :         if (stream->wrapperdata && stream->wrapper && strcasecmp(stream->wrapper->wops->label, "HTTP") == 0) {
      67                 :                 /* the wrapperdata is an array zval containing the headers */
      68                 :                 zval **tmp;
      69                 : 
      70                 : #define SERVER_MICROSOFT_IIS    "Server: Microsoft-IIS"
      71                 : #define SERVER_GOOGLE "Server: GFE/"
      72                 :                 
      73               0 :                 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata));
      74               0 :                 while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) {
      75                 : 
      76               0 :                         if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) {
      77               0 :                                 return 1;
      78               0 :                         } else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) {
      79               0 :                                 return 1;
      80                 :                         }
      81                 :                         
      82               0 :                         zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata));
      83                 :                 }
      84                 :         }
      85               0 :         return 0;
      86                 : }
      87                 : 
      88                 : static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC)
      89           10175 : {
      90           10175 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
      91           10175 :         int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
      92                 :         char esbuf[512];
      93           10175 :         char *ebuf = NULL, *wptr = NULL;
      94           10175 :         size_t ebuf_size = 0;
      95                 :         unsigned long code, ecode;
      96           10175 :         int retry = 1;
      97                 : 
      98           10175 :         switch(err) {
      99                 :                 case SSL_ERROR_ZERO_RETURN:
     100                 :                         /* SSL terminated (but socket may still be active) */
     101               0 :                         retry = 0;
     102               0 :                         break;
     103                 :                 case SSL_ERROR_WANT_READ:
     104                 :                 case SSL_ERROR_WANT_WRITE:
     105                 :                         /* re-negotiation, or perhaps the SSL layer needs more
     106                 :                          * packets: retry in next iteration */
     107           10175 :                         errno = EAGAIN;
     108           10175 :                         retry = is_init ? 1 : sslsock->s.is_blocked;
     109           10175 :                         break;
     110                 :                 case SSL_ERROR_SYSCALL:
     111               0 :                         if (ERR_peek_error() == 0) {
     112               0 :                                 if (nr_bytes == 0) {
     113               0 :                                         if (!is_http_stream_talking_to_iis(stream TSRMLS_CC) && ERR_get_error() != 0) {
     114               0 :                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING,
     115                 :                                                                 "SSL: fatal protocol error");
     116                 :                                         }
     117               0 :                                         SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
     118               0 :                                         stream->eof = 1;
     119               0 :                                         retry = 0;
     120                 :                                 } else {
     121               0 :                                         char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
     122                 : 
     123               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
     124                 :                                                         "SSL: %s", estr);
     125                 : 
     126               0 :                                         efree(estr);
     127               0 :                                         retry = 0;
     128                 :                                 }
     129               0 :                                 break;
     130                 :                         }
     131                 : 
     132                 :                         
     133                 :                         /* fall through */
     134                 :                 default:
     135                 :                         /* some other error */
     136               0 :                         ecode = ERR_get_error();
     137                 : 
     138               0 :                         switch (ERR_GET_REASON(ecode)) {
     139                 :                                 case SSL_R_NO_SHARED_CIPHER:
     140               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)");
     141               0 :                                         retry = 0;
     142               0 :                                         break;
     143                 : 
     144                 :                                 default:
     145                 :                                         do {
     146                 :                                                 /* allow room for a NUL and an optional \n */
     147               0 :                                                 if (ebuf) {
     148               0 :                                                         esbuf[0] = '\n';
     149               0 :                                                         esbuf[1] = '\0';
     150               0 :                                                         ERR_error_string_n(ecode, esbuf + 1, sizeof(esbuf) - 2);
     151                 :                                                 } else {
     152               0 :                                                         esbuf[0] = '\0';
     153               0 :                                                         ERR_error_string_n(ecode, esbuf, sizeof(esbuf) - 1);
     154                 :                                                 }
     155               0 :                                                 code = strlen(esbuf);
     156                 : 
     157               0 :                                                 ebuf = erealloc(ebuf, ebuf_size + code + 1);
     158               0 :                                                 ebuf_size += code;
     159                 : 
     160               0 :                                                 if (wptr == NULL) {
     161               0 :                                                         wptr = ebuf;
     162                 :                                                 }       
     163                 : 
     164                 :                                                 /* also copies the NUL */
     165               0 :                                                 memcpy(wptr, esbuf, code + 1);
     166               0 :                                                 wptr += code;
     167               0 :                                         } while ((ecode = ERR_get_error()) != 0);
     168                 : 
     169               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
     170                 :                                                         "SSL operation failed with code %d. %s%s",
     171                 :                                                         err,
     172                 :                                                         ebuf ? "OpenSSL Error messages:\n" : "",
     173                 :                                                         ebuf ? ebuf : "");
     174               0 :                                         if (ebuf) {
     175               0 :                                                 efree(ebuf);
     176                 :                                         }
     177                 :                         }
     178                 :                                 
     179               0 :                         retry = 0;
     180               0 :                         errno = 0;
     181                 :         }
     182           10175 :         return retry;
     183                 : }
     184                 : 
     185                 : 
     186                 : static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
     187             238 : {
     188             238 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     189                 :         int didwrite;
     190                 :         
     191             238 :         if (sslsock->ssl_active) {
     192              12 :                 int retry = 1;
     193                 : 
     194                 :                 do {
     195              12 :                         didwrite = SSL_write(sslsock->ssl_handle, buf, count);
     196                 : 
     197              12 :                         if (didwrite <= 0) {
     198               0 :                                 retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC);
     199                 :                         } else {
     200              12 :                                 break;
     201                 :                         }
     202               0 :                 } while(retry);
     203                 : 
     204              12 :                 if (didwrite > 0) {
     205              12 :                         php_stream_notify_progress_increment(stream->context, didwrite, 0);
     206                 :                 }
     207                 :         } else {
     208             226 :                 didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
     209                 :         }
     210                 : 
     211             238 :         if (didwrite < 0) {
     212               0 :                 didwrite = 0;
     213                 :         }
     214                 :         
     215             238 :         return didwrite;
     216                 : }
     217                 : 
     218                 : static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
     219             211 : {
     220             211 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     221             211 :         int nr_bytes = 0;
     222                 : 
     223             211 :         if (sslsock->ssl_active) {
     224              10 :                 int retry = 1;
     225                 : 
     226                 :                 do {
     227              10 :                         nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
     228                 : 
     229              10 :                         if (nr_bytes <= 0) {
     230               0 :                                 retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
     231               0 :                                 stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
     232                 :                                 
     233                 :                         } else {
     234                 :                                 /* we got the data */
     235              10 :                                 break;
     236                 :                         }
     237               0 :                 } while (retry);
     238                 : 
     239              10 :                 if (nr_bytes > 0) {
     240              10 :                         php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
     241                 :                 }
     242                 :         }
     243                 :         else
     244                 :         {
     245             201 :                 nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
     246                 :         }
     247                 : 
     248             211 :         if (nr_bytes < 0) {
     249               0 :                 nr_bytes = 0;
     250                 :         }
     251                 : 
     252             211 :         return nr_bytes;
     253                 : }
     254                 : 
     255                 : 
     256                 : static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
     257             175 : {
     258             175 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     259                 : #ifdef PHP_WIN32
     260                 :         int n;
     261                 : #endif
     262             175 :         if (close_handle) {
     263             175 :                 if (sslsock->ssl_active) {
     264               5 :                         SSL_shutdown(sslsock->ssl_handle);
     265               5 :                         sslsock->ssl_active = 0;
     266                 :                 }
     267             175 :                 if (sslsock->ssl_handle) {
     268               5 :                         SSL_free(sslsock->ssl_handle);
     269               5 :                         sslsock->ssl_handle = NULL;
     270                 :                 }
     271             175 :                 if (sslsock->ctx) {
     272               5 :                         SSL_CTX_free(sslsock->ctx);
     273               5 :                         sslsock->ctx = NULL;
     274                 :                 }
     275                 : #ifdef PHP_WIN32
     276                 :                 if (sslsock->s.socket == -1)
     277                 :                         sslsock->s.socket = SOCK_ERR;
     278                 : #endif
     279             175 :                 if (sslsock->s.socket != SOCK_ERR) {
     280                 : #ifdef PHP_WIN32
     281                 :                         /* prevent more data from coming in */
     282                 :                         shutdown(sslsock->s.socket, SHUT_RD);
     283                 : 
     284                 :                         /* try to make sure that the OS sends all data before we close the connection.
     285                 :                          * Essentially, we are waiting for the socket to become writeable, which means
     286                 :                          * that all pending data has been sent.
     287                 :                          * We use a small timeout which should encourage the OS to send the data,
     288                 :                          * but at the same time avoid hanging indefintely.
     289                 :                          * */
     290                 :                         do {
     291                 :                                 n = php_pollfd_for_ms(sslsock->s.socket, POLLOUT, 500);
     292                 :                         } while (n == -1 && php_socket_errno() == EINTR);
     293                 : #endif
     294             165 :                         closesocket(sslsock->s.socket);
     295             165 :                         sslsock->s.socket = SOCK_ERR;
     296                 :                 }
     297                 :         }
     298                 : 
     299             175 :         pefree(sslsock, php_stream_is_persistent(stream));
     300                 :         
     301             175 :         return 0;
     302                 : }
     303                 : 
     304                 : static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
     305             175 : {
     306             175 :         return php_stream_socket_ops.flush(stream TSRMLS_CC);
     307                 : }
     308                 : 
     309                 : static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
     310               2 : {
     311               2 :         return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
     312                 : }
     313                 : 
     314                 : 
     315                 : static inline int php_openssl_setup_crypto(php_stream *stream,
     316                 :                 php_openssl_netstream_data_t *sslsock,
     317                 :                 php_stream_xport_crypto_param *cparam
     318                 :                 TSRMLS_DC)
     319               5 : {
     320                 :         SSL_METHOD *method;
     321                 :         
     322               5 :         if (sslsock->ssl_handle) {
     323               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
     324               0 :                 return -1;
     325                 :         }
     326                 : 
     327                 :         /* need to do slightly different things, based on client/server method,
     328                 :          * so lets remember which method was selected */
     329                 : 
     330               5 :         switch (cparam->inputs.method) {
     331                 :                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     332               2 :                         sslsock->is_client = 1;
     333               2 :                         method = SSLv23_client_method();
     334               2 :                         break;
     335                 :                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     336               0 :                         sslsock->is_client = 1;
     337               0 :                         method = SSLv2_client_method();
     338               0 :                         break;
     339                 :                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     340               0 :                         sslsock->is_client = 1;
     341               0 :                         method = SSLv3_client_method();
     342               0 :                         break;
     343                 :                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     344               0 :                         sslsock->is_client = 1;
     345               0 :                         method = TLSv1_client_method();
     346               0 :                         break;
     347                 :                 case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
     348               3 :                         sslsock->is_client = 0;
     349               3 :                         method = SSLv23_server_method();
     350               3 :                         break;
     351                 :                 case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
     352               0 :                         sslsock->is_client = 0;
     353               0 :                         method = SSLv3_server_method();
     354               0 :                         break;
     355                 :                 case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
     356               0 :                         sslsock->is_client = 0;
     357               0 :                         method = SSLv2_server_method();
     358               0 :                         break;
     359                 :                 case STREAM_CRYPTO_METHOD_TLS_SERVER:
     360               0 :                         sslsock->is_client = 0;
     361               0 :                         method = TLSv1_server_method();
     362               0 :                         break;
     363                 :                 default:
     364               0 :                         return -1;
     365                 : 
     366                 :         }
     367                 : 
     368               5 :         sslsock->ctx = SSL_CTX_new(method);
     369               5 :         if (sslsock->ctx == NULL) {
     370               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
     371               0 :                 return -1;
     372                 :         }
     373                 : 
     374               5 :         SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL);
     375                 : 
     376               5 :         sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
     377               5 :         if (sslsock->ssl_handle == NULL) {
     378               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
     379               0 :                 SSL_CTX_free(sslsock->ctx);
     380               0 :                 sslsock->ctx = NULL;
     381               0 :                 return -1;
     382                 :         }
     383                 : 
     384               5 :         if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
     385               0 :                 handle_ssl_error(stream, 0, 1 TSRMLS_CC);
     386                 :         }
     387                 : 
     388               5 :         if (cparam->inputs.session) {
     389               0 :                 if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
     390               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
     391                 :                 } else {
     392               0 :                         SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
     393                 :                 }
     394                 :         }
     395               5 :         return 0;
     396                 : }
     397                 : 
     398                 : static inline int php_openssl_enable_crypto(php_stream *stream,
     399                 :                 php_openssl_netstream_data_t *sslsock,
     400                 :                 php_stream_xport_crypto_param *cparam
     401                 :                 TSRMLS_DC)
     402               5 : {
     403               5 :         int n, retry = 1;
     404                 : 
     405               5 :         if (cparam->inputs.activate && !sslsock->ssl_active) {
     406               5 :                 float timeout = sslsock->connect_timeout.tv_sec + sslsock->connect_timeout.tv_usec / 1000000;
     407               5 :                 int blocked = sslsock->s.is_blocked;
     408                 : 
     409               5 :                 if (!sslsock->state_set) {
     410               5 :                         if (sslsock->is_client) {
     411               2 :                                 SSL_set_connect_state(sslsock->ssl_handle);
     412                 :                         } else {
     413               3 :                                 SSL_set_accept_state(sslsock->ssl_handle);
     414                 :                         }
     415               5 :                         sslsock->state_set = 1;
     416                 :                 }
     417                 :         
     418               5 :                 if (sslsock->is_client && SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
     419               2 :                         sslsock->s.is_blocked = 0;
     420                 :                 }
     421                 :                 do {
     422           10180 :                         if (sslsock->is_client) {
     423                 :                                 struct timeval tvs, tve;
     424                 :                                 struct timezone tz;
     425                 : 
     426           10177 :                                 gettimeofday(&tvs, &tz);
     427           10177 :                                 n = SSL_connect(sslsock->ssl_handle);
     428           10177 :                                 gettimeofday(&tve, &tz);
     429                 : 
     430           10177 :                                 timeout -= (tve.tv_sec + (float) tve.tv_usec / 1000000) - (tvs.tv_sec + (float) tvs.tv_usec / 1000000);
     431           10177 :                                 if (timeout < 0) {
     432               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: connection timeout");
     433               0 :                                         return -1;
     434                 :                                 }
     435                 :                         } else {
     436               3 :                                 n = SSL_accept(sslsock->ssl_handle);
     437                 :                         }
     438                 : 
     439           10180 :                         if (n <= 0) {
     440           10175 :                                 retry = handle_ssl_error(stream, n, 1 TSRMLS_CC);
     441                 :                         } else {
     442               5 :                                 break;
     443                 :                         }
     444           10175 :                 } while (retry);
     445                 : 
     446               5 :                 if (sslsock->is_client && sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
     447               2 :                         sslsock->s.is_blocked = blocked;
     448                 :                 }
     449                 : 
     450               5 :                 if (n == 1) {
     451                 :                         X509 *peer_cert;
     452                 : 
     453               5 :                         peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
     454                 : 
     455               5 :                         if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
     456               0 :                                 SSL_shutdown(sslsock->ssl_handle);
     457                 :                         } else {        
     458               5 :                                 sslsock->ssl_active = 1;
     459                 : 
     460                 :                                 /* allow the script to capture the peer cert
     461                 :                                  * and/or the certificate chain */
     462               5 :                                 if (stream->context) {
     463                 :                                         zval **val, *zcert;
     464                 : 
     465               4 :                                         if (SUCCESS == php_stream_context_get_option(
     466                 :                                                                 stream->context, "ssl",
     467                 :                                                                 "capture_peer_cert", &val) &&
     468                 :                                                         zval_is_true(*val)) {
     469               0 :                                                 MAKE_STD_ZVAL(zcert);
     470               0 :                                                 ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 
     471                 :                                                                         php_openssl_get_x509_list_id()));
     472               0 :                                                 php_stream_context_set_option(stream->context,
     473                 :                                                                 "ssl", "peer_certificate",
     474                 :                                                                 zcert);
     475               0 :                                                 peer_cert = NULL;
     476               0 :                                                 efree(zcert);
     477                 :                                         }
     478                 : 
     479               4 :                                         if (SUCCESS == php_stream_context_get_option(
     480                 :                                                                 stream->context, "ssl",
     481                 :                                                                 "capture_peer_cert_chain", &val) &&
     482                 :                                                         zval_is_true(*val)) {
     483                 :                                                 zval *arr;
     484                 :                                                 STACK_OF(X509) *chain;
     485                 : 
     486               0 :                                                 MAKE_STD_ZVAL(arr);
     487               0 :                                                 chain = SSL_get_peer_cert_chain(
     488                 :                                                                         sslsock->ssl_handle);
     489                 : 
     490               0 :                                                 if (chain && sk_X509_num(chain) > 0) {
     491                 :                                                         int i;
     492               0 :                                                         array_init(arr);
     493                 : 
     494               0 :                                                         for (i = 0; i < sk_X509_num(chain); i++) {
     495                 :                                                                 X509 *mycert = X509_dup(
     496               0 :                                                                                 sk_X509_value(chain, i));
     497               0 :                                                                 MAKE_STD_ZVAL(zcert);
     498               0 :                                                                 ZVAL_RESOURCE(zcert,
     499                 :                                                                                 zend_list_insert(mycert,
     500                 :                                                                                         php_openssl_get_x509_list_id()));
     501               0 :                                                                 add_next_index_zval(arr, zcert);
     502                 :                                                         }
     503               0 :                                                                 efree(zcert);
     504                 : 
     505                 :                                                 } else {
     506               0 :                                                         ZVAL_NULL(arr);
     507                 :                                                 }
     508                 : 
     509               0 :                                                 php_stream_context_set_option(stream->context,
     510                 :                                                                 "ssl", "peer_certificate_chain",
     511                 :                                                                 arr);
     512               0 :                                                 zval_dtor(arr);
     513               0 :                                                 efree(arr);
     514                 :                                         }
     515                 :                                 }
     516                 :                         }
     517                 : 
     518               5 :                         if (peer_cert) {
     519               2 :                                 X509_free(peer_cert);
     520                 :                         }
     521                 :                 } else  {
     522               0 :                         n = errno == EAGAIN ? 0 : -1;
     523                 :                 }
     524                 : 
     525               5 :                 return n;
     526                 : 
     527               0 :         } else if (!cparam->inputs.activate && sslsock->ssl_active) {
     528                 :                 /* deactivate - common for server/client */
     529               0 :                 SSL_shutdown(sslsock->ssl_handle);
     530               0 :                 sslsock->ssl_active = 0;
     531                 :         }
     532               0 :         return -1;
     533                 : }
     534                 : 
     535                 : static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
     536                 :                 php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
     537              40 : {
     538                 :         int clisock;
     539                 : 
     540              40 :         xparam->outputs.client = NULL;
     541                 : 
     542              40 :         clisock = php_network_accept_incoming(sock->s.socket,
     543                 :                         xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
     544                 :                         xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
     545                 :                         xparam->want_addr ? &xparam->outputs.addr : NULL,
     546                 :                         xparam->want_addr ? &xparam->outputs.addrlen : NULL,
     547                 :                         xparam->inputs.timeout,
     548                 :                         xparam->want_errortext ? &xparam->outputs.error_text : NULL,
     549                 :                         &xparam->outputs.error_code
     550                 :                         TSRMLS_CC);
     551                 : 
     552              40 :         if (clisock >= 0) {
     553                 :                 php_openssl_netstream_data_t *clisockdata;
     554                 : 
     555              40 :                 clisockdata = emalloc(sizeof(*clisockdata));
     556                 : 
     557              40 :                 if (clisockdata == NULL) {
     558               0 :                         closesocket(clisock);
     559                 :                         /* technically a fatal error */
     560                 :                 } else {
     561                 :                         /* copy underlying tcp fields */
     562              40 :                         memset(clisockdata, 0, sizeof(*clisockdata));
     563              40 :                         memcpy(clisockdata, sock, sizeof(clisockdata->s));
     564                 : 
     565              40 :                         clisockdata->s.socket = clisock;
     566                 :                         
     567              40 :                         xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
     568              40 :                         if (xparam->outputs.client) {
     569              40 :                                 xparam->outputs.client->context = stream->context;
     570              40 :                                 if (stream->context) {
     571              40 :                                         zend_list_addref(stream->context->rsrc_id);
     572                 :                                 }
     573                 :                         }
     574                 :                 }
     575                 : 
     576              40 :                 if (xparam->outputs.client && sock->enable_on_connect) {
     577                 :                         /* apply crypto */
     578               2 :                         switch (sock->method) {
     579                 :                                 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
     580               2 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
     581               2 :                                         break;
     582                 :                                 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
     583               0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
     584               0 :                                         break;
     585                 :                                 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
     586               0 :                                         sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
     587               0 :                                         break;
     588                 :                                 case STREAM_CRYPTO_METHOD_TLS_CLIENT:
     589               0 :                                         sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
     590                 :                                         break;
     591                 :                         }
     592                 : 
     593               2 :                         clisockdata->method = sock->method;
     594                 : 
     595               2 :                         if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
     596                 :                                         NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
     597                 :                                         xparam->outputs.client, 1 TSRMLS_CC) < 0) {
     598               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     599                 : 
     600               0 :                                 php_stream_close(xparam->outputs.client);
     601               0 :                                 xparam->outputs.client = NULL;
     602               0 :                                 xparam->outputs.returncode = -1;
     603                 :                         }
     604                 :                 }
     605                 :         }
     606                 :         
     607              40 :         return xparam->outputs.client == NULL ? -1 : 0;
     608                 : }
     609                 : static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
     610             245 : {
     611             245 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     612             245 :         php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
     613             245 :         php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
     614                 : 
     615             245 :         switch (option) {
     616                 :                 case PHP_STREAM_OPTION_CHECK_LIVENESS:
     617                 :                         {
     618                 :                                 struct timeval tv;
     619                 :                                 char buf;
     620               3 :                                 int alive = 1;
     621                 : 
     622               3 :                                 if (value == -1) {
     623               0 :                                         if (sslsock->s.timeout.tv_sec == -1) {
     624               0 :                                                 tv.tv_sec = FG(default_socket_timeout);
     625               0 :                                                 tv.tv_usec = 0;
     626                 :                                         } else {
     627               0 :                                                 tv = sslsock->connect_timeout;
     628                 :                                         }
     629                 :                                 } else {
     630               3 :                                         tv.tv_sec = value;
     631               3 :                                         tv.tv_usec = 0;
     632                 :                                 }
     633                 : 
     634               3 :                                 if (sslsock->s.socket == -1) {
     635               0 :                                         alive = 0;
     636               3 :                                 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
     637               3 :                                         if (sslsock->ssl_active) {
     638                 :                                                 int n;
     639                 : 
     640                 :                                                 do {
     641               0 :                                                         n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
     642               0 :                                                         if (n <= 0) {
     643               0 :                                                                 int err = SSL_get_error(sslsock->ssl_handle, n);
     644                 : 
     645               0 :                                                                 if (err == SSL_ERROR_SYSCALL) {
     646               0 :                                                                         alive = php_socket_errno() == EAGAIN;
     647               0 :                                                                         break;
     648                 :                                                                 }
     649                 : 
     650               0 :                                                                 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
     651                 :                                                                         /* re-negotiate */
     652                 :                                                                         continue;
     653                 :                                                                 }
     654                 : 
     655                 :                                                                 /* any other problem is a fatal error */
     656               0 :                                                                 alive = 0;
     657                 :                                                         }
     658                 :                                                         /* either peek succeeded or there was an error; we
     659                 :                                                          * have set the alive flag appropriately */
     660               0 :                                                         break;
     661               0 :                                                 } while (1);
     662               3 :                                         } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
     663               3 :                                                 alive = 0;
     664                 :                                         }
     665                 :                                 }
     666               3 :                                 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
     667                 :                         }
     668                 :                         
     669                 :                 case PHP_STREAM_OPTION_CRYPTO_API:
     670                 : 
     671              10 :                         switch(cparam->op) {
     672                 : 
     673                 :                                 case STREAM_XPORT_CRYPTO_OP_SETUP:
     674               5 :                                         cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
     675               5 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     676                 :                                         break;
     677                 :                                 case STREAM_XPORT_CRYPTO_OP_ENABLE:
     678               5 :                                         cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
     679               5 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     680                 :                                         break;
     681                 :                                 default:
     682                 :                                         /* fall through */
     683                 :                                         break;
     684                 :                         }
     685                 : 
     686               0 :                         break;
     687                 : 
     688                 :                 case PHP_STREAM_OPTION_XPORT_API:
     689             211 :                         switch(xparam->op) {
     690                 : 
     691                 :                                 case STREAM_XPORT_OP_CONNECT:
     692                 :                                 case STREAM_XPORT_OP_CONNECT_ASYNC:
     693                 :                                         /* TODO: Async connects need to check the enable_on_connect option when
     694                 :                                          * we notice that the connect has actually been established */
     695              45 :                                         php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     696                 : 
     697              45 :                                         if ((sslsock->enable_on_connect) &&
     698                 :                                                 ((xparam->outputs.returncode == 0) ||
     699                 :                                                 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 
     700                 :                                                 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
     701                 :                                         {
     702               2 :                                                 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
     703                 :                                                                 php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
     704               0 :                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
     705               0 :                                                         xparam->outputs.returncode = -1;
     706                 :                                                 }
     707                 :                                         }
     708              45 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     709                 : 
     710                 :                                 case STREAM_XPORT_OP_ACCEPT:
     711                 :                                         /* we need to copy the additional fields that the underlying tcp transport
     712                 :                                          * doesn't know about */
     713              40 :                                         xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
     714                 : 
     715                 :                                         
     716              40 :                                         return PHP_STREAM_OPTION_RETURN_OK;
     717                 : 
     718                 :                                 default:
     719                 :                                         /* fall through */
     720                 :                                         break;
     721                 :                         }
     722                 :         }
     723                 : 
     724             147 :         return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
     725                 : }
     726                 : 
     727                 : static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
     728               6 : {
     729               6 :         php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
     730                 : 
     731               6 :         switch(castas)  {
     732                 :                 case PHP_STREAM_AS_STDIO:
     733               0 :                         if (sslsock->ssl_active) {
     734               0 :                                 return FAILURE;
     735                 :                         }
     736               0 :                         if (ret)        {
     737               0 :                                 *ret = fdopen(sslsock->s.socket, stream->mode);
     738               0 :                                 if (*ret) {
     739               0 :                                         return SUCCESS;
     740                 :                                 }
     741               0 :                                 return FAILURE;
     742                 :                         }
     743               0 :                         return SUCCESS;
     744                 : 
     745                 :                 case PHP_STREAM_AS_FD_FOR_SELECT:
     746               6 :                         if (ret) {
     747               6 :                                 *(int *)ret = sslsock->s.socket;
     748                 :                         }
     749               6 :                         return SUCCESS;
     750                 : 
     751                 :                 case PHP_STREAM_AS_FD:
     752                 :                 case PHP_STREAM_AS_SOCKETD:
     753               0 :                         if (sslsock->ssl_active) {
     754               0 :                                 return FAILURE;
     755                 :                         }
     756               0 :                         if (ret) {
     757               0 :                                 *(int *)ret = sslsock->s.socket;
     758                 :                         }
     759               0 :                         return SUCCESS;
     760                 :                 default:
     761               0 :                         return FAILURE;
     762                 :         }
     763                 : }
     764                 : 
     765                 : php_stream_ops php_openssl_socket_ops = {
     766                 :         php_openssl_sockop_write, php_openssl_sockop_read,
     767                 :         php_openssl_sockop_close, php_openssl_sockop_flush,
     768                 :         "tcp_socket/ssl",
     769                 :         NULL, /* seek */
     770                 :         php_openssl_sockop_cast,
     771                 :         php_openssl_sockop_stat,
     772                 :         php_openssl_sockop_set_option,
     773                 : };
     774                 : 
     775                 : 
     776                 : php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
     777                 :                 char *resourcename, long resourcenamelen,
     778                 :                 const char *persistent_id, int options, int flags,
     779                 :                 struct timeval *timeout,
     780                 :                 php_stream_context *context STREAMS_DC TSRMLS_DC)
     781             107 : {
     782             107 :         php_stream *stream = NULL;
     783             107 :         php_openssl_netstream_data_t *sslsock = NULL;
     784                 :         
     785             107 :         sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
     786             107 :         memset(sslsock, 0, sizeof(*sslsock));
     787                 : 
     788             107 :         sslsock->s.is_blocked = 1;
     789                 :         /* this timeout is used by standard stream funcs, therefor it should use the default value */
     790             107 :         sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
     791             107 :         sslsock->s.timeout.tv_usec = 0;
     792                 : 
     793                 :         /* use separate timeout for our private funcs */
     794             107 :         sslsock->connect_timeout.tv_sec = timeout->tv_sec;
     795             107 :         sslsock->connect_timeout.tv_usec = timeout->tv_usec;
     796                 : 
     797                 :         /* we don't know the socket until we have determined if we are binding or
     798                 :          * connecting */
     799             107 :         sslsock->s.socket = -1;
     800                 :         
     801                 :         /* Initialize context as NULL */
     802             107 :         sslsock->ctx = NULL; 
     803                 :         
     804             107 :         stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
     805                 : 
     806             107 :         if (stream == NULL)     {
     807               0 :                 pefree(sslsock, persistent_id ? 1 : 0);
     808               0 :                 return NULL;
     809                 :         }
     810                 : 
     811             107 :         if (strncmp(proto, "ssl", protolen) == 0) {
     812               4 :                 sslsock->enable_on_connect = 1;
     813               4 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
     814             103 :         } else if (strncmp(proto, "sslv2", protolen) == 0) {
     815               0 :                 sslsock->enable_on_connect = 1;
     816               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
     817             103 :         } else if (strncmp(proto, "sslv3", protolen) == 0) {
     818               0 :                 sslsock->enable_on_connect = 1;
     819               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
     820             103 :         } else if (strncmp(proto, "tls", protolen) == 0) {
     821               0 :                 sslsock->enable_on_connect = 1;
     822               0 :                 sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
     823                 :         }
     824                 : 
     825             107 :         return stream;
     826                 : }
     827                 : 
     828                 : 
     829                 : 
     830                 : /*
     831                 :  * Local variables:
     832                 :  * tab-width: 4
     833                 :  * c-basic-offset: 4
     834                 :  * End:
     835                 :  * vim600: noet sw=4 ts=4 fdm=marker
     836                 :  * vim<600: noet sw=4 ts=4
     837                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Thu, 19 Nov 2009 08:20:13 +0000 (5 days ago)

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