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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:04 +0000 (3 days ago)

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