2003-02-27 17:43:38 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2004-01-08 08:18:22 +00:00
| PHP Version 5 |
2003-02-27 17:43:38 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2015-01-15 15:26:37 +00:00
| Copyright ( c ) 1997 - 2015 The PHP Group |
2003-02-27 17:43:38 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2006-01-01 12:51:34 +00:00
| This source file is subject to version 3.01 of the PHP license , |
2003-02-27 17:43:38 +00:00
| that is bundled with this package in the file LICENSE , and is |
2003-06-10 20:04:29 +00:00
| available through the world - wide - web at the following url : |
2006-01-01 12:51:34 +00:00
| http : //www.php.net/license/3_01.txt |
2003-02-27 17:43:38 +00:00
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ php . net so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2014-03-04 01:57:33 +00:00
| Authors : Wez Furlong < wez @ thebrainroom . com > |
| Daniel Lowrey < rdlowrey @ php . net > |
| Chris Wright < daverandom @ php . net > |
2003-02-27 17:43:38 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
/* $Id$ */
2014-04-13 21:17:50 +00:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2003-02-27 17:43:38 +00:00
# include "php.h"
# include "ext/standard/file.h"
2009-11-30 13:31:53 +00:00
# include "ext/standard/url.h"
2003-02-27 17:43:38 +00:00
# include "streams/php_streams_int.h"
2008-12-08 11:54:22 +00:00
# include "ext/standard/php_smart_str.h"
2003-02-27 17:43:38 +00:00
# include "php_openssl.h"
2014-03-04 01:57:33 +00:00
# include "php_network.h"
2003-02-28 07:25:15 +00:00
# include <openssl/ssl.h>
2003-11-27 17:40:16 +00:00
# include <openssl/x509.h>
2014-03-04 01:57:33 +00:00
# include <openssl/x509v3.h>
2003-02-27 17:43:38 +00:00
# include <openssl/err.h>
2006-12-06 13:18:36 +00:00
# ifdef PHP_WIN32
2014-03-04 01:57:33 +00:00
# include "win32/winutil.h"
2006-12-06 13:18:36 +00:00
# include "win32/time.h"
2014-03-04 01:57:33 +00:00
# include <Wincrypt.h>
/* These are from Wincrypt.h, they conflict with OpenSSL */
# undef X509_NAME
# undef X509_CERT_PAIR
# undef X509_EXTENSIONS
2006-12-06 13:18:36 +00:00
# endif
2004-09-29 10:28:17 +00:00
# ifdef NETWARE
# include <sys/select.h>
# endif
2014-02-11 15:42:34 +00:00
# if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x0090800fL
2014-03-04 01:57:33 +00:00
# define HAVE_ECDH 1
# endif
# if OPENSSL_VERSION_NUMBER >= 0x00908070L && !defined(OPENSSL_NO_TLSEXT)
# define HAVE_SNI 1
2014-02-11 15:42:34 +00:00
# endif
2014-02-11 15:12:01 +00:00
/* Flags for determining allowed stream crypto methods */
# define STREAM_CRYPTO_IS_CLIENT (1<<0)
# define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
# define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
# define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
# define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
# define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
2014-03-04 01:57:33 +00:00
/* Simplify ssl context option retrieval */
# define GET_VER_OPT(name) (stream->context && SUCCESS == php_stream_context_get_option(stream->context, "ssl", name, &val))
# define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_PP(val); }
# define GET_VER_OPT_LONG(name, num) if (GET_VER_OPT(name)) { convert_to_long_ex(val); num = Z_LVAL_PP(val); }
/* Used for peer verification in windows */
# define PHP_X509_NAME_ENTRY_TO_UTF8(ne, i, out) ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(ne, i)))
2014-02-21 22:09:16 +00:00
extern php_stream * php_openssl_get_stream_from_ssl_handle ( const SSL * ssl ) ;
2014-07-31 04:17:17 +00:00
extern int php_openssl_x509_fingerprint ( X509 * peer , const char * method , zend_bool raw , char * * out , int * out_len TSRMLS_DC ) ;
2014-03-04 01:57:33 +00:00
extern int php_openssl_get_ssl_stream_data_index ( ) ;
extern int php_openssl_get_x509_list_id ( void ) ;
2015-01-28 05:04:20 +00:00
static struct timeval subtract_timeval ( struct timeval a , struct timeval b ) ;
static int compare_timeval ( struct timeval a , struct timeval b ) ;
static size_t php_openssl_sockop_io ( int read , php_stream * stream , char * buf , size_t count TSRMLS_DC ) ;
2003-02-27 17:43:38 +00:00
php_stream_ops php_openssl_socket_ops ;
2014-03-05 15:50:23 +00:00
/* Certificate contexts used for server-side SNI selection */
typedef struct _php_openssl_sni_cert_t {
char * name ;
SSL_CTX * ctx ;
} php_openssl_sni_cert_t ;
2014-03-04 01:57:33 +00:00
/* Provides leaky bucket handhsake renegotiation rate-limiting */
typedef struct _php_openssl_handshake_bucket_t {
long prev_handshake ;
long limit ;
long window ;
float tokens ;
unsigned should_close ;
} php_openssl_handshake_bucket_t ;
/* This implementation is very closely tied to the that of the native
* sockets implemented in the core .
* Don ' t try this technique in other extensions !
* */
typedef struct _php_openssl_netstream_data_t {
php_netstream_data_t s ;
SSL * ssl_handle ;
SSL_CTX * ctx ;
struct timeval connect_timeout ;
int enable_on_connect ;
int is_client ;
int ssl_active ;
php_stream_xport_crypt_method_t method ;
php_openssl_handshake_bucket_t * reneg ;
php_openssl_sni_cert_t * sni_certs ;
unsigned sni_cert_count ;
char * url_name ;
unsigned state_set : 1 ;
unsigned _spare : 31 ;
} php_openssl_netstream_data_t ;
2004-05-23 10:36:08 +00:00
/* it doesn't matter that we do some hash traversal here, since it is done only
* in an error condition arising from a network connection problem */
2014-03-04 01:57:33 +00:00
static int is_http_stream_talking_to_iis ( php_stream * stream TSRMLS_DC ) /* { { { */
2004-05-23 10:36:08 +00:00
{
2006-10-05 00:38:02 +00:00
if ( stream - > wrapperdata & & stream - > wrapper & & strcasecmp ( stream - > wrapper - > wops - > label , " HTTP " ) = = 0 ) {
2004-05-23 10:36:08 +00:00
/* the wrapperdata is an array zval containing the headers */
zval * * tmp ;
# define SERVER_MICROSOFT_IIS "Server: Microsoft-IIS"
2006-10-05 00:38:02 +00:00
# define SERVER_GOOGLE "Server: GFE / "
2004-05-23 10:36:08 +00:00
zend_hash_internal_pointer_reset ( Z_ARRVAL_P ( stream - > wrapperdata ) ) ;
while ( SUCCESS = = zend_hash_get_current_data ( Z_ARRVAL_P ( stream - > wrapperdata ) , ( void * * ) & tmp ) ) {
if ( strncasecmp ( Z_STRVAL_PP ( tmp ) , SERVER_MICROSOFT_IIS , sizeof ( SERVER_MICROSOFT_IIS ) - 1 ) = = 0 ) {
return 1 ;
2006-10-05 00:38:02 +00:00
} else if ( strncasecmp ( Z_STRVAL_PP ( tmp ) , SERVER_GOOGLE , sizeof ( SERVER_GOOGLE ) - 1 ) = = 0 ) {
return 1 ;
2004-05-23 10:36:08 +00:00
}
zend_hash_move_forward ( Z_ARRVAL_P ( stream - > wrapperdata ) ) ;
}
}
return 0 ;
}
2014-03-04 01:57:33 +00:00
/* }}} */
2004-05-23 10:36:08 +00:00
2014-03-04 01:57:33 +00:00
static int handle_ssl_error ( php_stream * stream , int nr_bytes , zend_bool is_init TSRMLS_DC ) /* { { { */
2003-02-27 17:43:38 +00:00
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
int err = SSL_get_error ( sslsock - > ssl_handle , nr_bytes ) ;
char esbuf [ 512 ] ;
2008-12-08 11:54:22 +00:00
smart_str ebuf = { 0 } ;
unsigned long ecode ;
2003-02-27 17:43:38 +00:00
int retry = 1 ;
switch ( err ) {
case SSL_ERROR_ZERO_RETURN :
/* SSL terminated (but socket may still be active) */
retry = 0 ;
break ;
case SSL_ERROR_WANT_READ :
case SSL_ERROR_WANT_WRITE :
/* re-negotiation, or perhaps the SSL layer needs more
* packets : retry in next iteration */
2004-12-25 02:02:56 +00:00
errno = EAGAIN ;
2007-05-27 17:05:51 +00:00
retry = is_init ? 1 : sslsock - > s . is_blocked ;
2003-02-27 17:43:38 +00:00
break ;
case SSL_ERROR_SYSCALL :
if ( ERR_peek_error ( ) = = 0 ) {
if ( nr_bytes = = 0 ) {
2007-07-02 16:42:10 +00:00
if ( ! is_http_stream_talking_to_iis ( stream TSRMLS_CC ) & & ERR_get_error ( ) ! = 0 ) {
2004-05-23 10:36:08 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSL: fatal protocol error " ) ;
}
SSL_set_shutdown ( sslsock - > ssl_handle , SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN ) ;
2003-02-27 17:43:38 +00:00
stream - > eof = 1 ;
retry = 0 ;
} else {
char * estr = php_socket_strerror ( php_socket_errno ( ) , NULL , 0 ) ;
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSL: %s " , estr ) ;
efree ( estr ) ;
retry = 0 ;
}
break ;
}
2004-04-21 23:02:06 +00:00
2003-02-27 17:43:38 +00:00
/* fall through */
default :
/* some other error */
2004-04-21 23:02:06 +00:00
ecode = ERR_get_error ( ) ;
2003-02-27 17:43:38 +00:00
2004-04-21 23:02:06 +00:00
switch ( ERR_GET_REASON ( ecode ) ) {
case SSL_R_NO_SHARED_CIPHER :
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) " ) ;
retry = 0 ;
break ;
2003-02-27 17:43:38 +00:00
2004-04-21 23:02:06 +00:00
default :
do {
2011-06-10 22:48:36 +00:00
/* NULL is automatically added */
2008-12-08 11:54:22 +00:00
ERR_error_string_n ( ecode , esbuf , sizeof ( esbuf ) ) ;
if ( ebuf . c ) {
smart_str_appendc ( & ebuf , ' \n ' ) ;
2004-04-21 23:02:06 +00:00
}
2008-12-08 11:54:22 +00:00
smart_str_appends ( & ebuf , esbuf ) ;
2004-04-21 23:02:06 +00:00
} while ( ( ecode = ERR_get_error ( ) ) ! = 0 ) ;
2003-02-27 17:43:38 +00:00
2008-12-08 11:54:22 +00:00
smart_str_0 ( & ebuf ) ;
2004-04-21 23:02:06 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSL operation failed with code %d. %s%s " ,
err ,
2008-12-08 11:54:22 +00:00
ebuf . c ? " OpenSSL Error messages: \n " : " " ,
ebuf . c ? ebuf . c : " " ) ;
if ( ebuf . c ) {
smart_str_free ( & ebuf ) ;
2007-04-04 10:44:55 +00:00
}
2004-04-21 23:02:06 +00:00
}
2003-02-27 17:43:38 +00:00
retry = 0 ;
2004-12-25 02:02:56 +00:00
errno = 0 ;
2003-02-27 17:43:38 +00:00
}
return retry ;
}
2014-03-04 01:57:33 +00:00
/* }}} */
static int verify_callback ( int preverify_ok , X509_STORE_CTX * ctx ) /* { { { */
{
php_stream * stream ;
SSL * ssl ;
int err , depth , ret ;
zval * * val ;
unsigned long allowed_depth = OPENSSL_DEFAULT_STREAM_VERIFY_DEPTH ;
ret = preverify_ok ;
/* determine the status for the current cert */
err = X509_STORE_CTX_get_error ( ctx ) ;
depth = X509_STORE_CTX_get_error_depth ( ctx ) ;
/* conjure the stream & context to use */
ssl = X509_STORE_CTX_get_ex_data ( ctx , SSL_get_ex_data_X509_STORE_CTX_idx ( ) ) ;
stream = ( php_stream * ) SSL_get_ex_data ( ssl , php_openssl_get_ssl_stream_data_index ( ) ) ;
/* if allow_self_signed is set, make sure that verification succeeds */
if ( err = = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT & &
GET_VER_OPT ( " allow_self_signed " ) & &
zend_is_true ( * val )
) {
ret = 1 ;
}
/* check the depth */
GET_VER_OPT_LONG ( " verify_depth " , allowed_depth ) ;
if ( ( unsigned long ) depth > allowed_depth ) {
ret = 0 ;
X509_STORE_CTX_set_error ( ctx , X509_V_ERR_CERT_CHAIN_TOO_LONG ) ;
}
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
return ret ;
}
/* }}} */
2003-02-27 17:43:38 +00:00
2014-07-31 04:17:17 +00:00
static int php_x509_fingerprint_cmp ( X509 * peer , const char * method , const char * expected TSRMLS_DC )
{
char * fingerprint ;
int fingerprint_len ;
int result = - 1 ;
if ( php_openssl_x509_fingerprint ( peer , method , 0 , & fingerprint , & fingerprint_len TSRMLS_CC ) = = SUCCESS ) {
2015-03-05 00:03:45 +00:00
result = strcasecmp ( expected , fingerprint ) ;
2014-07-31 04:17:17 +00:00
efree ( fingerprint ) ;
}
return result ;
}
static zend_bool php_x509_fingerprint_match ( X509 * peer , zval * val TSRMLS_DC )
{
if ( Z_TYPE_P ( val ) = = IS_STRING ) {
const char * method = NULL ;
switch ( Z_STRLEN_P ( val ) ) {
case 32 :
method = " md5 " ;
break ;
case 40 :
method = " sha1 " ;
break ;
}
return method & & php_x509_fingerprint_cmp ( peer , method , Z_STRVAL_P ( val ) TSRMLS_CC ) = = 0 ;
2015-03-04 17:54:34 +00:00
2014-07-31 04:17:17 +00:00
} else if ( Z_TYPE_P ( val ) = = IS_ARRAY ) {
HashPosition pos ;
zval * * current ;
char * key ;
uint key_len ;
ulong key_index ;
2015-03-04 17:54:34 +00:00
if ( ! zend_hash_num_elements ( Z_ARRVAL_P ( val ) ) ) {
2015-03-05 21:08:46 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Invalid peer_fingerprint array; [algo => fingerprint] form required " ) ;
2015-03-04 17:54:34 +00:00
return 0 ;
}
2014-07-31 04:17:17 +00:00
for ( zend_hash_internal_pointer_reset_ex ( Z_ARRVAL_P ( val ) , & pos ) ;
zend_hash_get_current_data_ex ( Z_ARRVAL_P ( val ) , ( void * * ) & current , & pos ) = = SUCCESS ;
zend_hash_move_forward_ex ( Z_ARRVAL_P ( val ) , & pos )
) {
int key_type = zend_hash_get_current_key_ex ( Z_ARRVAL_P ( val ) , & key , & key_len , & key_index , 0 , & pos ) ;
2015-03-04 17:54:34 +00:00
if ( ! ( key_type = = HASH_KEY_IS_STRING & & Z_TYPE_PP ( current ) = = IS_STRING ) ) {
2015-03-05 21:08:46 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Invalid peer_fingerprint array; [algo => fingerprint] form required " ) ;
2015-03-04 17:54:34 +00:00
return 0 ;
}
if ( php_x509_fingerprint_cmp ( peer , key , Z_STRVAL_PP ( current ) TSRMLS_CC ) ! = 0 ) {
2014-07-31 04:17:17 +00:00
return 0 ;
}
}
2015-03-04 17:54:34 +00:00
2014-07-31 04:17:17 +00:00
return 1 ;
2015-03-04 17:54:34 +00:00
} else {
2015-03-05 21:08:46 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Invalid peer_fingerprint value; fingerprint string or array of the form [algo => fingerprint] required " ) ;
2014-07-31 04:17:17 +00:00
}
2015-03-04 17:54:34 +00:00
2014-07-31 04:17:17 +00:00
return 0 ;
}
2014-03-04 01:57:33 +00:00
static zend_bool matches_wildcard_name ( const char * subjectname , const char * certname ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2014-03-04 01:57:33 +00:00
char * wildcard = NULL ;
int prefix_len , suffix_len , subject_len ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
if ( strcasecmp ( subjectname , certname ) = = 0 ) {
return 1 ;
}
2003-02-27 17:43:38 +00:00
2014-07-29 11:15:01 +00:00
/* wildcard, if present, must only be present in the left-most component */
if ( ! ( wildcard = strchr ( certname , ' * ' ) ) | | memchr ( certname , ' . ' , wildcard - certname ) ) {
2014-03-04 01:57:33 +00:00
return 0 ;
}
2008-04-14 12:16:07 +00:00
2014-07-29 11:15:01 +00:00
/* 1) prefix, if not empty, must match subject */
2014-03-04 01:57:33 +00:00
prefix_len = wildcard - certname ;
if ( prefix_len & & strncasecmp ( subjectname , certname , prefix_len ) ! = 0 ) {
return 0 ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
suffix_len = strlen ( wildcard + 1 ) ;
subject_len = strlen ( subjectname ) ;
if ( suffix_len < = subject_len ) {
/* 2) suffix must match
* 3 ) no . between prefix and suffix
* */
return strcasecmp ( wildcard + 1 , subjectname + subject_len - suffix_len ) = = 0 & &
memchr ( subjectname + prefix_len , ' . ' , subject_len - suffix_len - prefix_len ) = = NULL ;
2003-03-18 15:15:10 +00:00
}
2014-03-04 01:57:33 +00:00
return 0 ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2015-03-05 05:39:25 +00:00
static zend_bool matches_san_list ( X509 * peer , const char * subject_name ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2015-03-05 06:55:42 +00:00
int i , len ;
2014-03-04 01:57:33 +00:00
unsigned char * cert_name = NULL ;
2015-03-05 05:39:25 +00:00
char ipbuffer [ 64 ] ;
2014-03-04 01:57:33 +00:00
GENERAL_NAMES * alt_names = X509_get_ext_d2i ( peer , NID_subject_alt_name , 0 , 0 ) ;
int alt_name_count = sk_GENERAL_NAME_num ( alt_names ) ;
for ( i = 0 ; i < alt_name_count ; i + + ) {
GENERAL_NAME * san = sk_GENERAL_NAME_value ( alt_names , i ) ;
2003-02-27 17:43:38 +00:00
2015-03-05 05:39:25 +00:00
if ( san - > type = = GEN_DNS ) {
ASN1_STRING_to_UTF8 ( & cert_name , san - > d . dNSName ) ;
if ( ASN1_STRING_length ( san - > d . dNSName ) ! = strlen ( ( const char * ) cert_name ) ) {
OPENSSL_free ( cert_name ) ;
/* prevent null-byte poisoning*/
continue ;
}
2008-04-14 12:16:07 +00:00
2015-03-05 06:55:42 +00:00
/* accommodate valid FQDN entries ending in "." */
len = strlen ( ( const char * ) cert_name ) ;
if ( len & & strcmp ( ( const char * ) & cert_name [ len - 1 ] , " . " ) = = 0 ) {
cert_name [ len - 1 ] = ' \0 ' ;
}
2015-03-05 05:39:25 +00:00
if ( matches_wildcard_name ( subject_name , ( const char * ) cert_name ) ) {
OPENSSL_free ( cert_name ) ;
return 1 ;
}
OPENSSL_free ( cert_name ) ;
} else if ( san - > type = = GEN_IPADD ) {
if ( san - > d . iPAddress - > length = = 4 ) {
sprintf ( ipbuffer , " %d.%d.%d.%d " ,
san - > d . iPAddress - > data [ 0 ] ,
san - > d . iPAddress - > data [ 1 ] ,
san - > d . iPAddress - > data [ 2 ] ,
san - > d . iPAddress - > data [ 3 ]
) ;
if ( strcasecmp ( subject_name , ( const char * ) ipbuffer ) = = 0 ) {
return 1 ;
}
}
/* No, we aren't bothering to check IPv6 addresses. Why?
* Because IP SAN names are officially deprecated and are
* not allowed by CAs starting in 2015. Deal with it .
*/
2008-04-14 12:16:07 +00:00
}
2003-02-27 17:43:38 +00:00
}
2015-03-05 05:39:25 +00:00
return 0 ;
2014-03-04 01:57:33 +00:00
}
/* }}} */
static zend_bool matches_common_name ( X509 * peer , const char * subject_name TSRMLS_DC ) /* { { { */
{
char buf [ 1024 ] ;
X509_NAME * cert_name ;
zend_bool is_match = 0 ;
int cert_name_len ;
cert_name = X509_get_subject_name ( peer ) ;
cert_name_len = X509_NAME_get_text_by_NID ( cert_name , NID_commonName , buf , sizeof ( buf ) ) ;
if ( cert_name_len = = - 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to locate peer certificate CN " ) ;
} else if ( cert_name_len ! = strlen ( buf ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Peer certificate CN=`%.*s' is malformed " , cert_name_len , buf ) ;
} else if ( matches_wildcard_name ( subject_name , buf ) ) {
is_match = 1 ;
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Peer certificate CN=`%.*s' did not match expected CN=`%s' " , cert_name_len , buf , subject_name ) ;
2003-03-18 15:15:10 +00:00
}
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
return is_match ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
static int apply_peer_verification_policy ( SSL * ssl , X509 * peer , php_stream * stream TSRMLS_DC ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2014-03-04 01:57:33 +00:00
zval * * val = NULL ;
char * peer_name = NULL ;
int err ,
must_verify_peer ,
must_verify_peer_name ,
must_verify_fingerprint ,
has_cnmatch_ctx_opt ;
2003-02-27 17:43:38 +00:00
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
must_verify_peer = GET_VER_OPT ( " verify_peer " )
? zend_is_true ( * val )
: sslsock - > is_client ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
has_cnmatch_ctx_opt = GET_VER_OPT ( " CN_match " ) ;
must_verify_peer_name = ( has_cnmatch_ctx_opt | | GET_VER_OPT ( " verify_peer_name " ) )
? zend_is_true ( * val )
: sslsock - > is_client ;
2015-03-04 17:54:34 +00:00
must_verify_fingerprint = GET_VER_OPT ( " peer_fingerprint " ) ;
2014-03-04 01:57:33 +00:00
if ( ( must_verify_peer | | must_verify_peer_name | | must_verify_fingerprint ) & & peer = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Could not get peer certificate " ) ;
return FAILURE ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
/* Verify the peer against using CA file/path settings */
if ( must_verify_peer ) {
err = SSL_get_verify_result ( ssl ) ;
switch ( err ) {
case X509_V_OK :
/* fine */
break ;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT :
if ( GET_VER_OPT ( " allow_self_signed " ) & & zend_is_true ( * val ) ) {
/* allowed */
break ;
}
/* not allowed, so fall through */
default :
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Could not verify peer: code:%d %s " ,
err ,
X509_verify_cert_error_string ( err )
) ;
return FAILURE ;
}
2009-11-30 13:31:53 +00:00
}
2014-01-28 17:05:56 +00:00
2014-03-04 01:57:33 +00:00
/* If a peer_fingerprint match is required this trumps peer and peer_name verification */
if ( must_verify_fingerprint ) {
if ( Z_TYPE_PP ( val ) = = IS_STRING | | Z_TYPE_PP ( val ) = = IS_ARRAY ) {
if ( ! php_x509_fingerprint_match ( peer , * val TSRMLS_CC ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
2015-03-04 17:54:34 +00:00
" peer_fingerprint match failure "
2014-03-04 01:57:33 +00:00
) ;
return FAILURE ;
}
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Expected peer fingerprint must be a string or an array "
) ;
2015-03-04 17:54:34 +00:00
return FAILURE ;
2014-03-04 01:57:33 +00:00
}
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
/* verify the host name presented in the peer certificate */
if ( must_verify_peer_name ) {
GET_VER_OPT_STRING ( " peer_name " , peer_name ) ;
2014-01-28 17:05:56 +00:00
2014-03-04 01:57:33 +00:00
if ( has_cnmatch_ctx_opt ) {
GET_VER_OPT_STRING ( " CN_match " , peer_name ) ;
php_error ( E_DEPRECATED ,
" the 'CN_match' SSL context option is deprecated in favor of 'peer_name' "
) ;
}
/* If no peer name was specified we use the autodetected url name in client environments */
if ( peer_name = = NULL & & sslsock - > is_client ) {
peer_name = sslsock - > url_name ;
}
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
if ( peer_name ) {
2015-03-05 05:39:25 +00:00
if ( matches_san_list ( peer , peer_name ) ) {
2014-03-04 01:57:33 +00:00
return SUCCESS ;
} else if ( matches_common_name ( peer , peer_name TSRMLS_CC ) ) {
return SUCCESS ;
} else {
return FAILURE ;
}
} else {
return FAILURE ;
}
}
return SUCCESS ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
static int passwd_callback ( char * buf , int num , int verify , void * data ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2014-03-04 01:57:33 +00:00
php_stream * stream = ( php_stream * ) data ;
zval * * val = NULL ;
char * passphrase = NULL ;
/* TODO: could expand this to make a callback into PHP user-space */
GET_VER_OPT_STRING ( " passphrase " , passphrase ) ;
if ( passphrase ) {
if ( Z_STRLEN_PP ( val ) < num - 1 ) {
memcpy ( buf , Z_STRVAL_PP ( val ) , Z_STRLEN_PP ( val ) + 1 ) ;
return Z_STRLEN_PP ( val ) ;
}
}
return 0 ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
# if defined(PHP_WIN32) && OPENSSL_VERSION_NUMBER >= 0x00907000L
# define RETURN_CERT_VERIFY_FAILURE(code) X509_STORE_CTX_set_error(x509_store_ctx, code); return 0;
static int win_cert_verify_callback ( X509_STORE_CTX * x509_store_ctx , void * arg ) /* { { { */
2014-02-20 23:26:55 +00:00
{
2014-03-04 01:57:33 +00:00
PCCERT_CONTEXT cert_ctx = NULL ;
PCCERT_CHAIN_CONTEXT cert_chain_ctx = NULL ;
2014-02-20 23:26:55 +00:00
php_stream * stream ;
php_openssl_netstream_data_t * sslsock ;
2014-03-04 01:57:33 +00:00
zval * * val ;
zend_bool is_self_signed = 0 ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
TSRMLS_FETCH ( ) ;
stream = ( php_stream * ) arg ;
2014-02-20 23:26:55 +00:00
sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
2014-03-04 01:57:33 +00:00
{ /* First convert the x509 struct back to a DER encoded buffer and let Windows decode it into a form it can work with */
unsigned char * der_buf = NULL ;
int der_len ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
der_len = i2d_X509 ( x509_store_ctx - > cert , & der_buf ) ;
if ( der_len < 0 ) {
unsigned long err_code , e ;
char err_buf [ 512 ] ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
while ( ( e = ERR_get_error ( ) ) ! = 0 ) {
err_code = e ;
}
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Error encoding X509 certificate: %d: %s " , err_code , ERR_error_string ( err_code , err_buf ) ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
}
cert_ctx = CertCreateCertificateContext ( X509_ASN_ENCODING , der_buf , der_len ) ;
OPENSSL_free ( der_buf ) ;
if ( cert_ctx = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Error creating certificate context: %s " , php_win_err ( ) ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
}
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
{ /* Next fetch the relevant cert chain from the store */
CERT_ENHKEY_USAGE enhkey_usage = { 0 } ;
CERT_USAGE_MATCH cert_usage = { 0 } ;
CERT_CHAIN_PARA chain_params = { sizeof ( CERT_CHAIN_PARA ) } ;
LPSTR usages [ ] = { szOID_PKIX_KP_SERVER_AUTH , szOID_SERVER_GATED_CRYPTO , szOID_SGC_NETSCAPE } ;
DWORD chain_flags = 0 ;
unsigned long allowed_depth = OPENSSL_DEFAULT_STREAM_VERIFY_DEPTH ;
unsigned int i ;
enhkey_usage . cUsageIdentifier = 3 ;
enhkey_usage . rgpszUsageIdentifier = usages ;
cert_usage . dwType = USAGE_MATCH_TYPE_OR ;
cert_usage . Usage = enhkey_usage ;
chain_params . RequestedUsage = cert_usage ;
chain_flags = CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT ;
if ( ! CertGetCertificateChain ( NULL , cert_ctx , NULL , NULL , & chain_params , chain_flags , NULL , & cert_chain_ctx ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Error getting certificate chain: %s " , php_win_err ( ) ) ;
CertFreeCertificateContext ( cert_ctx ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
}
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
/* check if the cert is self-signed */
if ( cert_chain_ctx - > cChain > 0 & & cert_chain_ctx - > rgpChain [ 0 ] - > cElement > 0
& & ( cert_chain_ctx - > rgpChain [ 0 ] - > rgpElement [ 0 ] - > TrustStatus . dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED ) ! = 0 ) {
is_self_signed = 1 ;
}
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
/* check the depth */
GET_VER_OPT_LONG ( " verify_depth " , allowed_depth ) ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
for ( i = 0 ; i < cert_chain_ctx - > cChain ; i + + ) {
if ( cert_chain_ctx - > rgpChain [ i ] - > cElement > allowed_depth ) {
CertFreeCertificateChain ( cert_chain_ctx ) ;
CertFreeCertificateContext ( cert_ctx ) ;
RETURN_CERT_VERIFY_FAILURE ( X509_V_ERR_CERT_CHAIN_TOO_LONG ) ;
}
}
}
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
{ /* Then verify it against a policy */
SSL_EXTRA_CERT_CHAIN_POLICY_PARA ssl_policy_params = { sizeof ( SSL_EXTRA_CERT_CHAIN_POLICY_PARA ) } ;
CERT_CHAIN_POLICY_PARA chain_policy_params = { sizeof ( CERT_CHAIN_POLICY_PARA ) } ;
CERT_CHAIN_POLICY_STATUS chain_policy_status = { sizeof ( CERT_CHAIN_POLICY_STATUS ) } ;
LPWSTR server_name = NULL ;
BOOL verify_result ;
{ /* This looks ridiculous and it is - but we validate the name ourselves using the peer_name
ctx option , so just use the CN from the cert here */
X509_NAME * cert_name ;
unsigned char * cert_name_utf8 ;
int index , cert_name_utf8_len ;
DWORD num_wchars ;
cert_name = X509_get_subject_name ( x509_store_ctx - > cert ) ;
index = X509_NAME_get_index_by_NID ( cert_name , NID_commonName , - 1 ) ;
if ( index < 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to locate certificate CN " ) ;
CertFreeCertificateChain ( cert_chain_ctx ) ;
CertFreeCertificateContext ( cert_ctx ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
}
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
cert_name_utf8_len = PHP_X509_NAME_ENTRY_TO_UTF8 ( cert_name , index , cert_name_utf8 ) ;
num_wchars = MultiByteToWideChar ( CP_UTF8 , 0 , ( char * ) cert_name_utf8 , - 1 , NULL , 0 ) ;
if ( num_wchars = = 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to convert %s to wide character string " , cert_name_utf8 ) ;
OPENSSL_free ( cert_name_utf8 ) ;
CertFreeCertificateChain ( cert_chain_ctx ) ;
CertFreeCertificateContext ( cert_ctx ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
server_name = emalloc ( ( num_wchars * sizeof ( WCHAR ) ) + sizeof ( WCHAR ) ) ;
num_wchars = MultiByteToWideChar ( CP_UTF8 , 0 , ( char * ) cert_name_utf8 , - 1 , server_name , num_wchars ) ;
if ( num_wchars = = 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to convert %s to wide character string " , cert_name_utf8 ) ;
efree ( server_name ) ;
OPENSSL_free ( cert_name_utf8 ) ;
CertFreeCertificateChain ( cert_chain_ctx ) ;
CertFreeCertificateContext ( cert_ctx ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
OPENSSL_free ( cert_name_utf8 ) ;
}
ssl_policy_params . dwAuthType = ( sslsock - > is_client ) ? AUTHTYPE_SERVER : AUTHTYPE_CLIENT ;
ssl_policy_params . pwszServerName = server_name ;
chain_policy_params . pvExtraPolicyPara = & ssl_policy_params ;
verify_result = CertVerifyCertificateChainPolicy ( CERT_CHAIN_POLICY_SSL , cert_chain_ctx , & chain_policy_params , & chain_policy_status ) ;
efree ( server_name ) ;
CertFreeCertificateChain ( cert_chain_ctx ) ;
CertFreeCertificateContext ( cert_ctx ) ;
if ( ! verify_result ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Error verifying certificate chain policy: %s " , php_win_err ( ) ) ;
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
}
if ( chain_policy_status . dwError ! = 0 ) {
/* The chain does not match the policy */
if ( is_self_signed & & chain_policy_status . dwError = = CERT_E_UNTRUSTEDROOT
& & GET_VER_OPT ( " allow_self_signed " ) & & zend_is_true ( * val ) ) {
/* allow self-signed certs */
X509_STORE_CTX_set_error ( x509_store_ctx , X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ) ;
} else {
RETURN_CERT_VERIFY_FAILURE ( SSL_R_CERTIFICATE_VERIFY_FAILED ) ;
2014-02-20 23:26:55 +00:00
}
}
}
2014-03-04 01:57:33 +00:00
return 1 ;
2014-02-20 23:26:55 +00:00
}
/* }}} */
2014-03-04 01:57:33 +00:00
# endif
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
static long load_stream_cafile ( X509_STORE * cert_store , const char * cafile TSRMLS_DC ) /* { { { */
2014-02-20 23:26:55 +00:00
{
2014-03-04 01:57:33 +00:00
php_stream * stream ;
X509 * cert ;
BIO * buffer ;
int buffer_active = 0 ;
char * line = NULL ;
size_t line_len ;
long certs_added = 0 ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
stream = php_stream_open_wrapper ( cafile , " rb " , 0 , NULL ) ;
if ( stream = = NULL ) {
php_error ( E_WARNING , " failed loading cafile stream: `%s' " , cafile ) ;
return 0 ;
} else if ( stream - > wrapper - > is_url ) {
php_stream_close ( stream ) ;
php_error ( E_WARNING , " remote cafile streams are disabled for security purposes " ) ;
return 0 ;
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
cert_start : {
line = php_stream_get_line ( stream , NULL , 0 , & line_len ) ;
if ( line = = NULL ) {
goto stream_complete ;
} else if ( ! strcmp ( line , " -----BEGIN CERTIFICATE----- \n " ) | |
! strcmp ( line , " -----BEGIN CERTIFICATE----- \r \n " )
) {
buffer = BIO_new ( BIO_s_mem ( ) ) ;
buffer_active = 1 ;
goto cert_line ;
} else {
efree ( line ) ;
goto cert_start ;
}
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
cert_line : {
BIO_puts ( buffer , line ) ;
efree ( line ) ;
line = php_stream_get_line ( stream , NULL , 0 , & line_len ) ;
if ( line = = NULL ) {
goto stream_complete ;
} else if ( ! strcmp ( line , " -----END CERTIFICATE----- " ) | |
! strcmp ( line , " -----END CERTIFICATE----- \n " ) | |
! strcmp ( line , " -----END CERTIFICATE----- \r \n " )
) {
goto add_cert ;
} else {
goto cert_line ;
}
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
add_cert : {
BIO_puts ( buffer , line ) ;
efree ( line ) ;
cert = PEM_read_bio_X509 ( buffer , NULL , 0 , NULL ) ;
BIO_free ( buffer ) ;
buffer_active = 0 ;
if ( cert & & X509_STORE_add_cert ( cert_store , cert ) ) {
+ + certs_added ;
}
goto cert_start ;
}
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
stream_complete : {
php_stream_close ( stream ) ;
if ( buffer_active = = 1 ) {
BIO_free ( buffer ) ;
}
}
if ( certs_added = = 0 ) {
php_error ( E_WARNING , " no valid certs found cafile stream: `%s' " , cafile ) ;
}
return certs_added ;
}
/* }}} */
static int enable_peer_verification ( SSL_CTX * ctx , php_stream * stream TSRMLS_DC ) /* { { { */
{
zval * * val = NULL ;
char * cafile = NULL ;
char * capath = NULL ;
GET_VER_OPT_STRING ( " cafile " , cafile ) ;
GET_VER_OPT_STRING ( " capath " , capath ) ;
if ( ! cafile ) {
cafile = zend_ini_string ( " openssl.cafile " , sizeof ( " openssl.cafile " ) , 0 ) ;
cafile = strlen ( cafile ) ? cafile : NULL ;
}
if ( ! capath ) {
capath = zend_ini_string ( " openssl.capath " , sizeof ( " openssl.capath " ) , 0 ) ;
capath = strlen ( capath ) ? capath : NULL ;
}
if ( cafile | | capath ) {
if ( ! SSL_CTX_load_verify_locations ( ctx , cafile , capath ) ) {
if ( cafile & & ! load_stream_cafile ( SSL_CTX_get_cert_store ( ctx ) , cafile TSRMLS_CC ) ) {
return FAILURE ;
}
}
} else {
# if defined(PHP_WIN32) && OPENSSL_VERSION_NUMBER >= 0x00907000L
SSL_CTX_set_cert_verify_callback ( ctx , win_cert_verify_callback , ( void * ) stream ) ;
SSL_CTX_set_verify ( ctx , SSL_VERIFY_PEER , NULL ) ;
# else
php_openssl_netstream_data_t * sslsock ;
sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
if ( sslsock - > is_client & & ! SSL_CTX_set_default_verify_paths ( ctx ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Unable to set default verify locations and no CA settings specified " ) ;
return FAILURE ;
}
# endif
}
SSL_CTX_set_verify ( ctx , SSL_VERIFY_PEER , verify_callback ) ;
return SUCCESS ;
}
/* }}} */
static void disable_peer_verification ( SSL_CTX * ctx , php_stream * stream TSRMLS_DC ) /* { { { */
{
SSL_CTX_set_verify ( ctx , SSL_VERIFY_NONE , NULL ) ;
}
/* }}} */
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
static int set_local_cert ( SSL_CTX * ctx , php_stream * stream TSRMLS_DC ) /* { { { */
{
zval * * val = NULL ;
char * certfile = NULL ;
GET_VER_OPT_STRING ( " local_cert " , certfile ) ;
if ( certfile ) {
char resolved_path_buff [ MAXPATHLEN ] ;
const char * private_key = NULL ;
if ( VCWD_REALPATH ( certfile , resolved_path_buff ) ) {
/* a certificate to use for authentication */
if ( SSL_CTX_use_certificate_chain_file ( ctx , resolved_path_buff ) ! = 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to set local cert chain file `%s'; Check that your cafile/capath settings include details of your certificate and its issuer " , certfile ) ;
return FAILURE ;
}
GET_VER_OPT_STRING ( " local_pk " , private_key ) ;
if ( private_key ) {
char resolved_path_buff_pk [ MAXPATHLEN ] ;
if ( VCWD_REALPATH ( private_key , resolved_path_buff_pk ) ) {
if ( SSL_CTX_use_PrivateKey_file ( ctx , resolved_path_buff_pk , SSL_FILETYPE_PEM ) ! = 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to set private key file `%s' " , resolved_path_buff_pk ) ;
return FAILURE ;
}
}
} else {
if ( SSL_CTX_use_PrivateKey_file ( ctx , resolved_path_buff , SSL_FILETYPE_PEM ) ! = 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Unable to set private key file `%s' " , resolved_path_buff ) ;
return FAILURE ;
}
}
# if OPENSSL_VERSION_NUMBER < 0x10001001L
do {
/* Unnecessary as of OpenSSLv1.0.1 (will segfault if used with >= 10001001 ) */
X509 * cert = NULL ;
EVP_PKEY * key = NULL ;
SSL * tmpssl = SSL_new ( ctx ) ;
cert = SSL_get_certificate ( tmpssl ) ;
if ( cert ) {
key = X509_get_pubkey ( cert ) ;
EVP_PKEY_copy_parameters ( key , SSL_get_privatekey ( tmpssl ) ) ;
EVP_PKEY_free ( key ) ;
}
SSL_free ( tmpssl ) ;
} while ( 0 ) ;
# endif
if ( ! SSL_CTX_check_private_key ( ctx ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Private key does not match certificate! " ) ;
}
}
}
return SUCCESS ;
2014-02-20 23:26:55 +00:00
}
/* }}} */
2003-11-27 17:40:16 +00:00
2014-03-04 01:57:33 +00:00
static const SSL_METHOD * php_select_crypto_method ( long method_value , int is_client TSRMLS_DC ) /* { { { */
2014-02-11 15:12:01 +00:00
{
if ( method_value = = STREAM_CRYPTO_METHOD_SSLv2 ) {
# ifndef OPENSSL_NO_SSL2
return is_client ? SSLv2_client_method ( ) : SSLv2_server_method ( ) ;
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSLv2 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# endif
} else if ( method_value = = STREAM_CRYPTO_METHOD_SSLv3 ) {
2014-08-25 16:16:38 +00:00
# ifndef OPENSSL_NO_SSL3
2014-02-11 15:12:01 +00:00
return is_client ? SSLv3_client_method ( ) : SSLv3_server_method ( ) ;
2014-08-25 16:16:38 +00:00
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSLv3 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# endif
2014-02-11 15:12:01 +00:00
} else if ( method_value = = STREAM_CRYPTO_METHOD_TLSv1_0 ) {
return is_client ? TLSv1_client_method ( ) : TLSv1_server_method ( ) ;
} else if ( method_value = = STREAM_CRYPTO_METHOD_TLSv1_1 ) {
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
return is_client ? TLSv1_1_client_method ( ) : TLSv1_1_server_method ( ) ;
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# endif
} else if ( method_value = = STREAM_CRYPTO_METHOD_TLSv1_2 ) {
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
return is_client ? TLSv1_2_client_method ( ) : TLSv1_2_server_method ( ) ;
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
2014-02-21 00:20:40 +00:00
" TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against " ) ;
2014-02-11 15:12:01 +00:00
return NULL ;
# endif
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Invalid crypto method " ) ;
return NULL ;
}
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-02-11 15:12:01 +00:00
2014-03-04 01:57:33 +00:00
static long php_get_crypto_method_ctx_flags ( long method_flags TSRMLS_DC ) /* { { { */
2014-02-11 15:12:01 +00:00
{
long ssl_ctx_options = SSL_OP_ALL ;
# ifndef OPENSSL_NO_SSL2
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_SSLv2 ) ) {
ssl_ctx_options | = SSL_OP_NO_SSLv2 ;
}
# endif
2014-02-21 00:20:40 +00:00
# ifndef OPENSSL_NO_SSL3
2014-02-11 15:12:01 +00:00
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_SSLv3 ) ) {
ssl_ctx_options | = SSL_OP_NO_SSLv3 ;
}
2014-02-21 00:20:40 +00:00
# endif
# ifndef OPENSSL_NO_TLS1
2014-02-11 15:12:01 +00:00
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_TLSv1_0 ) ) {
ssl_ctx_options | = SSL_OP_NO_TLSv1 ;
}
2014-02-21 00:20:40 +00:00
# endif
2014-02-11 15:12:01 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
2014-02-21 00:20:40 +00:00
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_TLSv1_1 ) ) {
2014-02-11 15:12:01 +00:00
ssl_ctx_options | = SSL_OP_NO_TLSv1_1 ;
}
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_TLSv1_2 ) ) {
ssl_ctx_options | = SSL_OP_NO_TLSv1_2 ;
}
2014-02-21 00:20:40 +00:00
# endif
2014-02-11 15:12:01 +00:00
return ssl_ctx_options ;
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-02-11 15:12:01 +00:00
2014-03-04 01:57:33 +00:00
static void limit_handshake_reneg ( const SSL * ssl ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2014-03-04 01:57:33 +00:00
php_stream * stream ;
php_openssl_netstream_data_t * sslsock ;
struct timeval now ;
long elapsed_time ;
2014-02-11 15:12:01 +00:00
2014-03-04 01:57:33 +00:00
stream = php_openssl_get_stream_from_ssl_handle ( ssl ) ;
sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
gettimeofday ( & now , NULL ) ;
/* The initial handshake is never rate-limited */
if ( sslsock - > reneg - > prev_handshake = = 0 ) {
sslsock - > reneg - > prev_handshake = now . tv_sec ;
return ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
elapsed_time = ( now . tv_sec - sslsock - > reneg - > prev_handshake ) ;
sslsock - > reneg - > prev_handshake = now . tv_sec ;
sslsock - > reneg - > tokens - = ( elapsed_time * ( sslsock - > reneg - > limit / sslsock - > reneg - > window ) ) ;
2014-02-11 15:12:01 +00:00
2014-03-04 01:57:33 +00:00
if ( sslsock - > reneg - > tokens < 0 ) {
sslsock - > reneg - > tokens = 0 ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
+ + sslsock - > reneg - > tokens ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
/* The token level exceeds our allowed limit */
if ( sslsock - > reneg - > tokens > sslsock - > reneg - > limit ) {
zval * * val ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
TSRMLS_FETCH ( ) ;
2004-05-23 10:36:08 +00:00
2014-03-04 01:57:33 +00:00
sslsock - > reneg - > should_close = 1 ;
2010-12-03 09:34:35 +00:00
2014-03-04 01:57:33 +00:00
if ( stream - > context & & SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " reneg_limit_callback " , & val )
2014-01-31 21:18:31 +00:00
) {
2014-03-04 01:57:33 +00:00
zval * param , * * params [ 1 ] , * retval ;
2013-01-30 19:45:31 +00:00
2014-03-04 01:57:33 +00:00
MAKE_STD_ZVAL ( param ) ;
php_stream_to_zval ( stream , param ) ;
params [ 0 ] = & param ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
/* Closing the stream inside this callback would segfault! */
stream - > flags | = PHP_STREAM_FLAG_NO_FCLOSE ;
if ( FAILURE = = call_user_function_ex ( EG ( function_table ) , NULL , * val , & retval , 1 , params , 0 , NULL TSRMLS_CC ) ) {
php_error ( E_WARNING , " SSL: failed invoking reneg limit notification callback " ) ;
}
stream - > flags ^ = PHP_STREAM_FLAG_NO_FCLOSE ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
/* If the reneg_limit_callback returned true don't auto-close */
if ( retval ! = NULL & & Z_TYPE_P ( retval ) = = IS_BOOL & & Z_BVAL_P ( retval ) = = 1 ) {
sslsock - > reneg - > should_close = 0 ;
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
FREE_ZVAL ( param ) ;
if ( retval ! = NULL ) {
zval_ptr_dtor ( & retval ) ;
}
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SSL: client-initiated handshake rate limit exceeded by peer " ) ;
}
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
}
/* }}} */
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
static void info_callback ( const SSL * ssl , int where , int ret ) /* { { { */
{
/* Rate-limit client-initiated handshake renegotiation to prevent DoS */
if ( where & SSL_CB_HANDSHAKE_START ) {
limit_handshake_reneg ( ssl ) ;
2014-02-20 03:39:57 +00:00
}
2014-03-04 01:57:33 +00:00
}
/* }}} */
2014-02-20 03:39:57 +00:00
2014-03-04 01:57:33 +00:00
static void init_server_reneg_limit ( php_stream * stream , php_openssl_netstream_data_t * sslsock ) /* { { { */
{
zval * * val ;
long limit = OPENSSL_DEFAULT_RENEG_LIMIT ;
long window = OPENSSL_DEFAULT_RENEG_WINDOW ;
2014-02-11 13:24:34 +00:00
2014-03-04 01:57:33 +00:00
if ( stream - > context & &
SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " reneg_limit " , & val )
) {
convert_to_long ( * val ) ;
limit = Z_LVAL_PP ( val ) ;
2014-02-20 23:26:55 +00:00
}
2014-03-04 01:57:33 +00:00
/* No renegotiation rate-limiting */
if ( limit < 0 ) {
return ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
if ( stream - > context & &
SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " reneg_window " , & val )
) {
convert_to_long ( * val ) ;
window = Z_LVAL_PP ( val ) ;
2003-02-27 17:43:38 +00:00
}
2014-02-11 13:42:29 +00:00
2014-03-04 01:57:33 +00:00
sslsock - > reneg = ( void * ) pemalloc ( sizeof ( php_openssl_handshake_bucket_t ) ,
php_stream_is_persistent ( stream )
) ;
2014-02-11 13:42:29 +00:00
2014-03-04 01:57:33 +00:00
sslsock - > reneg - > limit = limit ;
sslsock - > reneg - > window = window ;
sslsock - > reneg - > prev_handshake = 0 ;
sslsock - > reneg - > tokens = 0 ;
sslsock - > reneg - > should_close = 0 ;
2014-02-11 13:42:29 +00:00
2014-03-04 01:57:33 +00:00
SSL_set_info_callback ( sslsock - > ssl_handle , info_callback ) ;
2014-02-11 13:42:29 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-01-28 17:05:56 +00:00
2014-03-04 01:57:33 +00:00
static int set_server_rsa_key ( php_stream * stream , SSL_CTX * ctx TSRMLS_DC ) /* { { { */
2014-02-11 15:42:34 +00:00
{
zval * * val ;
int rsa_key_size ;
RSA * rsa ;
if ( php_stream_context_get_option ( stream - > context , " ssl " , " rsa_key_size " , & val ) = = SUCCESS ) {
rsa_key_size = ( int ) Z_LVAL_PP ( val ) ;
if ( ( rsa_key_size ! = 1 ) & & ( rsa_key_size & ( rsa_key_size - 1 ) ) ) {
2014-03-04 01:57:33 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " RSA key size requires a power of 2: %d " , rsa_key_size ) ;
2014-02-11 15:42:34 +00:00
rsa_key_size = 2048 ;
}
} else {
rsa_key_size = 2048 ;
}
rsa = RSA_generate_key ( rsa_key_size , RSA_F4 , NULL , NULL ) ;
2014-03-04 01:57:33 +00:00
if ( ! SSL_CTX_set_tmp_rsa ( ctx , rsa ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Failed setting RSA key " ) ;
RSA_free ( rsa ) ;
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
RSA_free ( rsa ) ;
2014-03-04 01:57:33 +00:00
return SUCCESS ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
static int set_server_dh_param ( SSL_CTX * ctx , char * dh_path TSRMLS_DC ) /* { { { */
2014-02-11 15:42:34 +00:00
{
DH * dh ;
BIO * bio ;
bio = BIO_new_file ( dh_path , " r " ) ;
if ( bio = = NULL ) {
2014-03-04 01:57:33 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Invalid dh_param file: %s " , dh_path ) ;
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
dh = PEM_read_bio_DHparams ( bio , NULL , NULL , NULL ) ;
BIO_free ( bio ) ;
2014-03-04 01:57:33 +00:00
if ( dh = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Failed reading DH params from file: %s " , dh_path ) ;
return FAILURE ;
}
if ( SSL_CTX_set_tmp_dh ( ctx , dh ) < 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " DH param assignment failed " ) ;
DH_free ( dh ) ;
return FAILURE ;
}
DH_free ( dh ) ;
return SUCCESS ;
}
/* }}} */
# ifdef HAVE_ECDH
static int set_server_ecdh_curve ( php_stream * stream , SSL_CTX * ctx TSRMLS_DC ) /* { { { */
{
zval * * val ;
int curve_nid ;
char * curve_str ;
EC_KEY * ecdh ;
if ( php_stream_context_get_option ( stream - > context , " ssl " , " ecdh_curve " , & val ) = = SUCCESS ) {
convert_to_string_ex ( val ) ;
curve_str = Z_STRVAL_PP ( val ) ;
curve_nid = OBJ_sn2nid ( curve_str ) ;
if ( curve_nid = = NID_undef ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Invalid ECDH curve: %s " , curve_str ) ;
return FAILURE ;
}
} else {
curve_nid = NID_X9_62_prime256v1 ;
}
ecdh = EC_KEY_new_by_curve_name ( curve_nid ) ;
if ( ecdh = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" Failed generating ECDH curve " ) ;
return FAILURE ;
}
SSL_CTX_set_tmp_ecdh ( ctx , ecdh ) ;
EC_KEY_free ( ecdh ) ;
return SUCCESS ;
}
/* }}} */
# endif
static int set_server_specific_opts ( php_stream * stream , SSL_CTX * ctx TSRMLS_DC ) /* { { { */
{
zval * * val ;
long ssl_ctx_options = SSL_CTX_get_options ( ctx ) ;
# ifdef HAVE_ECDH
if ( FAILURE = = set_server_ecdh_curve ( stream , ctx TSRMLS_CC ) ) {
return FAILURE ;
}
# else
if ( SUCCESS = = php_stream_context_get_option ( stream - > context , " ssl " , " ecdh_curve " , & val ) ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" ECDH curve support not compiled into the OpenSSL lib against which PHP is linked " ) ;
return FAILURE ;
}
# endif
if ( php_stream_context_get_option ( stream - > context , " ssl " , " dh_param " , & val ) = = SUCCESS ) {
convert_to_string_ex ( val ) ;
if ( FAILURE = = set_server_dh_param ( ctx , Z_STRVAL_PP ( val ) TSRMLS_CC ) ) {
return FAILURE ;
}
}
if ( FAILURE = = set_server_rsa_key ( stream , ctx TSRMLS_CC ) ) {
return FAILURE ;
}
if ( SUCCESS = = php_stream_context_get_option (
stream - > context , " ssl " , " honor_cipher_order " , & val ) & &
zend_is_true ( * val )
) {
ssl_ctx_options | = SSL_OP_CIPHER_SERVER_PREFERENCE ;
}
if ( SUCCESS = = php_stream_context_get_option (
stream - > context , " ssl " , " single_dh_use " , & val ) & &
zend_is_true ( * val )
) {
ssl_ctx_options | = SSL_OP_SINGLE_DH_USE ;
}
2014-07-15 21:10:29 +00:00
# ifdef HAVE_ECDH
2014-03-04 01:57:33 +00:00
if ( SUCCESS = = php_stream_context_get_option (
stream - > context , " ssl " , " single_ecdh_use " , & val ) & &
zend_is_true ( * val )
) {
ssl_ctx_options | = SSL_OP_SINGLE_ECDH_USE ;
}
2014-07-15 21:10:29 +00:00
# endif
2014-03-04 01:57:33 +00:00
SSL_CTX_set_options ( ctx , ssl_ctx_options ) ;
return SUCCESS ;
}
/* }}} */
# ifdef HAVE_SNI
2014-03-05 15:50:23 +00:00
static int server_sni_callback ( SSL * ssl_handle , int * al , void * arg ) /* { { { */
{
php_stream * stream ;
php_openssl_netstream_data_t * sslsock ;
unsigned i ;
const char * server_name ;
server_name = SSL_get_servername ( ssl_handle , TLSEXT_NAMETYPE_host_name ) ;
if ( ! server_name ) {
return SSL_TLSEXT_ERR_NOACK ;
}
stream = ( php_stream * ) SSL_get_ex_data ( ssl_handle , php_openssl_get_ssl_stream_data_index ( ) ) ;
sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
if ( ! ( sslsock - > sni_cert_count & & sslsock - > sni_certs ) ) {
return SSL_TLSEXT_ERR_NOACK ;
}
for ( i = 0 ; i < sslsock - > sni_cert_count ; i + + ) {
if ( matches_wildcard_name ( server_name , sslsock - > sni_certs [ i ] . name ) ) {
SSL_set_SSL_CTX ( ssl_handle , sslsock - > sni_certs [ i ] . ctx ) ;
return SSL_TLSEXT_ERR_OK ;
}
}
return SSL_TLSEXT_ERR_NOACK ;
}
/* }}} */
static int enable_server_sni ( php_stream * stream , php_openssl_netstream_data_t * sslsock TSRMLS_DC )
{
zval * * val ;
zval * * current ;
char * key ;
uint key_len ;
ulong key_index ;
int key_type ;
HashPosition pos ;
int i = 0 ;
char resolved_path_buff [ MAXPATHLEN ] ;
SSL_CTX * ctx ;
/* If the stream ctx disables SNI we're finished here */
if ( GET_VER_OPT ( " SNI_enabled " ) & & ! zend_is_true ( * val ) ) {
return SUCCESS ;
}
/* If no SNI cert array is specified we're finished here */
if ( ! GET_VER_OPT ( " SNI_server_certs " ) ) {
return SUCCESS ;
}
if ( Z_TYPE_PP ( val ) ! = IS_ARRAY ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SNI_server_certs requires an array mapping host names to cert paths "
) ;
return FAILURE ;
}
sslsock - > sni_cert_count = zend_hash_num_elements ( Z_ARRVAL_PP ( val ) ) ;
if ( sslsock - > sni_cert_count = = 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SNI_server_certs host cert array must not be empty "
) ;
return FAILURE ;
}
sslsock - > sni_certs = ( php_openssl_sni_cert_t * ) safe_pemalloc ( sslsock - > sni_cert_count ,
sizeof ( php_openssl_sni_cert_t ) , 0 , php_stream_is_persistent ( stream )
) ;
for ( zend_hash_internal_pointer_reset_ex ( Z_ARRVAL_PP ( val ) , & pos ) ;
zend_hash_get_current_data_ex ( Z_ARRVAL_PP ( val ) , ( void * * ) & current , & pos ) = = SUCCESS ;
zend_hash_move_forward_ex ( Z_ARRVAL_PP ( val ) , & pos )
) {
key_type = zend_hash_get_current_key_ex ( Z_ARRVAL_PP ( val ) , & key , & key_len , & key_index , 0 , & pos ) ;
if ( key_type ! = HASH_KEY_IS_STRING ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" SNI_server_certs array requires string host name keys "
) ;
return FAILURE ;
}
if ( VCWD_REALPATH ( Z_STRVAL_PP ( current ) , resolved_path_buff ) ) {
/* The hello method is not inherited by SSL structs when assigning a new context
* inside the SNI callback , so the just use SSLv23 */
ctx = SSL_CTX_new ( SSLv23_server_method ( ) ) ;
if ( SSL_CTX_use_certificate_chain_file ( ctx , resolved_path_buff ) ! = 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" failed setting local cert chain file `%s'; " \
" check that your cafile/capath settings include " \
" details of your certificate and its issuer " ,
resolved_path_buff
) ;
SSL_CTX_free ( ctx ) ;
return FAILURE ;
} else if ( SSL_CTX_use_PrivateKey_file ( ctx , resolved_path_buff , SSL_FILETYPE_PEM ) ! = 1 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" failed setting private key from file `%s' " ,
resolved_path_buff
) ;
SSL_CTX_free ( ctx ) ;
return FAILURE ;
} else {
sslsock - > sni_certs [ i ] . name = pestrdup ( key , php_stream_is_persistent ( stream ) ) ;
sslsock - > sni_certs [ i ] . ctx = ctx ;
+ + i ;
}
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING ,
" failed setting local cert chain file `%s'; file not found " ,
Z_STRVAL_PP ( current )
) ;
return FAILURE ;
}
}
SSL_CTX_set_tlsext_servername_callback ( sslsock - > ctx , server_sni_callback ) ;
return SUCCESS ;
}
2014-03-04 01:57:33 +00:00
static void enable_client_sni ( php_stream * stream , php_openssl_netstream_data_t * sslsock ) /* { { { */
{
zval * * val ;
char * sni_server_name ;
/* If SNI is explicitly disabled we're finished here */
if ( GET_VER_OPT ( " SNI_enabled " ) & & ! zend_is_true ( * val ) ) {
return ;
}
sni_server_name = sslsock - > url_name ;
GET_VER_OPT_STRING ( " peer_name " , sni_server_name ) ;
if ( GET_VER_OPT ( " SNI_server_name " ) ) {
GET_VER_OPT_STRING ( " SNI_server_name " , sni_server_name ) ;
php_error ( E_DEPRECATED , " SNI_server_name is deprecated in favor of peer_name " ) ;
}
if ( sni_server_name ) {
SSL_set_tlsext_host_name ( sslsock - > ssl_handle , sni_server_name ) ;
}
}
/* }}} */
# endif
int php_openssl_setup_crypto ( php_stream * stream ,
php_openssl_netstream_data_t * sslsock ,
php_stream_xport_crypto_param * cparam
TSRMLS_DC ) /* {{{ */
{
const SSL_METHOD * method ;
long ssl_ctx_options ;
long method_flags ;
char * cipherlist = NULL ;
zval * * val ;
if ( sslsock - > ssl_handle ) {
if ( sslsock - > s . is_blocked ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSL/TLS already set-up for this stream " ) ;
return FAILURE ;
} else {
return SUCCESS ;
}
}
ERR_clear_error ( ) ;
/* We need to do slightly different things based on client/server method
* so lets remember which method was selected */
sslsock - > is_client = cparam - > inputs . method & STREAM_CRYPTO_IS_CLIENT ;
method_flags = ( ( cparam - > inputs . method > > 1 ) < < 1 ) ;
/* Should we use a specific crypto method or is generic SSLv23 okay? */
if ( ( method_flags & ( method_flags - 1 ) ) = = 0 ) {
ssl_ctx_options = SSL_OP_ALL ;
method = php_select_crypto_method ( method_flags , sslsock - > is_client TSRMLS_CC ) ;
if ( method = = NULL ) {
return FAILURE ;
}
} else {
method = sslsock - > is_client ? SSLv23_client_method ( ) : SSLv23_server_method ( ) ;
ssl_ctx_options = php_get_crypto_method_ctx_flags ( method_flags TSRMLS_CC ) ;
if ( ssl_ctx_options = = - 1 ) {
return FAILURE ;
}
}
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
sslsock - > ctx = SSL_CTX_new ( method ) ;
# else
/* Avoid const warning with old versions */
sslsock - > ctx = SSL_CTX_new ( ( SSL_METHOD * ) method ) ;
# endif
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( sslsock - > ctx = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSL context creation failure " ) ;
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x0090806fL
if ( GET_VER_OPT ( " no_ticket " ) & & zend_is_true ( * val ) ) {
ssl_ctx_options | = SSL_OP_NO_TICKET ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
# endif
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x0090605fL
ssl_ctx_options & = ~ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS ;
# endif
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x10000000L
if ( ! GET_VER_OPT ( " disable_compression " ) | | zend_is_true ( * val ) ) {
ssl_ctx_options | = SSL_OP_NO_COMPRESSION ;
}
2014-02-21 10:23:42 +00:00
# endif
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( GET_VER_OPT ( " verify_peer " ) & & ! zend_is_true ( * val ) ) {
disable_peer_verification ( sslsock - > ctx , stream TSRMLS_CC ) ;
} else if ( FAILURE = = enable_peer_verification ( sslsock - > ctx , stream TSRMLS_CC ) ) {
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
/* callback for the passphrase (for localcert) */
if ( GET_VER_OPT ( " passphrase " ) ) {
SSL_CTX_set_default_passwd_cb_userdata ( sslsock - > ctx , stream ) ;
SSL_CTX_set_default_passwd_cb ( sslsock - > ctx , passwd_callback ) ;
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
GET_VER_OPT_STRING ( " ciphers " , cipherlist ) ;
2014-09-24 08:34:55 +00:00
# ifndef USE_OPENSSL_SYSTEM_CIPHERS
2014-03-04 01:57:33 +00:00
if ( ! cipherlist ) {
cipherlist = OPENSSL_DEFAULT_STREAM_CIPHERS ;
}
2014-09-24 08:34:55 +00:00
# endif
if ( cipherlist ) {
if ( SSL_CTX_set_cipher_list ( sslsock - > ctx , cipherlist ) ! = 1 ) {
return FAILURE ;
}
2014-03-04 01:57:33 +00:00
}
if ( FAILURE = = set_local_cert ( sslsock - > ctx , stream TSRMLS_CC ) ) {
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
SSL_CTX_set_options ( sslsock - > ctx , ssl_ctx_options ) ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( sslsock - > is_client = = 0 & &
stream - > context & &
FAILURE = = set_server_specific_opts ( stream , sslsock - > ctx TSRMLS_CC )
) {
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
sslsock - > ssl_handle = SSL_new ( sslsock - > ctx ) ;
if ( sslsock - > ssl_handle = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSL handle creation failure " ) ;
SSL_CTX_free ( sslsock - > ctx ) ;
sslsock - > ctx = NULL ;
return FAILURE ;
} else {
SSL_set_ex_data ( sslsock - > ssl_handle , php_openssl_get_ssl_stream_data_index ( ) , stream ) ;
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( ! SSL_set_fd ( sslsock - > ssl_handle , sslsock - > s . socket ) ) {
handle_ssl_error ( stream , 0 , 1 TSRMLS_CC ) ;
}
2014-02-11 15:42:34 +00:00
2014-03-05 15:50:23 +00:00
# ifdef HAVE_SNI
/* Enable server-side SNI */
if ( sslsock - > is_client = = 0 & & enable_server_sni ( stream , sslsock TSRMLS_CC ) = = FAILURE ) {
return FAILURE ;
}
# endif
2014-03-04 01:57:33 +00:00
/* Enable server-side handshake renegotiation rate-limiting */
if ( sslsock - > is_client = = 0 ) {
init_server_reneg_limit ( stream , sslsock ) ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
# ifdef SSL_MODE_RELEASE_BUFFERS
do {
long mode = SSL_get_mode ( sslsock - > ssl_handle ) ;
SSL_set_mode ( sslsock - > ssl_handle , mode | SSL_MODE_RELEASE_BUFFERS ) ;
} while ( 0 ) ;
2014-02-11 15:42:34 +00:00
# endif
2014-03-04 01:57:33 +00:00
if ( cparam - > inputs . session ) {
if ( cparam - > inputs . session - > ops ! = & php_openssl_socket_ops ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " supplied session stream must be an SSL enabled stream " ) ;
} else if ( ( ( php_openssl_netstream_data_t * ) cparam - > inputs . session - > abstract ) - > ssl_handle = = NULL ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " supplied SSL session stream is not initialized " ) ;
} else {
SSL_copy_session_id ( sslsock - > ssl_handle , ( ( php_openssl_netstream_data_t * ) cparam - > inputs . session - > abstract ) - > ssl_handle ) ;
}
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
return SUCCESS ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
static zval * capture_session_meta ( SSL * ssl_handle ) /* { { { */
2014-02-11 15:42:34 +00:00
{
2014-03-04 01:57:33 +00:00
zval * meta_arr ;
char * proto_str ;
long proto = SSL_version ( ssl_handle ) ;
const SSL_CIPHER * cipher = SSL_get_current_cipher ( ssl_handle ) ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
switch ( proto ) {
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
case TLS1_2_VERSION : proto_str = " TLSv1.2 " ; break ;
case TLS1_1_VERSION : proto_str = " TLSv1.1 " ; break ;
# endif
case TLS1_VERSION : proto_str = " TLSv1 " ; break ;
case SSL3_VERSION : proto_str = " SSLv3 " ; break ;
case SSL2_VERSION : proto_str = " SSLv2 " ; break ;
default : proto_str = " UNKNOWN " ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
MAKE_STD_ZVAL ( meta_arr ) ;
array_init ( meta_arr ) ;
add_assoc_string ( meta_arr , " protocol " , proto_str , 1 ) ;
add_assoc_string ( meta_arr , " cipher_name " , ( char * ) SSL_CIPHER_get_name ( cipher ) , 1 ) ;
add_assoc_long ( meta_arr , " cipher_bits " , SSL_CIPHER_get_bits ( cipher , NULL ) ) ;
add_assoc_string ( meta_arr , " cipher_version " , SSL_CIPHER_get_version ( cipher ) , 1 ) ;
return meta_arr ;
2014-02-11 15:42:34 +00:00
}
/* }}} */
2014-03-04 01:57:33 +00:00
static int capture_peer_certs ( php_stream * stream , php_openssl_netstream_data_t * sslsock , X509 * peer_cert TSRMLS_DC ) /* { { { */
2014-03-02 14:03:39 +00:00
{
zval * * val , * zcert ;
int cert_captured = 0 ;
if ( SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " capture_peer_cert " , & val ) & &
zend_is_true ( * val )
) {
MAKE_STD_ZVAL ( zcert ) ;
ZVAL_RESOURCE ( zcert , zend_list_insert ( peer_cert , php_openssl_get_x509_list_id ( ) TSRMLS_CC ) ) ;
php_stream_context_set_option ( stream - > context , " ssl " , " peer_certificate " , zcert ) ;
cert_captured = 1 ;
FREE_ZVAL ( zcert ) ;
}
if ( SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " capture_peer_cert_chain " , & val ) & &
zend_is_true ( * val )
) {
zval * arr ;
STACK_OF ( X509 ) * chain ;
MAKE_STD_ZVAL ( arr ) ;
chain = SSL_get_peer_cert_chain ( sslsock - > ssl_handle ) ;
if ( chain & & sk_X509_num ( chain ) > 0 ) {
int i ;
array_init ( arr ) ;
for ( i = 0 ; i < sk_X509_num ( chain ) ; i + + ) {
X509 * mycert = X509_dup ( sk_X509_value ( chain , i ) ) ;
MAKE_STD_ZVAL ( zcert ) ;
ZVAL_RESOURCE ( zcert , zend_list_insert ( mycert , php_openssl_get_x509_list_id ( ) TSRMLS_CC ) ) ;
add_next_index_zval ( arr , zcert ) ;
}
} else {
ZVAL_NULL ( arr ) ;
}
php_stream_context_set_option ( stream - > context , " ssl " , " peer_certificate_chain " , arr ) ;
zval_dtor ( arr ) ;
efree ( arr ) ;
}
return cert_captured ;
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-03-02 14:03:39 +00:00
2014-03-04 01:57:33 +00:00
static int php_openssl_enable_crypto ( php_stream * stream ,
2014-02-11 15:42:34 +00:00
php_openssl_netstream_data_t * sslsock ,
php_stream_xport_crypto_param * cparam
TSRMLS_DC )
2003-02-27 17:43:38 +00:00
{
2014-03-02 14:03:39 +00:00
int n ;
int retry = 1 ;
int cert_captured ;
X509 * peer_cert ;
2003-02-27 17:43:38 +00:00
2003-11-27 17:40:16 +00:00
if ( cparam - > inputs . activate & & ! sslsock - > ssl_active ) {
2010-12-23 01:44:54 +00:00
struct timeval start_time ,
* timeout ;
int blocked = sslsock - > s . is_blocked ,
has_timeout = 0 ;
2006-12-05 01:39:07 +00:00
2014-03-04 01:57:33 +00:00
# ifdef HAVE_SNI
2014-02-11 15:42:34 +00:00
if ( sslsock - > is_client ) {
enable_client_sni ( stream , sslsock ) ;
2009-11-30 13:31:53 +00:00
}
# endif
2014-02-11 15:42:34 +00:00
2004-12-25 02:02:56 +00:00
if ( ! sslsock - > state_set ) {
if ( sslsock - > is_client ) {
SSL_set_connect_state ( sslsock - > ssl_handle ) ;
} else {
SSL_set_accept_state ( sslsock - > ssl_handle ) ;
}
sslsock - > state_set = 1 ;
2003-11-27 17:40:16 +00:00
}
2014-03-04 01:57:33 +00:00
2010-12-23 01:44:54 +00:00
if ( SUCCESS = = php_set_sock_blocking ( sslsock - > s . socket , 0 TSRMLS_CC ) ) {
sslsock - > s . is_blocked = 0 ;
2006-12-05 01:39:07 +00:00
}
2010-12-23 01:44:54 +00:00
timeout = sslsock - > is_client ? & sslsock - > connect_timeout : & sslsock - > s . timeout ;
has_timeout = ! sslsock - > s . is_blocked & & ( timeout - > tv_sec | | timeout - > tv_usec ) ;
/* gettimeofday is not monotonic; using it here is not strictly correct */
if ( has_timeout ) {
gettimeofday ( & start_time , NULL ) ;
}
2003-11-27 17:40:16 +00:00
do {
2010-12-23 01:44:54 +00:00
struct timeval cur_time ,
2013-08-15 03:36:50 +00:00
elapsed_time = { 0 } ;
2010-12-23 01:44:54 +00:00
2003-11-27 17:40:16 +00:00
if ( sslsock - > is_client ) {
2003-02-27 17:43:38 +00:00
n = SSL_connect ( sslsock - > ssl_handle ) ;
2010-12-23 01:44:54 +00:00
} else {
n = SSL_accept ( sslsock - > ssl_handle ) ;
}
2006-12-05 01:39:07 +00:00
2010-12-23 01:44:54 +00:00
if ( has_timeout ) {
gettimeofday ( & cur_time , NULL ) ;
2015-02-09 16:44:09 +00:00
elapsed_time = subtract_timeval ( cur_time , start_time ) ;
2010-12-23 01:44:54 +00:00
2015-02-09 16:44:09 +00:00
if ( compare_timeval ( elapsed_time , * timeout ) > 0 ) {
2014-03-04 03:02:18 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSL: Handshake timed out " ) ;
2006-12-05 01:39:07 +00:00
return - 1 ;
}
2003-11-27 17:40:16 +00:00
}
2003-02-27 17:43:38 +00:00
2003-11-27 17:40:16 +00:00
if ( n < = 0 ) {
2010-12-23 01:44:54 +00:00
/* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
retry = handle_ssl_error ( stream , n , blocked TSRMLS_CC ) ;
if ( retry ) {
/* wait until something interesting happens in the socket. It may be a
* timeout . Also consider the unlikely of possibility of a write block */
int err = SSL_get_error ( sslsock - > ssl_handle , n ) ;
struct timeval left_time ;
if ( has_timeout ) {
2015-02-09 16:44:09 +00:00
left_time = subtract_timeval ( * timeout , elapsed_time ) ;
2010-12-23 01:44:54 +00:00
}
php_pollfd_for ( sslsock - > s . socket , ( err = = SSL_ERROR_WANT_READ ) ?
( POLLIN | POLLPRI ) : POLLOUT , has_timeout ? & left_time : NULL ) ;
}
2003-11-27 17:40:16 +00:00
} else {
2010-12-23 01:44:54 +00:00
retry = 0 ;
2003-11-27 17:40:16 +00:00
}
} while ( retry ) ;
2003-02-27 17:43:38 +00:00
2010-12-23 01:44:54 +00:00
if ( sslsock - > s . is_blocked ! = blocked & & SUCCESS = = php_set_sock_blocking ( sslsock - > s . socket , blocked TSRMLS_CC ) ) {
2006-12-05 01:39:07 +00:00
sslsock - > s . is_blocked = blocked ;
}
2003-11-27 17:40:16 +00:00
if ( n = = 1 ) {
peer_cert = SSL_get_peer_certificate ( sslsock - > ssl_handle ) ;
2014-03-02 14:03:39 +00:00
if ( peer_cert & & stream - > context ) {
cert_captured = capture_peer_certs ( stream , sslsock , peer_cert TSRMLS_CC ) ;
}
2003-11-27 17:40:16 +00:00
2014-03-04 01:57:33 +00:00
if ( FAILURE = = apply_peer_verification_policy ( sslsock - > ssl_handle , peer_cert , stream TSRMLS_CC ) ) {
2003-11-27 17:40:16 +00:00
SSL_shutdown ( sslsock - > ssl_handle ) ;
2011-06-08 00:23:02 +00:00
n = - 1 ;
2003-11-27 17:40:16 +00:00
} else {
2003-02-27 17:43:38 +00:00
sslsock - > ssl_active = 1 ;
2006-04-30 23:43:46 +00:00
if ( stream - > context ) {
2014-03-02 14:03:39 +00:00
zval * * val ;
2006-04-30 23:43:46 +00:00
2014-03-02 14:03:39 +00:00
if ( SUCCESS = = php_stream_context_get_option ( stream - > context ,
" ssl " , " capture_session_meta " , & val ) & &
zend_is_true ( * val )
) {
2014-03-04 01:57:33 +00:00
zval * meta_arr = capture_session_meta ( sslsock - > ssl_handle ) ;
2014-03-02 14:03:39 +00:00
php_stream_context_set_option ( stream - > context , " ssl " , " session_meta " , meta_arr ) ;
2014-02-11 13:42:29 +00:00
zval_dtor ( meta_arr ) ;
efree ( meta_arr ) ;
}
2006-04-30 23:43:46 +00:00
}
2003-02-27 17:43:38 +00:00
}
2014-03-02 14:03:39 +00:00
} else if ( errno = = EAGAIN ) {
n = 0 ;
} else {
n = - 1 ;
peer_cert = SSL_get_peer_certificate ( sslsock - > ssl_handle ) ;
if ( peer_cert & & stream - > context ) {
cert_captured = capture_peer_certs ( stream , sslsock , peer_cert TSRMLS_CC ) ;
2006-04-30 23:43:46 +00:00
}
2014-03-02 14:03:39 +00:00
}
if ( n & & peer_cert & & cert_captured = = 0 ) {
X509_free ( peer_cert ) ;
2003-02-27 17:43:38 +00:00
}
2003-11-27 17:40:16 +00:00
return n ;
} else if ( ! cparam - > inputs . activate & & sslsock - > ssl_active ) {
2003-02-27 17:43:38 +00:00
/* deactivate - common for server/client */
2003-11-27 17:40:16 +00:00
SSL_shutdown ( sslsock - > ssl_handle ) ;
sslsock - > ssl_active = 0 ;
2003-02-27 17:43:38 +00:00
}
2014-03-02 14:03:39 +00:00
2003-02-27 17:43:38 +00:00
return - 1 ;
}
2015-02-09 16:44:09 +00:00
static size_t php_openssl_sockop_read ( php_stream * stream , char * buf , size_t count TSRMLS_DC ) /* { { { */
2015-01-28 05:04:20 +00:00
{
2015-02-09 16:42:17 +00:00
return php_openssl_sockop_io ( 1 , stream , buf , count TSRMLS_CC ) ;
2015-01-28 05:04:20 +00:00
}
2015-02-09 16:44:09 +00:00
/* }}} */
2015-01-28 05:04:20 +00:00
2014-03-04 01:57:33 +00:00
static size_t php_openssl_sockop_write ( php_stream * stream , const char * buf , size_t count TSRMLS_DC ) /* { { { */
2015-01-28 05:04:20 +00:00
{
2015-02-09 16:42:17 +00:00
return php_openssl_sockop_io ( 0 , stream , ( char * ) buf , count TSRMLS_CC ) ;
2015-01-28 05:04:20 +00:00
}
2015-02-09 16:44:09 +00:00
/* }}} */
2015-01-28 05:04:20 +00:00
/**
* Factored out common functionality ( blocking , timeout , loop management ) for read and write .
* Perform IO ( read or write ) to an SSL socket . If we have a timeout , we switch to non - blocking mode
* for the duration of the operation , using select to do our waits . If we time out , or we have an error
* report that back to PHP
*
*/
static size_t php_openssl_sockop_io ( int read , php_stream * stream , char * buf , size_t count TSRMLS_DC )
2014-03-04 01:57:33 +00:00
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
2015-02-09 16:42:17 +00:00
int nr_bytes = 0 ;
2014-03-04 01:57:33 +00:00
2015-02-09 16:42:17 +00:00
/* Only do this if SSL is active. */
2014-03-04 01:57:33 +00:00
if ( sslsock - > ssl_active ) {
int retry = 1 ;
2015-02-09 16:42:17 +00:00
struct timeval start_time ;
struct timeval * timeout ;
int blocked = sslsock - > s . is_blocked ;
int has_timeout = 0 ;
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* Begin by making the socket non-blocking. This allows us to check the timeout. */
if ( SUCCESS = = php_set_sock_blocking ( sslsock - > s . socket , 0 TSRMLS_CC ) ) {
sslsock - > s . is_blocked = 0 ;
}
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* Get the timeout value (and make sure we are to check it. */
timeout = sslsock - > is_client ? & sslsock - > connect_timeout : & sslsock - > s . timeout ;
has_timeout = ! sslsock - > s . is_blocked & & ( timeout - > tv_sec | | timeout - > tv_usec ) ;
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* gettimeofday is not monotonic; using it here is not strictly correct */
if ( has_timeout ) {
gettimeofday ( & start_time , NULL ) ;
2014-03-04 01:57:33 +00:00
}
2015-01-28 05:04:20 +00:00
/* Main IO loop. */
do {
struct timeval cur_time , elapsed_time , left_time ;
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* If we have a timeout to check, figure out how much time has elapsed since we started. */
if ( has_timeout ) {
gettimeofday ( & cur_time , NULL ) ;
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* Determine how much time we've taken so far. */
elapsed_time = subtract_timeval ( cur_time , start_time ) ;
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* and return an error if we've taken too long. */
if ( compare_timeval ( elapsed_time , * timeout ) > 0 ) {
/* If the socket was originally blocking, set it back. */
if ( blocked ) {
php_set_sock_blocking ( sslsock - > s . socket , 1 TSRMLS_CC ) ;
sslsock - > s . is_blocked = 1 ;
}
return - 1 ;
}
}
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* Now, do the IO operation. Don't block if we can't complete... */
if ( read ) {
2015-01-29 03:36:41 +00:00
nr_bytes = SSL_read ( sslsock - > ssl_handle , buf , count ) ;
2014-03-04 01:57:33 +00:00
2015-02-09 16:44:09 +00:00
if ( sslsock - > reneg & & sslsock - > reneg - > should_close ) {
/* renegotiation rate limiting triggered */
php_stream_xport_shutdown ( stream , ( stream_shutdown_t ) SHUT_RDWR TSRMLS_CC ) ;
nr_bytes = 0 ;
stream - > eof = 1 ;
break ;
}
2015-01-28 05:04:20 +00:00
} else {
nr_bytes = SSL_write ( sslsock - > ssl_handle , buf , count ) ;
}
/* Now, how much time until we time out? */
if ( has_timeout ) {
left_time = subtract_timeval ( * timeout , elapsed_time ) ;
}
/* If we didn't do anything on the last loop (or an error) check to see if we should retry or exit. */
2003-02-27 17:43:38 +00:00
if ( nr_bytes < = 0 ) {
2015-01-28 05:04:20 +00:00
/* Get the error code from SSL, and check to see if it's an error or not. */
int err = SSL_get_error ( sslsock - > ssl_handle , nr_bytes ) ;
2014-03-04 01:57:33 +00:00
retry = handle_ssl_error ( stream , nr_bytes , 0 TSRMLS_CC ) ;
2015-01-28 05:04:20 +00:00
/* If we get this (the above doesn't check) then we'll retry as well. */
if ( errno = = EAGAIN & & err = = SSL_ERROR_WANT_READ & & read ) {
retry = 1 ;
}
2015-02-13 12:39:46 +00:00
if ( errno = = EAGAIN & & err = = SSL_ERROR_WANT_WRITE & & read = = 0 ) {
2015-01-28 05:04:20 +00:00
retry = 1 ;
}
/* Also, on reads, we may get this condition on an EOF. We should check properly. */
if ( read ) {
2014-03-04 01:57:33 +00:00
stream - > eof = ( retry = = 0 & & errno ! = EAGAIN & & ! SSL_pending ( sslsock - > ssl_handle ) ) ;
2015-01-28 05:04:20 +00:00
}
2014-03-04 01:57:33 +00:00
2015-01-28 05:04:20 +00:00
/* Now, if we have to wait some time, and we're supposed to be blocking, wait for the socket to become
* available . Now , php_pollfd_for uses select to wait up to our time_left value only . . .
*/
if ( retry & & blocked ) {
if ( read ) {
php_pollfd_for ( sslsock - > s . socket , ( err = = SSL_ERROR_WANT_WRITE ) ?
( POLLOUT | POLLPRI ) : ( POLLIN | POLLPRI ) , has_timeout ? & left_time : NULL ) ;
} else {
php_pollfd_for ( sslsock - > s . socket , ( err = = SSL_ERROR_WANT_READ ) ?
( POLLIN | POLLPRI ) : ( POLLOUT | POLLPRI ) , has_timeout ? & left_time : NULL ) ;
}
}
2014-03-04 01:57:33 +00:00
} else {
2015-01-28 05:04:20 +00:00
/* Else, if we got bytes back, check for possible errors. */
int err = SSL_get_error ( sslsock - > ssl_handle , nr_bytes ) ;
/* If we didn't get any error, then let's return it to PHP. */
2015-02-09 16:44:09 +00:00
if ( err = = SSL_ERROR_NONE )
2014-03-04 01:57:33 +00:00
break ;
2015-01-28 05:04:20 +00:00
/* Otherwise, we need to wait again (up to time_left or we get an error) */
2015-03-04 17:54:34 +00:00
if ( blocked ) {
2015-01-28 05:04:20 +00:00
if ( read ) {
php_pollfd_for ( sslsock - > s . socket , ( err = = SSL_ERROR_WANT_WRITE ) ?
( POLLOUT | POLLPRI ) : ( POLLIN | POLLPRI ) , has_timeout ? & left_time : NULL ) ;
} else {
php_pollfd_for ( sslsock - > s . socket , ( err = = SSL_ERROR_WANT_READ ) ?
( POLLIN | POLLPRI ) : ( POLLOUT | POLLPRI ) , has_timeout ? & left_time : NULL ) ;
}
2015-03-04 17:54:34 +00:00
}
2014-03-04 01:57:33 +00:00
}
2015-01-28 05:04:20 +00:00
/* Finally, we keep going until we got data, and an SSL_ERROR_NONE, unless we had an error. */
2014-03-04 01:57:33 +00:00
} while ( retry ) ;
2015-01-28 05:04:20 +00:00
/* Tell PHP if we read / wrote bytes. */
2014-03-04 01:57:33 +00:00
if ( nr_bytes > 0 ) {
php_stream_notify_progress_increment ( stream - > context , nr_bytes , 0 ) ;
}
2015-01-28 05:04:20 +00:00
/* And if we were originally supposed to be blocking, let's reset the socket to that. */
if ( blocked ) {
2015-02-09 16:42:17 +00:00
php_set_sock_blocking ( sslsock - > s . socket , 1 TSRMLS_CC ) ;
sslsock - > s . is_blocked = 1 ;
}
} else {
2015-02-09 16:44:09 +00:00
/*
* This block is if we had no timeout . . . We will just sit and wait forever on the IO operation .
*/
2015-02-09 16:42:17 +00:00
if ( read ) {
nr_bytes = php_stream_socket_ops . read ( stream , buf , count TSRMLS_CC ) ;
} else {
nr_bytes = php_stream_socket_ops . write ( stream , buf , count TSRMLS_CC ) ;
}
2014-03-04 01:57:33 +00:00
}
2015-02-09 16:42:17 +00:00
/* PHP doesn't expect a negative return. */
2014-03-04 01:57:33 +00:00
if ( nr_bytes < 0 ) {
nr_bytes = 0 ;
}
return nr_bytes ;
}
/* }}} */
2015-01-28 05:04:20 +00:00
struct timeval subtract_timeval ( struct timeval a , struct timeval b )
{
struct timeval difference ;
difference . tv_sec = a . tv_sec - b . tv_sec ;
difference . tv_usec = a . tv_usec - b . tv_usec ;
if ( a . tv_usec < b . tv_usec ) {
b . tv_sec - = 1L ;
b . tv_usec + = 1000000L ;
}
return difference ;
}
int compare_timeval ( struct timeval a , struct timeval b )
{
if ( a . tv_sec > b . tv_sec | | ( a . tv_sec = = b . tv_sec & & a . tv_usec > b . tv_usec ) ) {
return 1 ;
} else if ( a . tv_sec = = b . tv_sec & & a . tv_usec = = b . tv_usec ) {
return 0 ;
} else {
return - 1 ;
}
}
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
static int php_openssl_sockop_close ( php_stream * stream , int close_handle TSRMLS_DC ) /* { { { */
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
# ifdef PHP_WIN32
int n ;
# endif
unsigned i ;
if ( close_handle ) {
if ( sslsock - > ssl_active ) {
SSL_shutdown ( sslsock - > ssl_handle ) ;
sslsock - > ssl_active = 0 ;
}
if ( sslsock - > ssl_handle ) {
SSL_free ( sslsock - > ssl_handle ) ;
sslsock - > ssl_handle = NULL ;
}
if ( sslsock - > ctx ) {
SSL_CTX_free ( sslsock - > ctx ) ;
sslsock - > ctx = NULL ;
}
# ifdef PHP_WIN32
if ( sslsock - > s . socket = = - 1 )
sslsock - > s . socket = SOCK_ERR ;
# endif
if ( sslsock - > s . socket ! = SOCK_ERR ) {
# ifdef PHP_WIN32
/* prevent more data from coming in */
shutdown ( sslsock - > s . socket , SHUT_RD ) ;
/* try to make sure that the OS sends all data before we close the connection.
* Essentially , we are waiting for the socket to become writeable , which means
* that all pending data has been sent .
* We use a small timeout which should encourage the OS to send the data ,
* but at the same time avoid hanging indefinitely .
* */
do {
n = php_pollfd_for_ms ( sslsock - > s . socket , POLLOUT , 500 ) ;
} while ( n = = - 1 & & php_socket_errno ( ) = = EINTR ) ;
# endif
closesocket ( sslsock - > s . socket ) ;
sslsock - > s . socket = SOCK_ERR ;
}
}
2014-03-05 15:50:23 +00:00
if ( sslsock - > sni_certs ) {
for ( i = 0 ; i < sslsock - > sni_cert_count ; i + + ) {
SSL_CTX_free ( sslsock - > sni_certs [ i ] . ctx ) ;
pefree ( sslsock - > sni_certs [ i ] . name , php_stream_is_persistent ( stream ) ) ;
}
pefree ( sslsock - > sni_certs , php_stream_is_persistent ( stream ) ) ;
sslsock - > sni_certs = NULL ;
}
2014-03-04 01:57:33 +00:00
if ( sslsock - > url_name ) {
pefree ( sslsock - > url_name , php_stream_is_persistent ( stream ) ) ;
}
if ( sslsock - > reneg ) {
pefree ( sslsock - > reneg , php_stream_is_persistent ( stream ) ) ;
}
pefree ( sslsock , php_stream_is_persistent ( stream ) ) ;
return 0 ;
}
/* }}} */
static int php_openssl_sockop_flush ( php_stream * stream TSRMLS_DC ) /* { { { */
{
return php_stream_socket_ops . flush ( stream TSRMLS_CC ) ;
}
/* }}} */
static int php_openssl_sockop_stat ( php_stream * stream , php_stream_statbuf * ssb TSRMLS_DC ) /* { { { */
{
return php_stream_socket_ops . stat ( stream , ssb TSRMLS_CC ) ;
}
/* }}} */
2003-02-28 19:53:21 +00:00
static inline int php_openssl_tcp_sockop_accept ( php_stream * stream , php_openssl_netstream_data_t * sock ,
php_stream_xport_param * xparam STREAMS_DC TSRMLS_DC )
{
int clisock ;
xparam - > outputs . client = NULL ;
clisock = php_network_accept_incoming ( sock - > s . socket ,
xparam - > want_textaddr ? & xparam - > outputs . textaddr : NULL ,
xparam - > want_textaddr ? & xparam - > outputs . textaddrlen : NULL ,
xparam - > want_addr ? & xparam - > outputs . addr : NULL ,
xparam - > want_addr ? & xparam - > outputs . addrlen : NULL ,
xparam - > inputs . timeout ,
xparam - > want_errortext ? & xparam - > outputs . error_text : NULL ,
& xparam - > outputs . error_code
TSRMLS_CC ) ;
if ( clisock > = 0 ) {
php_openssl_netstream_data_t * clisockdata ;
2005-09-07 15:36:31 +00:00
clisockdata = emalloc ( sizeof ( * clisockdata ) ) ;
2003-02-28 19:53:21 +00:00
if ( clisockdata = = NULL ) {
2003-11-27 17:40:16 +00:00
closesocket ( clisock ) ;
2003-02-28 19:53:21 +00:00
/* technically a fatal error */
} else {
/* copy underlying tcp fields */
memset ( clisockdata , 0 , sizeof ( * clisockdata ) ) ;
memcpy ( clisockdata , sock , sizeof ( clisockdata - > s ) ) ;
clisockdata - > s . socket = clisock ;
xparam - > outputs . client = php_stream_alloc_rel ( stream - > ops , clisockdata , NULL , " r+ " ) ;
if ( xparam - > outputs . client ) {
2008-11-16 23:14:12 +00:00
xparam - > outputs . client - > context = stream - > context ;
2008-07-11 10:25:15 +00:00
if ( stream - > context ) {
zend_list_addref ( stream - > context - > rsrc_id ) ;
}
2003-02-28 19:53:21 +00:00
}
}
2004-04-21 23:02:06 +00:00
if ( xparam - > outputs . client & & sock - > enable_on_connect ) {
2014-02-11 15:12:01 +00:00
/* remove the client bit */
if ( sock - > method & STREAM_CRYPTO_IS_CLIENT ) {
sock - > method = ( ( sock - > method > > 1 ) < < 1 ) ;
2004-04-21 23:02:06 +00:00
}
clisockdata - > method = sock - > method ;
if ( php_stream_xport_crypto_setup ( xparam - > outputs . client , clisockdata - > method ,
NULL TSRMLS_CC ) < 0 | | php_stream_xport_crypto_enable (
xparam - > outputs . client , 1 TSRMLS_CC ) < 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Failed to enable crypto " ) ;
php_stream_close ( xparam - > outputs . client ) ;
xparam - > outputs . client = NULL ;
xparam - > outputs . returncode = - 1 ;
}
}
2003-02-28 19:53:21 +00:00
}
return xparam - > outputs . client = = NULL ? - 1 : 0 ;
}
2014-03-04 01:57:33 +00:00
2003-02-27 17:43:38 +00:00
static int php_openssl_sockop_set_option ( php_stream * stream , int option , int value , void * ptrparam TSRMLS_DC )
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
php_stream_xport_crypto_param * cparam = ( php_stream_xport_crypto_param * ) ptrparam ;
php_stream_xport_param * xparam = ( php_stream_xport_param * ) ptrparam ;
switch ( option ) {
2003-11-27 17:40:16 +00:00
case PHP_STREAM_OPTION_CHECK_LIVENESS :
{
2003-11-28 23:20:23 +00:00
struct timeval tv ;
2003-11-27 17:40:16 +00:00
char buf ;
int alive = 1 ;
2004-09-17 12:44:56 +00:00
if ( value = = - 1 ) {
if ( sslsock - > s . timeout . tv_sec = = - 1 ) {
tv . tv_sec = FG ( default_socket_timeout ) ;
tv . tv_usec = 0 ;
} else {
2007-03-14 19:22:14 +00:00
tv = sslsock - > connect_timeout ;
2004-09-17 12:44:56 +00:00
}
2003-11-28 23:20:23 +00:00
} else {
2004-09-17 12:44:56 +00:00
tv . tv_sec = value ;
tv . tv_usec = 0 ;
2003-11-28 23:20:23 +00:00
}
2003-11-27 17:40:16 +00:00
if ( sslsock - > s . socket = = - 1 ) {
alive = 0 ;
2004-09-17 12:44:56 +00:00
} else if ( php_pollfd_for ( sslsock - > s . socket , PHP_POLLREADABLE | POLLPRI , & tv ) > 0 ) {
if ( sslsock - > ssl_active ) {
int n ;
do {
n = SSL_peek ( sslsock - > ssl_handle , & buf , sizeof ( buf ) ) ;
if ( n < = 0 ) {
int err = SSL_get_error ( sslsock - > ssl_handle , n ) ;
if ( err = = SSL_ERROR_SYSCALL ) {
alive = php_socket_errno ( ) = = EAGAIN ;
break ;
2003-11-27 17:40:16 +00:00
}
2004-09-17 12:44:56 +00:00
if ( err = = SSL_ERROR_WANT_READ | | err = = SSL_ERROR_WANT_WRITE ) {
/* re-negotiate */
continue ;
}
/* any other problem is a fatal error */
alive = 0 ;
}
/* either peek succeeded or there was an error; we
* have set the alive flag appropriately */
break ;
} while ( 1 ) ;
} else if ( 0 = = recv ( sslsock - > s . socket , & buf , sizeof ( buf ) , MSG_PEEK ) & & php_socket_errno ( ) ! = EAGAIN ) {
alive = 0 ;
2003-11-27 17:40:16 +00:00
}
}
return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR ;
}
2003-02-27 17:43:38 +00:00
case PHP_STREAM_OPTION_CRYPTO_API :
switch ( cparam - > op ) {
case STREAM_XPORT_CRYPTO_OP_SETUP :
cparam - > outputs . returncode = php_openssl_setup_crypto ( stream , sslsock , cparam TSRMLS_CC ) ;
return PHP_STREAM_OPTION_RETURN_OK ;
2003-02-27 23:50:55 +00:00
break ;
2003-02-27 17:43:38 +00:00
case STREAM_XPORT_CRYPTO_OP_ENABLE :
cparam - > outputs . returncode = php_openssl_enable_crypto ( stream , sslsock , cparam TSRMLS_CC ) ;
return PHP_STREAM_OPTION_RETURN_OK ;
2003-02-27 23:50:55 +00:00
break ;
2003-02-27 17:43:38 +00:00
default :
/* fall through */
2003-02-27 23:50:55 +00:00
break ;
2003-02-27 17:43:38 +00:00
}
break ;
case PHP_STREAM_OPTION_XPORT_API :
switch ( xparam - > op ) {
case STREAM_XPORT_OP_CONNECT :
case STREAM_XPORT_OP_CONNECT_ASYNC :
/* TODO: Async connects need to check the enable_on_connect option when
* we notice that the connect has actually been established */
php_stream_socket_ops . set_option ( stream , option , value , ptrparam TSRMLS_CC ) ;
2009-07-28 19:28:08 +00:00
if ( ( sslsock - > enable_on_connect ) & &
( ( xparam - > outputs . returncode = = 0 ) | |
( xparam - > op = = STREAM_XPORT_OP_CONNECT_ASYNC & &
xparam - > outputs . returncode = = 1 & & xparam - > outputs . error_code = = EINPROGRESS ) ) )
{
2003-02-27 17:43:38 +00:00
if ( php_stream_xport_crypto_setup ( stream , sslsock - > method , NULL TSRMLS_CC ) < 0 | |
php_stream_xport_crypto_enable ( stream , 1 TSRMLS_CC ) < 0 ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " Failed to enable crypto " ) ;
xparam - > outputs . returncode = - 1 ;
}
}
return PHP_STREAM_OPTION_RETURN_OK ;
2003-02-28 19:53:21 +00:00
case STREAM_XPORT_OP_ACCEPT :
/* we need to copy the additional fields that the underlying tcp transport
* doesn ' t know about */
xparam - > outputs . returncode = php_openssl_tcp_sockop_accept ( stream , sslsock , xparam STREAMS_CC TSRMLS_CC ) ;
2004-04-21 23:02:06 +00:00
2003-02-28 19:53:21 +00:00
return PHP_STREAM_OPTION_RETURN_OK ;
2003-02-27 17:43:38 +00:00
default :
/* fall through */
2003-02-27 23:50:55 +00:00
break ;
2003-02-27 17:43:38 +00:00
}
}
return php_stream_socket_ops . set_option ( stream , option , value , ptrparam TSRMLS_CC ) ;
}
static int php_openssl_sockop_cast ( php_stream * stream , int castas , void * * ret TSRMLS_DC )
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
switch ( castas ) {
case PHP_STREAM_AS_STDIO :
if ( sslsock - > ssl_active ) {
return FAILURE ;
}
if ( ret ) {
* ret = fdopen ( sslsock - > s . socket , stream - > mode ) ;
if ( * ret ) {
return SUCCESS ;
}
return FAILURE ;
}
return SUCCESS ;
2003-11-27 17:40:16 +00:00
2003-09-21 18:02:08 +00:00
case PHP_STREAM_AS_FD_FOR_SELECT :
2003-11-27 17:40:16 +00:00
if ( ret ) {
2014-08-23 00:40:19 +00:00
if ( sslsock - > ssl_active ) {
/* OpenSSL has an internal buffer which select() cannot see. If we don't
fetch it into the stream ' s buffer , no activity will be reported on the
stream even though there is data waiting to be read - but we only fetch
the number of bytes OpenSSL has ready to give us since we weren ' t asked
for any data at this stage . This is only likely to cause issues with
non - blocking streams , but it ' s harmless to always do it . */
int bytes ;
while ( ( bytes = SSL_pending ( sslsock - > ssl_handle ) ) > 0 ) {
php_stream_fill_read_buffer ( stream , ( size_t ) bytes ) ;
}
}
* ( int * ) ret = sslsock - > s . socket ;
2003-11-27 17:40:16 +00:00
}
return SUCCESS ;
2003-02-27 17:43:38 +00:00
case PHP_STREAM_AS_FD :
case PHP_STREAM_AS_SOCKETD :
if ( sslsock - > ssl_active ) {
return FAILURE ;
}
if ( ret ) {
2013-12-12 09:17:01 +00:00
* ( php_socket_t * ) ret = sslsock - > s . socket ;
2003-02-27 17:43:38 +00:00
}
return SUCCESS ;
default :
return FAILURE ;
}
}
php_stream_ops php_openssl_socket_ops = {
php_openssl_sockop_write , php_openssl_sockop_read ,
php_openssl_sockop_close , php_openssl_sockop_flush ,
" tcp_socket/ssl " ,
NULL , /* seek */
php_openssl_sockop_cast ,
php_openssl_sockop_stat ,
php_openssl_sockop_set_option ,
} ;
2014-02-11 15:12:01 +00:00
static long get_crypto_method ( php_stream_context * ctx , long crypto_method )
{
zval * * val ;
if ( ctx & & php_stream_context_get_option ( ctx , " ssl " , " crypto_method " , & val ) = = SUCCESS ) {
convert_to_long_ex ( val ) ;
crypto_method = ( long ) Z_LVAL_PP ( val ) ;
2015-02-09 16:44:09 +00:00
crypto_method | = STREAM_CRYPTO_IS_CLIENT ;
2014-02-11 15:12:01 +00:00
}
return crypto_method ;
2013-09-21 19:26:40 +00:00
}
2014-02-11 15:12:01 +00:00
static char * get_url_name ( const char * resourcename , size_t resourcenamelen , int is_persistent TSRMLS_DC )
{
2009-11-30 13:31:53 +00:00
php_url * url ;
if ( ! resourcename ) {
return NULL ;
}
url = php_url_parse_ex ( resourcename , resourcenamelen ) ;
if ( ! url ) {
return NULL ;
}
if ( url - > host ) {
const char * host = url - > host ;
2014-01-28 17:05:56 +00:00
char * url_name = NULL ;
2009-11-30 13:31:53 +00:00
size_t len = strlen ( host ) ;
/* skip trailing dots */
while ( len & & host [ len - 1 ] = = ' . ' ) {
- - len ;
}
if ( len ) {
2014-01-28 17:05:56 +00:00
url_name = pestrndup ( host , len , is_persistent ) ;
2009-11-30 13:31:53 +00:00
}
php_url_free ( url ) ;
2014-01-28 17:05:56 +00:00
return url_name ;
2009-11-30 13:31:53 +00:00
}
php_url_free ( url ) ;
return NULL ;
}
2003-02-27 17:43:38 +00:00
2013-07-30 10:49:36 +00:00
php_stream * php_openssl_ssl_socket_factory ( const char * proto , size_t protolen ,
const char * resourcename , size_t resourcenamelen ,
2003-02-27 17:43:38 +00:00
const char * persistent_id , int options , int flags ,
struct timeval * timeout ,
php_stream_context * context STREAMS_DC TSRMLS_DC )
{
php_stream * stream = NULL ;
php_openssl_netstream_data_t * sslsock = NULL ;
2014-02-11 15:12:01 +00:00
2003-02-27 17:43:38 +00:00
sslsock = pemalloc ( sizeof ( php_openssl_netstream_data_t ) , persistent_id ? 1 : 0 ) ;
2003-02-28 19:53:21 +00:00
memset ( sslsock , 0 , sizeof ( * sslsock ) ) ;
2003-02-27 17:43:38 +00:00
sslsock - > s . is_blocked = 1 ;
2007-03-14 19:22:14 +00:00
/* this timeout is used by standard stream funcs, therefor it should use the default value */
sslsock - > s . timeout . tv_sec = FG ( default_socket_timeout ) ;
sslsock - > s . timeout . tv_usec = 0 ;
/* use separate timeout for our private funcs */
sslsock - > connect_timeout . tv_sec = timeout - > tv_sec ;
sslsock - > connect_timeout . tv_usec = timeout - > tv_usec ;
2003-02-27 17:43:38 +00:00
/* we don't know the socket until we have determined if we are binding or
* connecting */
sslsock - > s . socket = - 1 ;
2014-02-11 15:12:01 +00:00
2009-08-22 02:31:23 +00:00
/* Initialize context as NULL */
sslsock - > ctx = NULL ;
2014-02-11 15:12:01 +00:00
2003-02-27 17:43:38 +00:00
stream = php_stream_alloc_rel ( & php_openssl_socket_ops , sslsock , persistent_id , " r+ " ) ;
if ( stream = = NULL ) {
pefree ( sslsock , persistent_id ? 1 : 0 ) ;
return NULL ;
}
if ( strncmp ( proto , " ssl " , protolen ) = = 0 ) {
sslsock - > enable_on_connect = 1 ;
2014-02-11 15:12:01 +00:00
sslsock - > method = get_crypto_method ( context , STREAM_CRYPTO_METHOD_ANY_CLIENT ) ;
2004-09-10 11:43:47 +00:00
} else if ( strncmp ( proto , " sslv2 " , protolen ) = = 0 ) {
2011-04-24 23:27:48 +00:00
# ifdef OPENSSL_NO_SSL2
2011-04-25 16:50:30 +00:00
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSLv2 support is not compiled into the OpenSSL library PHP is linked against " ) ;
2011-04-24 23:27:48 +00:00
return NULL ;
# else
2004-09-10 11:43:47 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT ;
2011-04-24 23:27:48 +00:00
# endif
2004-09-10 11:43:47 +00:00
} else if ( strncmp ( proto , " sslv3 " , protolen ) = = 0 ) {
2014-08-25 16:16:38 +00:00
# ifdef OPENSSL_NO_SSL3
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " SSLv3 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# else
2004-09-10 11:43:47 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT ;
2014-08-25 16:16:38 +00:00
# endif
2003-02-27 17:43:38 +00:00
} else if ( strncmp ( proto , " tls " , protolen ) = = 0 ) {
sslsock - > enable_on_connect = 1 ;
2014-02-11 15:12:01 +00:00
sslsock - > method = get_crypto_method ( context , STREAM_CRYPTO_METHOD_TLS_CLIENT ) ;
} else if ( strncmp ( proto , " tlsv1.0 " , protolen ) = = 0 ) {
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT ;
2013-10-08 17:54:22 +00:00
} else if ( strncmp ( proto , " tlsv1.1 " , protolen ) = = 0 ) {
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT ;
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " TLSv1.1 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# endif
} else if ( strncmp ( proto , " tlsv1.2 " , protolen ) = = 0 ) {
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT ;
# else
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " TLSv1.2 support is not compiled into the OpenSSL library PHP is linked against " ) ;
return NULL ;
# endif
2003-02-27 17:43:38 +00:00
}
2014-01-28 17:05:56 +00:00
sslsock - > url_name = get_url_name ( resourcename , resourcenamelen , ! ! persistent_id TSRMLS_CC ) ;
2003-02-27 17:43:38 +00:00
return stream ;
}
/*
* Local variables :
* tab - width : 4
* c - basic - offset : 4
* End :
* vim600 : noet sw = 4 ts = 4 fdm = marker
* vim < 600 : noet sw = 4 ts = 4
*/