2003-02-27 17:43:38 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2014-09-19 16:33:14 +00:00
| PHP Version 7 |
2003-02-27 17:43:38 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2015-01-15 15:27:30 +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"
2014-09-20 20:42:02 +00:00
# include "zend_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>
2015-02-28 20:23:59 +00:00
# include <openssl/rsa.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>
2015-02-28 22:11:30 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x10002000L
# include <openssl/bn.h>
# include <openssl/dh.h>
# endif
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
2015-02-28 20:23:59 +00:00
/* OpenSSL 1.0.2 removes SSLv2 support entirely*/
# if OPENSSL_VERSION_NUMBER < 0x10002000L && !defined(OPENSSL_NO_SSL2)
# define HAVE_SSL2 1
# endif
# ifndef OPENSSL_NO_SSL3
# define HAVE_SSL3 1
# endif
# if OPENSSL_VERSION_NUMBER >= 0x10001001L
# define HAVE_TLS11 1
# define HAVE_TLS12 1
# 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
2015-02-28 20:23:59 +00:00
# if !defined(OPENSSL_NO_TLSEXT)
# if OPENSSL_VERSION_NUMBER >= 0x00908070L
# define HAVE_TLS_SNI 1
2014-02-11 15:42:34 +00:00
# endif
2015-02-28 20:35:25 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x10002000L
# define HAVE_TLS_ALPN 1
# endif
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 */
2014-08-07 11:55:37 +00:00
# define GET_VER_OPT(name) (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL)
2014-05-06 21:09:42 +00:00
# define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_P(val); }
2014-08-25 19:51:49 +00:00
# define GET_VER_OPT_LONG(name, num) if (GET_VER_OPT(name)) { convert_to_long_ex(val); num = Z_LVAL_P(val); }
2014-03-04 01:57:33 +00:00
/* 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)))
2015-02-28 22:11:30 +00:00
# ifndef OPENSSL_NO_RSA
static RSA * tmp_rsa_cb ( SSL * s , int is_export , int keylength ) ;
# endif
2014-02-21 22:09:16 +00:00
extern php_stream * php_openssl_get_stream_from_ssl_handle ( const SSL * ssl ) ;
2014-12-13 22:06:14 +00:00
extern zend_string * php_openssl_x509_fingerprint ( X509 * peer , const char * method , zend_bool raw ) ;
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-21 15:20:32 +00:00
static struct timeval subtract_timeval ( struct timeval a , struct timeval b ) ;
static int compare_timeval ( struct timeval a , struct timeval b ) ;
2015-01-23 15:03:20 +00:00
static size_t php_openssl_sockop_io ( int read , php_stream * stream , char * buf , size_t count ) ;
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 {
2014-08-25 17:24:55 +00:00
zend_long prev_handshake ;
zend_long limit ;
zend_long window ;
2014-03-04 01:57:33 +00:00
float tokens ;
unsigned should_close ;
} php_openssl_handshake_bucket_t ;
2015-02-28 20:35:25 +00:00
# ifdef HAVE_TLS_ALPN
/* Holds the available server ALPN protocols for negotiation */
typedef struct _php_openssl_alpn_ctx_t {
2015-02-28 22:11:30 +00:00
unsigned char * data ;
unsigned short len ;
2015-02-28 20:35:25 +00:00
} php_openssl_alpn_ctx ;
# endif
2014-03-04 01:57:33 +00:00
/* 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 ;
2015-02-28 20:35:25 +00:00
# ifdef HAVE_TLS_ALPN
php_openssl_alpn_ctx * alpn_ctx ;
# endif
2014-03-04 01:57:33 +00:00
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-12-13 22:06:14 +00:00
static int is_http_stream_talking_to_iis ( php_stream * stream ) /* { { { */
2004-05-23 10:36:08 +00:00
{
2014-05-06 21:09:42 +00:00
if ( Z_TYPE ( stream - > wrapperdata ) = = IS_ARRAY & & 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 */
2014-05-06 21:09:42 +00:00
zval * tmp ;
2004-05-23 10:36:08 +00:00
# define SERVER_MICROSOFT_IIS "Server: Microsoft-IIS"
2006-10-05 00:38:02 +00:00
# define SERVER_GOOGLE "Server: GFE / "
2015-01-03 09:22:58 +00:00
2014-05-06 21:09:42 +00:00
ZEND_HASH_FOREACH_VAL ( Z_ARRVAL ( stream - > wrapperdata ) , tmp ) {
if ( strncasecmp ( Z_STRVAL_P ( tmp ) , SERVER_MICROSOFT_IIS , sizeof ( SERVER_MICROSOFT_IIS ) - 1 ) = = 0 ) {
2004-05-23 10:36:08 +00:00
return 1 ;
2014-05-06 21:09:42 +00:00
} else if ( strncasecmp ( Z_STRVAL_P ( tmp ) , SERVER_GOOGLE , sizeof ( SERVER_GOOGLE ) - 1 ) = = 0 ) {
2006-10-05 00:38:02 +00:00
return 1 ;
2004-05-23 10:36:08 +00:00
}
2014-05-06 21:09:42 +00:00
} ZEND_HASH_FOREACH_END ( ) ;
2004-05-23 10:36:08 +00:00
}
return 0 ;
}
2014-03-04 01:57:33 +00:00
/* }}} */
2004-05-23 10:36:08 +00:00
2014-12-13 22:06:14 +00:00
static int handle_ssl_error ( php_stream * stream , int nr_bytes , zend_bool is_init ) /* { { { */
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 } ;
2015-01-30 12:52:11 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
if ( ! is_http_stream_talking_to_iis ( stream ) & & ERR_get_error ( ) ! = 0 ) {
php_error_docref ( NULL , E_WARNING ,
2004-05-23 10:36:08 +00:00
" 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 ) ;
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2003-02-27 17:43:38 +00:00
" SSL: %s " , estr ) ;
efree ( estr ) ;
retry = 0 ;
}
break ;
}
2004-04-21 23:02:06 +00:00
2015-01-03 09:22:58 +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 :
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , 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) " ) ;
2004-04-21 23:02:06 +00:00
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 ) ) ;
2014-05-06 21:09:42 +00:00
if ( ebuf . s ) {
2008-12-08 11:54:22 +00:00
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 ) ;
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2004-04-21 23:02:06 +00:00
" SSL operation failed with code %d. %s%s " ,
err ,
2014-05-06 21:09:42 +00:00
ebuf . s ? " OpenSSL Error messages: \n " : " " ,
ebuf . s ? ebuf . s - > val : " " ) ;
if ( ebuf . s ) {
2008-12-08 11:54:22 +00:00
smart_str_free ( & ebuf ) ;
2007-04-04 10:44:55 +00:00
}
2004-04-21 23:02:06 +00:00
}
2015-01-03 09:22:58 +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 ;
2014-05-06 21:09:42 +00:00
zval * val ;
2014-08-25 17:24:55 +00:00
zend_ulong allowed_depth = OPENSSL_DEFAULT_STREAM_VERIFY_DEPTH ;
2014-03-04 01:57:33 +00:00
2014-03-05 17:23:54 +00:00
2014-03-04 01:57:33 +00:00
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 ) ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
/* 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 ( ) ) ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
/* 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 " ) & &
2014-12-13 22:06:14 +00:00
zend_is_true ( val )
2014-03-04 01:57:33 +00:00
) {
ret = 1 ;
}
/* check the depth */
GET_VER_OPT_LONG ( " verify_depth " , allowed_depth ) ;
2014-08-25 17:24:55 +00:00
if ( ( zend_ulong ) depth > allowed_depth ) {
2014-03-04 01:57:33 +00:00
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-12-13 22:06:14 +00:00
static int php_x509_fingerprint_cmp ( X509 * peer , const char * method , const char * expected )
2014-07-31 04:17:17 +00:00
{
2014-08-13 15:43:34 +00:00
zend_string * fingerprint ;
2014-07-31 04:17:17 +00:00
int result = - 1 ;
2014-12-13 22:06:14 +00:00
fingerprint = php_openssl_x509_fingerprint ( peer , method , 0 ) ;
2014-08-13 15:43:34 +00:00
if ( fingerprint ) {
2015-03-05 06:00:59 +00:00
result = strcasecmp ( expected , fingerprint - > val ) ;
2014-08-25 17:24:55 +00:00
zend_string_release ( fingerprint ) ;
2014-07-31 04:17:17 +00:00
}
return result ;
}
2014-12-13 22:06:14 +00:00
static zend_bool php_x509_fingerprint_match ( X509 * peer , zval * val )
2014-07-31 04:17:17 +00:00
{
if ( Z_TYPE_P ( val ) = = IS_STRING ) {
const char * method = NULL ;
2014-08-25 17:24:55 +00:00
switch ( Z_STRLEN_P ( val ) ) {
2014-07-31 04:17:17 +00:00
case 32 :
method = " md5 " ;
break ;
case 40 :
method = " sha1 " ;
break ;
}
2014-12-13 22:06:14 +00:00
return method & & php_x509_fingerprint_cmp ( peer , method , Z_STRVAL_P ( val ) ) = = 0 ;
2014-07-31 04:17:17 +00:00
} else if ( Z_TYPE_P ( val ) = = IS_ARRAY ) {
2014-08-04 09:56:27 +00:00
zval * current ;
zend_string * key ;
2014-07-31 04:17:17 +00:00
2015-03-04 17:54:34 +00:00
if ( ! zend_hash_num_elements ( Z_ARRVAL_P ( val ) ) ) {
php_error_docref ( NULL , E_WARNING , " Invalid peer_fingerprint array; [algo => fingerprint] form required " ) ;
return 0 ;
}
2014-08-04 09:56:27 +00:00
ZEND_HASH_FOREACH_STR_KEY_VAL ( Z_ARRVAL_P ( val ) , key , current ) {
2015-03-04 19:50:35 +00:00
if ( key = = NULL | | Z_TYPE_P ( current ) ! = IS_STRING ) {
2015-03-04 17:54:34 +00:00
php_error_docref ( NULL , E_WARNING , " Invalid peer_fingerprint array; [algo => fingerprint] form required " ) ;
return 0 ;
}
2015-03-04 19:50:35 +00:00
if ( php_x509_fingerprint_cmp ( peer , key - > val , Z_STRVAL_P ( current ) ) ! = 0 ) {
2014-07-31 04:17:17 +00:00
return 0 ;
}
2014-08-04 09:56:27 +00:00
} ZEND_HASH_FOREACH_END ( ) ;
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 {
php_error_docref ( NULL , E_WARNING ,
2015-03-05 19:24:04 +00:00
" 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 ;
2015-01-30 12:52:11 +00:00
ptrdiff_t prefix_len ;
size_t 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
2014-12-13 22:06:14 +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?
2015-03-05 05:42:25 +00:00
* * 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
}
/* }}} */
2014-12-13 22:06:14 +00:00
static zend_bool matches_common_name ( X509 * peer , const char * subject_name ) /* { { { */
2014-03-04 01:57:33 +00:00
{
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to locate peer certificate CN " ) ;
2014-03-04 01:57:33 +00:00
} else if ( cert_name_len ! = strlen ( buf ) ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Peer certificate CN=`%.*s' is malformed " , cert_name_len , buf ) ;
2014-03-04 01:57:33 +00:00
} else if ( matches_wildcard_name ( subject_name , buf ) ) {
is_match = 1 ;
} else {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , 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-12-13 22:06:14 +00:00
static int apply_peer_verification_policy ( SSL * ssl , X509 * peer , php_stream * stream ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val = NULL ;
2014-03-04 01:57:33 +00:00
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 " )
2014-12-13 22:06:14 +00:00
? zend_is_true ( val )
2014-03-04 01:57:33 +00:00
: 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 " ) )
2014-12-13 22:06:14 +00:00
? zend_is_true ( val )
2014-03-04 01:57:33 +00:00
: 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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Could not get peer certificate " ) ;
2014-03-04 01:57:33 +00:00
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 :
2014-12-13 22:06:14 +00:00
if ( GET_VER_OPT ( " allow_self_signed " ) & & zend_is_true ( val ) ) {
2014-03-04 01:57:33 +00:00
/* allowed */
break ;
}
/* not allowed, so fall through */
default :
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-04 01:57:33 +00:00
" 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 ) {
2014-05-06 21:09:42 +00:00
if ( Z_TYPE_P ( val ) = = IS_STRING | | Z_TYPE_P ( val ) = = IS_ARRAY ) {
2014-12-13 22:06:14 +00:00
if ( ! php_x509_fingerprint_match ( peer , val ) ) {
php_error_docref ( NULL , 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 {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-04 01:57:33 +00:00
" 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 ) {
2014-12-13 22:06:14 +00:00
if ( matches_san_list ( peer , peer_name ) ) {
2014-03-04 01:57:33 +00:00
return SUCCESS ;
2014-12-13 22:06:14 +00:00
} else if ( matches_common_name ( peer , peer_name ) ) {
2014-03-04 01:57:33 +00:00
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 ;
2014-05-06 21:09:42 +00:00
zval * val = NULL ;
2014-03-04 01:57:33 +00:00
char * passphrase = NULL ;
/* TODO: could expand this to make a callback into PHP user-space */
GET_VER_OPT_STRING ( " passphrase " , passphrase ) ;
if ( passphrase ) {
2014-08-25 17:24:55 +00:00
if ( Z_STRLEN_P ( val ) < num - 1 ) {
memcpy ( buf , Z_STRVAL_P ( val ) , Z_STRLEN_P ( val ) + 1 ) ;
2015-01-30 12:52:11 +00:00
return ( int ) Z_STRLEN_P ( val ) ;
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
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-08-19 05:44:03 +00:00
zval * val ;
2014-03-04 01:57:33 +00:00
zend_bool is_self_signed = 0 ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
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 ;
}
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Error encoding X509 certificate: %d: %s " , err_code , ERR_error_string ( err_code , err_buf ) ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Error creating certificate context: %s " , php_win_err ( ) ) ;
2014-03-04 01:57:33 +00:00
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 ) ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Error getting certificate chain: %s " , php_win_err ( ) ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to locate certificate CN " ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to convert %s to wide character string " , cert_name_utf8 ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to convert %s to wide character string " , cert_name_utf8 ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Error verifying certificate chain policy: %s " , php_win_err ( ) ) ;
2014-03-04 01:57:33 +00:00
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
2014-12-13 22:06:14 +00:00
& & GET_VER_OPT ( " allow_self_signed " ) & & zend_is_true ( val ) ) {
2014-03-04 01:57:33 +00:00
/* 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-12-13 22:06:14 +00:00
static long load_stream_cafile ( X509_STORE * cert_store , const char * cafile ) /* { { { */
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 ;
}
/* }}} */
2014-12-13 22:06:14 +00:00
static int enable_peer_verification ( SSL_CTX * ctx , php_stream * stream ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val = NULL ;
2014-03-04 01:57:33 +00:00
char * cafile = NULL ;
char * capath = NULL ;
GET_VER_OPT_STRING ( " cafile " , cafile ) ;
GET_VER_OPT_STRING ( " capath " , capath ) ;
if ( ! cafile ) {
2014-05-06 21:09:42 +00:00
cafile = zend_ini_string ( " openssl.cafile " , sizeof ( " openssl.cafile " ) - 1 , 0 ) ;
2014-03-04 01:57:33 +00:00
cafile = strlen ( cafile ) ? cafile : NULL ;
}
if ( ! capath ) {
2014-05-06 21:09:42 +00:00
capath = zend_ini_string ( " openssl.capath " , sizeof ( " openssl.capath " ) - 1 , 0 ) ;
2014-03-04 01:57:33 +00:00
capath = strlen ( capath ) ? capath : NULL ;
}
if ( cafile | | capath ) {
if ( ! SSL_CTX_load_verify_locations ( ctx , cafile , capath ) ) {
2014-12-13 22:06:14 +00:00
if ( cafile & & ! load_stream_cafile ( SSL_CTX_get_cert_store ( ctx ) , cafile ) ) {
2014-03-04 01:57:33 +00:00
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 ) ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-04 01:57:33 +00:00
" 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 ;
}
/* }}} */
2014-12-13 22:06:14 +00:00
static void disable_peer_verification ( SSL_CTX * ctx , php_stream * stream ) /* { { { */
2014-03-04 01:57:33 +00:00
{
SSL_CTX_set_verify ( ctx , SSL_VERIFY_NONE , NULL ) ;
}
/* }}} */
2014-02-20 23:26:55 +00:00
2014-12-13 22:06:14 +00:00
static int set_local_cert ( SSL_CTX * ctx , php_stream * stream ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val = NULL ;
2014-03-04 01:57:33 +00:00
char * certfile = NULL ;
2014-02-20 23:26:55 +00:00
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , 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 ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to set private key file `%s' " , resolved_path_buff_pk ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
}
}
} else {
if ( SSL_CTX_use_PrivateKey_file ( ctx , resolved_path_buff , SSL_FILETYPE_PEM ) ! = 1 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Unable to set private key file `%s' " , resolved_path_buff ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
2015-01-03 09:22:58 +00:00
}
2014-03-04 01:57:33 +00:00
}
# 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 ) ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " Private key does not match certificate! " ) ;
2014-03-04 01:57:33 +00:00
}
}
}
return SUCCESS ;
2014-02-20 23:26:55 +00:00
}
/* }}} */
2003-11-27 17:40:16 +00:00
2014-12-13 22:06:14 +00:00
static const SSL_METHOD * php_select_crypto_method ( zend_long method_value , int is_client ) /* { { { */
2014-02-11 15:12:01 +00:00
{
if ( method_value = = STREAM_CRYPTO_METHOD_SSLv2 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_SSL2
return is_client ? ( SSL_METHOD * ) SSLv2_client_method ( ) : ( SSL_METHOD * ) SSLv2_server_method ( ) ;
2014-02-11 15:12:01 +00:00
# else
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2015-02-28 20:23:59 +00:00
" SSLv2 unavailable in the OpenSSL library against which PHP is linked " ) ;
2014-02-11 15:12:01 +00:00
return NULL ;
# endif
} else if ( method_value = = STREAM_CRYPTO_METHOD_SSLv3 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_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
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2015-02-28 20:23:59 +00:00
" SSLv3 unavailable in the OpenSSL library against which PHP is linked " ) ;
2014-08-25 16:16:38 +00:00
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 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS11
2014-02-11 15:12:01 +00:00
return is_client ? TLSv1_1_client_method ( ) : TLSv1_1_server_method ( ) ;
# else
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2015-02-28 20:23:59 +00:00
" TLSv1.1 unavailable in the OpenSSL library against which PHP is linked " ) ;
2014-02-11 15:12:01 +00:00
return NULL ;
# endif
} else if ( method_value = = STREAM_CRYPTO_METHOD_TLSv1_2 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS12
2014-02-11 15:12:01 +00:00
return is_client ? TLSv1_2_client_method ( ) : TLSv1_2_server_method ( ) ;
# else
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2015-02-28 20:23:59 +00:00
" TLSv1.2 unavailable in the OpenSSL library against which PHP is linked " ) ;
2014-02-11 15:12:01 +00:00
return NULL ;
# endif
} else {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-02-11 15:12:01 +00:00
" Invalid crypto method " ) ;
return NULL ;
}
}
2014-03-04 01:57:33 +00:00
/* }}} */
2014-02-11 15:12:01 +00:00
2015-01-30 13:36:33 +00:00
static int php_get_crypto_method_ctx_flags ( int method_flags ) /* { { { */
2014-02-11 15:12:01 +00:00
{
2015-01-30 13:36:33 +00:00
int ssl_ctx_options = SSL_OP_ALL ;
2014-02-11 15:12:01 +00:00
2015-02-28 22:11:30 +00:00
# ifdef HAVE_SSL2
2014-02-11 15:12:01 +00:00
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_SSLv2 ) ) {
ssl_ctx_options | = SSL_OP_NO_SSLv2 ;
}
# endif
2015-02-28 22:11:30 +00:00
# ifdef HAVE_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
2014-02-11 15:12:01 +00:00
if ( ! ( method_flags & STREAM_CRYPTO_METHOD_TLSv1_0 ) ) {
ssl_ctx_options | = SSL_OP_NO_TLSv1 ;
}
2015-02-28 22:11:30 +00:00
# ifdef HAVE_TLS11
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 ;
}
2015-02-28 22:11:30 +00:00
# endif
# ifdef HAVE_TLS12
2014-02-11 15:12:01 +00:00
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 ;
2014-08-25 17:24:55 +00:00
zend_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 ) {
2014-05-06 21:09:42 +00:00
zval * val ;
2003-02-27 17:43:38 +00:00
2015-01-03 09:22:58 +00:00
2014-03-04 01:57:33 +00:00
sslsock - > reneg - > should_close = 1 ;
2010-12-03 09:34:35 +00:00
2014-08-07 11:55:37 +00:00
if ( PHP_STREAM_CONTEXT ( stream ) & & ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2014-05-06 21:09:42 +00:00
" ssl " , " reneg_limit_callback " ) ) ! = NULL
2014-01-31 21:18:31 +00:00
) {
2014-05-06 21:09:42 +00:00
zval param , retval ;
2013-01-30 19:45:31 +00:00
2014-05-06 21:09:42 +00:00
php_stream_to_zval ( stream , & 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 ;
2014-12-13 22:06:14 +00:00
if ( FAILURE = = call_user_function_ex ( EG ( function_table ) , NULL , val , & retval , 1 , & param , 0 , NULL ) ) {
2014-03-04 01:57:33 +00:00
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 */
2014-05-06 21:09:42 +00:00
if ( Z_TYPE ( retval ) = = IS_TRUE ) {
2014-03-04 01:57:33 +00:00
sslsock - > reneg - > should_close = 0 ;
}
2014-02-11 15:42:34 +00:00
2014-05-06 21:09:42 +00:00
zval_ptr_dtor ( & retval ) ;
2010-06-20 16:33:16 +00:00
} else {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-04 01:57:33 +00:00
" SSL: client-initiated handshake rate limit exceeded by peer " ) ;
2010-06-20 16:33:16 +00:00
}
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-11 15:12:01 +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 ) /* { { { */
{
2014-05-06 21:09:42 +00:00
zval * val ;
2014-08-25 17:24:55 +00:00
zend_long limit = OPENSSL_DEFAULT_RENEG_LIMIT ;
zend_long window = OPENSSL_DEFAULT_RENEG_WINDOW ;
2014-02-11 13:24:34 +00:00
2014-08-07 11:55:37 +00:00
if ( PHP_STREAM_CONTEXT ( stream ) & &
NULL ! = ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2014-05-06 21:09:42 +00:00
" ssl " , " reneg_limit " ) )
2014-03-04 01:57:33 +00:00
) {
2014-08-25 19:51:49 +00:00
convert_to_long ( val ) ;
2014-08-25 17:24:55 +00:00
limit = Z_LVAL_P ( 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-08-07 11:55:37 +00:00
if ( PHP_STREAM_CONTEXT ( stream ) & &
NULL ! = ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2014-05-06 21:09:42 +00:00
" ssl " , " reneg_window " ) )
2014-03-04 01:57:33 +00:00
) {
2014-08-25 19:51:49 +00:00
convert_to_long ( val ) ;
2014-08-25 17:24:55 +00:00
window = Z_LVAL_P ( 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
2015-02-28 22:11:30 +00:00
# ifndef OPENSSL_NO_RSA
static RSA * tmp_rsa_cb ( SSL * s , int is_export , int keylength )
2014-02-11 15:42:34 +00:00
{
2015-02-28 22:11:30 +00:00
BIGNUM * bn = NULL ;
static RSA * rsa_tmp = NULL ;
2014-02-11 15:42:34 +00:00
2015-02-28 22:11:30 +00:00
if ( ! rsa_tmp & & ( ( bn = BN_new ( ) ) = = NULL ) ) {
php_error_docref ( NULL , E_WARNING , " allocation error generating RSA key " ) ;
2014-02-11 15:42:34 +00:00
}
2015-02-28 22:11:30 +00:00
if ( ! rsa_tmp & & bn ) {
if ( ! BN_set_word ( bn , RSA_F4 ) | | ( ( rsa_tmp = RSA_new ( ) ) = = NULL ) | |
! RSA_generate_key_ex ( rsa_tmp , keylength , bn , NULL ) ) {
if ( rsa_tmp ) {
RSA_free ( rsa_tmp ) ;
}
rsa_tmp = NULL ;
}
BN_free ( bn ) ;
2014-02-11 15:42:34 +00:00
}
2015-02-28 22:11:30 +00:00
return ( rsa_tmp ) ;
2014-02-11 15:42:34 +00:00
}
2015-02-28 22:11:30 +00:00
# endif
2014-02-11 15:42:34 +00:00
2015-02-28 22:11:30 +00:00
# ifndef OPENSSL_NO_DH
static int set_server_dh_param ( php_stream * stream , SSL_CTX * ctx ) /* { { { */
2014-02-11 15:42:34 +00:00
{
DH * dh ;
BIO * bio ;
2015-02-28 22:11:30 +00:00
zval * zdhpath ;
zdhpath = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " dh_param " ) ;
if ( zdhpath = = NULL ) {
#if 0
/* Coming in OpenSSL 1.1 ... eventually we'll want to enable this
* in the absence of an explicit dh_param .
*/
SSL_CTX_set_dh_auto ( ctx , 1 ) ;
# endif
return SUCCESS ;
}
2014-02-11 15:42:34 +00:00
2015-02-28 22:11:30 +00:00
convert_to_string_ex ( zdhpath ) ;
bio = BIO_new_file ( Z_STRVAL_P ( zdhpath ) , " r " ) ;
2014-02-11 15:42:34 +00:00
if ( bio = = NULL ) {
2015-02-28 22:11:30 +00:00
php_error_docref ( NULL , E_WARNING , " invalid dh_param " ) ;
2014-03-04 01:57:33 +00:00
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 ) {
2015-02-28 22:11:30 +00:00
php_error_docref ( NULL , E_WARNING , " failed reading DH params " ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
}
if ( SSL_CTX_set_tmp_dh ( ctx , dh ) < 0 ) {
2015-02-28 22:11:30 +00:00
php_error_docref ( NULL , E_WARNING , " failed assigning DH params " ) ;
2014-03-04 01:57:33 +00:00
DH_free ( dh ) ;
return FAILURE ;
}
DH_free ( dh ) ;
return SUCCESS ;
}
/* }}} */
2015-02-28 22:11:30 +00:00
# endif
2014-03-04 01:57:33 +00:00
# ifdef HAVE_ECDH
2014-12-13 22:06:14 +00:00
static int set_server_ecdh_curve ( php_stream * stream , SSL_CTX * ctx ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2015-02-28 22:11:30 +00:00
zval * zvcurve ;
2014-03-04 01:57:33 +00:00
int curve_nid ;
EC_KEY * ecdh ;
2015-02-28 22:11:30 +00:00
zvcurve = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " ecdh_curve " ) ;
if ( zvcurve = = NULL ) {
# if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_ecdh_auto ( ctx , 1 ) ;
return SUCCESS ;
# else
curve_nid = NID_X9_62_prime256v1 ;
# endif
} else {
convert_to_string_ex ( zvcurve ) ;
curve_nid = OBJ_sn2nid ( Z_STRVAL_P ( zvcurve ) ) ;
2014-03-04 01:57:33 +00:00
if ( curve_nid = = NID_undef ) {
2015-02-28 22:11:30 +00:00
php_error_docref ( NULL , E_WARNING , " invalid ecdh_curve specified " ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
2014-02-11 15:12:01 +00:00
}
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
ecdh = EC_KEY_new_by_curve_name ( curve_nid ) ;
if ( ecdh = = NULL ) {
2015-02-28 22:11:30 +00:00
php_error_docref ( NULL , E_WARNING , " failed generating ECDH curve " ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
2003-02-27 17:43:38 +00:00
}
2014-03-04 01:57:33 +00:00
SSL_CTX_set_tmp_ecdh ( ctx , ecdh ) ;
EC_KEY_free ( ecdh ) ;
return SUCCESS ;
}
/* }}} */
2012-01-20 05:31:53 +00:00
# endif
2004-05-23 10:36:08 +00:00
2014-12-13 22:06:14 +00:00
static int set_server_specific_opts ( php_stream * stream , SSL_CTX * ctx ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2015-02-28 22:11:30 +00:00
zval * zv ;
2014-03-04 01:57:33 +00:00
long ssl_ctx_options = SSL_CTX_get_options ( ctx ) ;
# ifdef HAVE_ECDH
2015-02-28 22:11:30 +00:00
if ( set_server_ecdh_curve ( stream , ctx ) = = FAILURE ) {
2014-03-04 01:57:33 +00:00
return FAILURE ;
2010-12-03 09:34:35 +00:00
}
# endif
2015-02-28 22:11:30 +00:00
# ifndef OPENSSL_NO_RSA
SSL_CTX_set_tmp_rsa_callback ( ctx , tmp_rsa_cb ) ;
# endif
/* We now use tmp_rsa_cb to generate a key of appropriate size whenever necessary */
if ( php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " rsa_key_size " ) ! = NULL ) {
php_error_docref ( NULL , E_WARNING , " rsa_key_size context option has been removed " ) ;
2014-02-11 15:42:34 +00:00
}
2015-02-28 22:11:30 +00:00
# ifndef OPENSSL_NO_DH
set_server_dh_param ( stream , ctx ) ;
zv = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " single_dh_use " ) ;
if ( zv ! = NULL & & zend_is_true ( zv ) ) {
2014-03-04 01:57:33 +00:00
ssl_ctx_options | = SSL_OP_SINGLE_DH_USE ;
2014-02-11 15:42:34 +00:00
}
2015-02-28 22:11:30 +00:00
# endif
2014-02-11 15:42:34 +00:00
2015-02-28 22:11:30 +00:00
zv = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " honor_cipher_order " ) ;
if ( zv ! = NULL & & zend_is_true ( zv ) ) {
ssl_ctx_options | = SSL_OP_CIPHER_SERVER_PREFERENCE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
SSL_CTX_set_options ( ctx , ssl_ctx_options ) ;
2003-02-27 17:43:38 +00:00
2014-03-04 01:57:33 +00:00
return SUCCESS ;
}
/* }}} */
2014-02-20 03:39:57 +00:00
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS_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 ;
2014-02-11 13:24:34 +00:00
2014-03-05 15:50:23 +00:00
server_name = SSL_get_servername ( ssl_handle , TLSEXT_NAMETYPE_host_name ) ;
if ( ! server_name ) {
return SSL_TLSEXT_ERR_NOACK ;
2014-02-20 23:26:55 +00:00
}
2014-03-05 15:50:23 +00:00
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 ;
2003-02-27 17:43:38 +00:00
}
2014-03-05 15:50:23 +00:00
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 ;
2003-02-27 17:43:38 +00:00
}
}
2014-03-05 15:50:23 +00:00
return SSL_TLSEXT_ERR_NOACK ;
2003-02-27 17:43:38 +00:00
}
2014-03-05 15:50:23 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2014-12-13 22:06:14 +00:00
static int enable_server_sni ( php_stream * stream , php_openssl_netstream_data_t * sslsock )
2014-02-11 13:42:29 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val ;
zval * current ;
zend_string * key ;
2014-08-25 17:24:55 +00:00
zend_ulong key_index ;
2014-03-05 15:50:23 +00:00
int i = 0 ;
char resolved_path_buff [ MAXPATHLEN ] ;
SSL_CTX * ctx ;
/* If the stream ctx disables SNI we're finished here */
2014-12-13 22:06:14 +00:00
if ( GET_VER_OPT ( " SNI_enabled " ) & & ! zend_is_true ( val ) ) {
2014-03-05 15:50:23 +00:00
return SUCCESS ;
}
2014-02-11 13:42:29 +00:00
2014-03-05 15:50:23 +00:00
/* If no SNI cert array is specified we're finished here */
if ( ! GET_VER_OPT ( " SNI_server_certs " ) ) {
return SUCCESS ;
2014-02-11 13:42:29 +00:00
}
2014-05-06 21:09:42 +00:00
if ( Z_TYPE_P ( val ) ! = IS_ARRAY ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" SNI_server_certs requires an array mapping host names to cert paths "
) ;
return FAILURE ;
}
2014-01-28 17:05:56 +00:00
2014-05-06 21:09:42 +00:00
sslsock - > sni_cert_count = zend_hash_num_elements ( Z_ARRVAL_P ( val ) ) ;
2014-03-05 15:50:23 +00:00
if ( sslsock - > sni_cert_count = = 0 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" SNI_server_certs host cert array must not be empty "
) ;
return FAILURE ;
}
2014-01-28 17:05:56 +00:00
2014-03-05 15:50:23 +00:00
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 )
) ;
2014-02-11 15:42:34 +00:00
2015-01-30 21:49:44 +00:00
ZEND_HASH_FOREACH_KEY_VAL ( Z_ARRVAL_P ( val ) , key_index , key , current ) {
( void ) key_index ;
2014-05-06 21:09:42 +00:00
if ( ! key ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" SNI_server_certs array requires string host name keys "
) ;
return FAILURE ;
}
2014-02-11 15:42:34 +00:00
2014-05-06 21:09:42 +00:00
if ( VCWD_REALPATH ( Z_STRVAL_P ( current ) , resolved_path_buff ) ) {
2014-03-05 15:50:23 +00:00
/* 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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" 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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" failed setting private key from file `%s' " ,
resolved_path_buff
) ;
SSL_CTX_free ( ctx ) ;
return FAILURE ;
} else {
2014-05-06 21:09:42 +00:00
sslsock - > sni_certs [ i ] . name = pestrdup ( key - > val , php_stream_is_persistent ( stream ) ) ;
2014-03-05 15:50:23 +00:00
sslsock - > sni_certs [ i ] . ctx = ctx ;
+ + i ;
}
} else {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING ,
2014-03-05 15:50:23 +00:00
" failed setting local cert chain file `%s'; file not found " ,
2014-05-06 21:09:42 +00:00
Z_STRVAL_P ( current )
2014-03-05 15:50:23 +00:00
) ;
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-05-06 21:09:42 +00:00
} ZEND_HASH_FOREACH_END ( ) ;
2014-02-11 15:42:34 +00:00
2014-03-05 15:50:23 +00:00
SSL_CTX_set_tlsext_servername_callback ( sslsock - > ctx , server_sni_callback ) ;
2014-02-11 15:42:34 +00:00
2014-03-05 15:50:23 +00:00
return SUCCESS ;
}
2014-12-13 22:06:14 +00:00
static void enable_client_sni ( php_stream * stream , php_openssl_netstream_data_t * sslsock ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val ;
2014-03-04 01:57:33 +00:00
char * sni_server_name ;
/* If SNI is explicitly disabled we're finished here */
2014-12-13 22:06:14 +00:00
if ( GET_VER_OPT ( " SNI_enabled " ) & & ! zend_is_true ( val ) ) {
2014-03-04 01:57:33 +00:00
return ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
sni_server_name = sslsock - > url_name ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
GET_VER_OPT_STRING ( " peer_name " , sni_server_name ) ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
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 " ) ;
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( sni_server_name ) {
SSL_set_tlsext_host_name ( sslsock - > ssl_handle , sni_server_name ) ;
}
}
/* }}} */
# endif
2015-02-28 20:35:25 +00:00
# ifdef HAVE_TLS_ALPN
/*-
2015-02-28 22:11:30 +00:00
* parses a comma - separated list of strings into a string suitable for SSL_CTX_set_next_protos_advertised
2015-02-28 20:35:25 +00:00
* outlen : ( output ) set to the length of the resulting buffer on success .
* err : ( maybe NULL ) on failure , an error message line is written to this BIO .
2015-02-28 22:11:30 +00:00
* in : a NULL terminated string like " abc,def,ghi "
2015-02-28 20:35:25 +00:00
*
* returns : an emalloced buffer or NULL on failure .
*/
static unsigned char * alpn_protos_parse ( unsigned short * outlen , const char * in )
{
size_t len ;
unsigned char * out ;
size_t i , start = 0 ;
len = strlen ( in ) ;
if ( len > = 65535 ) {
return NULL ;
}
out = emalloc ( strlen ( in ) + 1 ) ;
if ( ! out ) {
return NULL ;
}
for ( i = 0 ; i < = len ; + + i ) {
if ( i = = len | | in [ i ] = = ' , ' ) {
if ( i - start > 255 ) {
efree ( out ) ;
return NULL ;
}
out [ start ] = i - start ;
start = i + 1 ;
} else {
out [ i + 1 ] = in [ i ] ;
}
}
* outlen = len + 1 ;
return out ;
}
static int server_alpn_callback ( SSL * ssl_handle , const unsigned char * * out , unsigned char * outlen ,
2015-02-28 22:11:30 +00:00
const unsigned char * in , unsigned int inlen , void * arg )
2015-02-28 20:35:25 +00:00
{
php_openssl_netstream_data_t * sslsock = arg ;
if ( SSL_select_next_proto
( ( unsigned char * * ) out , outlen , sslsock - > alpn_ctx - > data , sslsock - > alpn_ctx - > len , in ,
inlen ) ! = OPENSSL_NPN_NEGOTIATED ) {
return SSL_TLSEXT_ERR_NOACK ;
}
return SSL_TLSEXT_ERR_OK ;
}
# endif
2014-03-04 01:57:33 +00:00
int php_openssl_setup_crypto ( php_stream * stream ,
php_openssl_netstream_data_t * sslsock ,
php_stream_xport_crypto_param * cparam
2014-12-14 13:07:59 +00:00
) /* {{{ */
2014-02-11 15:42:34 +00:00
{
2014-03-04 01:57:33 +00:00
const SSL_METHOD * method ;
2015-01-30 13:36:33 +00:00
int ssl_ctx_options ;
int method_flags ;
2014-03-04 01:57:33 +00:00
char * cipherlist = NULL ;
2015-02-28 20:35:25 +00:00
char * alpn_protocols = NULL ;
2014-05-06 21:09:42 +00:00
zval * val ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
if ( sslsock - > ssl_handle ) {
if ( sslsock - > s . is_blocked ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " SSL/TLS already set-up for this stream " ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
} else {
return SUCCESS ;
}
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
ERR_clear_error ( ) ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
/* 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 ) ;
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
/* 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 ;
2014-12-13 22:06:14 +00:00
method = php_select_crypto_method ( method_flags , sslsock - > is_client ) ;
2014-03-04 01:57:33 +00:00
if ( method = = NULL ) {
return FAILURE ;
}
} else {
method = sslsock - > is_client ? SSLv23_client_method ( ) : SSLv23_server_method ( ) ;
2014-12-13 22:06:14 +00:00
ssl_ctx_options = php_get_crypto_method_ctx_flags ( method_flags ) ;
2014-03-04 01:57:33 +00:00
if ( ssl_ctx_options = = - 1 ) {
return FAILURE ;
}
}
2014-02-11 15:42:34 +00:00
2014-03-04 01:57:33 +00:00
# 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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " SSL context creation failure " ) ;
2014-03-04 01:57:33 +00:00
return FAILURE ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x0090806fL
2014-12-13 22:06:14 +00:00
if ( GET_VER_OPT ( " no_ticket " ) & & zend_is_true ( val ) ) {
2014-03-04 01:57:33 +00:00
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
2014-12-13 22:06:14 +00:00
if ( ! GET_VER_OPT ( " disable_compression " ) | | zend_is_true ( val ) ) {
2014-03-04 01:57:33 +00:00
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-12-13 22:06:14 +00:00
if ( GET_VER_OPT ( " verify_peer " ) & & ! zend_is_true ( val ) ) {
disable_peer_verification ( sslsock - > ctx , stream ) ;
} else if ( FAILURE = = enable_peer_verification ( sslsock - > ctx , stream ) ) {
2014-03-04 01:57:33 +00:00
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
}
2015-02-28 20:23:59 +00:00
2015-02-28 20:35:25 +00:00
GET_VER_OPT_STRING ( " alpn_protocols " , alpn_protocols ) ;
if ( alpn_protocols ) {
# ifdef HAVE_TLS_ALPN
{
unsigned short alpn_len ;
unsigned char * alpn = alpn_protos_parse ( & alpn_len , alpn_protocols ) ;
if ( alpn = = NULL ) {
php_error_docref ( NULL , E_WARNING , " Failed parsing comma-separated TLS ALPN protocol string " ) ;
SSL_CTX_free ( sslsock - > ctx ) ;
sslsock - > ctx = NULL ;
return FAILURE ;
}
if ( sslsock - > is_client ) {
SSL_CTX_set_alpn_protos ( sslsock - > ctx , alpn , alpn_len ) ;
} else {
sslsock - > alpn_ctx = ( php_openssl_alpn_ctx * ) emalloc ( sizeof ( php_openssl_alpn_ctx ) ) ;
sslsock - > alpn_ctx - > data = ( unsigned char * ) estrndup ( ( const char * ) alpn , alpn_len ) ;
sslsock - > alpn_ctx - > len = alpn_len ;
SSL_CTX_set_alpn_select_cb ( sslsock - > ctx , server_alpn_callback , sslsock ) ;
}
efree ( alpn ) ;
}
# else
php_error_docref ( NULL , E_WARNING ,
" alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked " ) ;
# endif
}
2014-12-13 22:06:14 +00:00
if ( FAILURE = = set_local_cert ( sslsock - > ctx , stream ) ) {
2014-03-04 01:57:33 +00:00
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 & &
2014-08-07 11:55:37 +00:00
PHP_STREAM_CONTEXT ( stream ) & &
2014-12-13 22:06:14 +00:00
FAILURE = = set_server_specific_opts ( stream , sslsock - > ctx )
2014-03-04 01:57:33 +00:00
) {
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 ) ;
2015-02-28 22:11:30 +00:00
2014-03-04 01:57:33 +00:00
if ( sslsock - > ssl_handle = = NULL ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " SSL handle creation failure " ) ;
2014-03-04 01:57:33 +00:00
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 ) ) {
2014-12-13 22:06:14 +00:00
handle_ssl_error ( stream , 0 , 1 ) ;
2014-03-04 01:57:33 +00:00
}
2014-02-11 15:42:34 +00:00
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS_SNI
2014-03-05 15:50:23 +00:00
/* Enable server-side SNI */
2015-02-28 22:11:30 +00:00
if ( ! sslsock - > is_client & & enable_server_sni ( stream , sslsock ) = = FAILURE ) {
2014-03-05 15:50:23 +00:00
return FAILURE ;
}
# endif
2014-03-04 01:57:33 +00:00
/* Enable server-side handshake renegotiation rate-limiting */
2015-02-28 22:11:30 +00:00
if ( ! sslsock - > is_client ) {
2014-03-04 01:57:33 +00:00
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 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " supplied session stream must be an SSL enabled stream " ) ;
2014-03-04 01:57:33 +00:00
} else if ( ( ( php_openssl_netstream_data_t * ) cparam - > inputs . session - > abstract ) - > ssl_handle = = NULL ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " supplied SSL session stream is not initialized " ) ;
2014-03-04 01:57:33 +00:00
} 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-05-06 21:09:42 +00:00
static zend_array * capture_session_meta ( SSL * ssl_handle ) /* { { { */
2014-02-11 15:42:34 +00:00
{
2014-05-06 21:09:42 +00:00
zval meta_arr ;
2014-03-04 01:57:33 +00:00
char * proto_str ;
long proto = SSL_version ( ssl_handle ) ;
const SSL_CIPHER * cipher = SSL_get_current_cipher ( ssl_handle ) ;
2014-02-21 00:46:54 +00:00
2014-03-04 01:57:33 +00:00
switch ( proto ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS12
case TLS1_2_VERSION :
proto_str = " TLSv1.2 " ;
break ;
# endif
# ifdef HAVE_TLS11
case TLS1_1_VERSION :
proto_str = " TLSv1.1 " ;
break ;
# endif
case TLS1_VERSION :
proto_str = " TLSv1 " ;
break ;
# ifdef HAVE_SSL3
case SSL3_VERSION :
proto_str = " SSLv3 " ;
break ;
# endif
# ifdef HAVE_SSL2
case SSL2_VERSION :
proto_str = " SSLv2 " ;
break ;
2014-03-04 01:57:33 +00:00
# endif
default : proto_str = " UNKNOWN " ;
2014-02-11 15:42:34 +00:00
}
2014-03-04 01:57:33 +00:00
2014-05-06 21:09:42 +00:00
array_init ( & meta_arr ) ;
add_assoc_string ( & meta_arr , " protocol " , proto_str ) ;
add_assoc_string ( & meta_arr , " cipher_name " , ( char * ) SSL_CIPHER_get_name ( cipher ) ) ;
2014-08-25 17:24:55 +00:00
add_assoc_long ( & meta_arr , " cipher_bits " , SSL_CIPHER_get_bits ( cipher , NULL ) ) ;
2014-05-06 21:09:42 +00:00
add_assoc_string ( & meta_arr , " cipher_version " , SSL_CIPHER_get_version ( cipher ) ) ;
2014-03-04 01:57:33 +00:00
2014-05-06 21:09:42 +00:00
return Z_ARR ( meta_arr ) ;
2014-02-11 15:42:34 +00:00
}
/* }}} */
2015-02-28 20:32:15 +00:00
static int php_openssl_crypto_info ( php_stream * stream ,
php_openssl_netstream_data_t * sslsock ,
php_stream_xport_crypto_param * cparam
) /* {{{ */
{
zval * zresult ;
const SSL_CIPHER * cipher ;
char * cipher_name , * cipher_version , * crypto_protocol ;
int cipher_bits ;
2015-02-28 20:35:25 +00:00
const unsigned char * alpn_proto = NULL ;
unsigned int alpn_proto_len = 0 ;
2015-02-28 20:32:15 +00:00
int needs_array_return ;
if ( ! sslsock - > ssl_active ) {
php_error_docref ( NULL , E_WARNING , " SSL/TLS not currently enabled for this stream " ) ;
return FAILURE ;
}
zresult = cparam - > inputs . zresult ;
needs_array_return = ( cparam - > inputs . infotype = = STREAM_CRYPTO_INFO_ALL ) ? 1 : 0 ;
2015-02-28 20:35:25 +00:00
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_ALPN_PROTOCOL ) {
# ifdef HAVE_TLS_ALPN
SSL_get0_alpn_selected ( sslsock - > ssl_handle , & alpn_proto , & alpn_proto_len ) ;
# endif
if ( ! needs_array_return ) {
if ( alpn_proto_len > 0 ) {
2015-02-28 22:11:30 +00:00
ZVAL_STRINGL ( zresult , ( const char * ) alpn_proto , alpn_proto_len ) ;
2015-02-28 20:35:25 +00:00
}
return SUCCESS ;
}
}
2015-02-28 20:32:15 +00:00
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_CIPHER ) {
cipher = SSL_get_current_cipher ( sslsock - > ssl_handle ) ;
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_CIPHER_NAME ) {
cipher_name = ( char * ) SSL_CIPHER_get_name ( cipher ) ;
if ( ! needs_array_return ) {
ZVAL_STRING ( zresult , cipher_name ) ;
return SUCCESS ;
}
}
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_CIPHER_BITS ) {
cipher_bits = SSL_CIPHER_get_bits ( cipher , NULL ) ;
if ( ! needs_array_return ) {
ZVAL_LONG ( zresult , cipher_bits ) ;
return SUCCESS ;
}
}
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_CIPHER_VERSION ) {
cipher_version = ( char * ) SSL_CIPHER_get_version ( cipher ) ;
if ( ! needs_array_return ) {
ZVAL_STRING ( zresult , cipher_version ) ;
return SUCCESS ;
}
}
}
if ( cparam - > inputs . infotype & STREAM_CRYPTO_INFO_PROTOCOL ) {
switch ( SSL_version ( sslsock - > ssl_handle ) ) {
# ifdef HAVE_TLS12
case TLS1_2_VERSION : crypto_protocol = " TLSv1.2 " ; break ;
# endif
# ifdef HAVE_TLS11
case TLS1_1_VERSION : crypto_protocol = " TLSv1.1 " ; break ;
# endif
case TLS1_VERSION : crypto_protocol = " TLSv1 " ; break ;
# ifdef HAVE_SSL3
case SSL3_VERSION : crypto_protocol = " SSLv3 " ; break ;
# endif
# ifdef HAVE_SSL2
case SSL2_VERSION : crypto_protocol = " SSLv2 " ; break ;
# endif
default : crypto_protocol = " UNKNOWN " ;
}
if ( ! needs_array_return ) {
ZVAL_STRING ( zresult , crypto_protocol ) ;
return SUCCESS ;
}
}
/* If we're still here we need to return an array with everything */
array_init ( zresult ) ;
add_assoc_string ( zresult , " protocol " , crypto_protocol ) ;
add_assoc_string ( zresult , " cipher_name " , cipher_name ) ;
add_assoc_long ( zresult , " cipher_bits " , cipher_bits ) ;
add_assoc_string ( zresult , " cipher_version " , cipher_version ) ;
2015-02-28 20:35:25 +00:00
if ( alpn_proto ) {
add_assoc_stringl ( zresult , " alpn_protocol " , ( char * ) alpn_proto , alpn_proto_len ) ;
}
2015-02-28 20:32:15 +00:00
return SUCCESS ;
}
/* }}} */
2014-12-13 22:06:14 +00:00
static int capture_peer_certs ( php_stream * stream , php_openssl_netstream_data_t * sslsock , X509 * peer_cert ) /* { { { */
2014-03-02 14:03:39 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val , zcert ;
2014-03-02 14:03:39 +00:00
int cert_captured = 0 ;
2014-08-07 11:55:37 +00:00
if ( NULL ! = ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2014-05-06 21:09:42 +00:00
" ssl " , " capture_peer_cert " ) ) & &
2014-12-13 22:06:14 +00:00
zend_is_true ( val )
2014-03-02 14:03:39 +00:00
) {
2015-02-02 05:23:16 +00:00
ZVAL_RES ( & zcert , zend_register_resource ( peer_cert , php_openssl_get_x509_list_id ( ) ) ) ;
2014-08-07 11:55:37 +00:00
php_stream_context_set_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " peer_certificate " , & zcert ) ;
2014-03-02 14:03:39 +00:00
cert_captured = 1 ;
}
2014-08-07 11:55:37 +00:00
if ( NULL ! = ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2014-05-06 21:09:42 +00:00
" ssl " , " capture_peer_cert_chain " ) ) & &
2014-12-13 22:06:14 +00:00
zend_is_true ( val )
2014-03-02 14:03:39 +00:00
) {
2014-05-06 21:09:42 +00:00
zval arr ;
2014-03-02 14:03:39 +00:00
STACK_OF ( X509 ) * chain ;
chain = SSL_get_peer_cert_chain ( sslsock - > ssl_handle ) ;
if ( chain & & sk_X509_num ( chain ) > 0 ) {
int i ;
2014-05-06 21:09:42 +00:00
array_init ( & arr ) ;
2014-03-02 14:03:39 +00:00
for ( i = 0 ; i < sk_X509_num ( chain ) ; i + + ) {
X509 * mycert = X509_dup ( sk_X509_value ( chain , i ) ) ;
2015-02-02 05:23:16 +00:00
ZVAL_RES ( & zcert , zend_register_resource ( mycert , php_openssl_get_x509_list_id ( ) ) ) ;
2014-05-06 21:09:42 +00:00
add_next_index_zval ( & arr , & zcert ) ;
2014-03-02 14:03:39 +00:00
}
} else {
2014-05-06 21:09:42 +00:00
ZVAL_NULL ( & arr ) ;
2014-03-02 14:03:39 +00:00
}
2014-08-07 11:55:37 +00:00
php_stream_context_set_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " peer_certificate_chain " , & arr ) ;
2014-05-06 21:09:42 +00:00
zval_dtor ( & arr ) ;
2014-03-02 14:03:39 +00:00
}
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
2014-12-14 13:07:59 +00:00
)
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
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS_SNI
2014-02-11 15:42:34 +00:00
if ( sslsock - > is_client ) {
2014-12-13 22:06:14 +00:00
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
2014-12-13 22:06:14 +00:00
if ( SUCCESS = = php_set_sock_blocking ( sslsock - > s . socket , 0 ) ) {
2010-12-23 01:44:54 +00:00
sslsock - > s . is_blocked = 0 ;
2006-12-05 01:39:07 +00:00
}
2015-01-03 09:22:58 +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 ) ;
}
2015-01-03 09:22:58 +00:00
2003-11-27 17:40:16 +00:00
do {
2010-12-23 01:44:54 +00:00
struct timeval cur_time ,
2014-12-31 15:49:21 +00:00
elapsed_time ;
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-01-21 15:20:32 +00:00
elapsed_time = subtract_timeval ( cur_time , start_time ) ;
2010-12-23 01:44:54 +00:00
2015-01-21 15:20:32 +00:00
if ( compare_timeval ( elapsed_time , * timeout ) > 0 ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , 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 */
2014-12-13 22:06:14 +00:00
retry = handle_ssl_error ( stream , n , blocked ) ;
2010-12-23 01:44:54 +00:00
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 ;
2015-01-03 09:22:58 +00:00
2010-12-23 01:44:54 +00:00
if ( has_timeout ) {
2015-01-21 15:20:32 +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
2014-12-13 22:06:14 +00:00
if ( sslsock - > s . is_blocked ! = blocked & & SUCCESS = = php_set_sock_blocking ( sslsock - > s . socket , blocked ) ) {
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-08-07 11:55:37 +00:00
if ( peer_cert & & PHP_STREAM_CONTEXT ( stream ) ) {
2014-12-13 22:06:14 +00:00
cert_captured = capture_peer_certs ( stream , sslsock , peer_cert ) ;
2014-03-02 14:03:39 +00:00
}
2003-11-27 17:40:16 +00:00
2014-12-13 22:06:14 +00:00
if ( FAILURE = = apply_peer_verification_policy ( sslsock - > ssl_handle , peer_cert , stream ) ) {
2003-11-27 17:40:16 +00:00
SSL_shutdown ( sslsock - > ssl_handle ) ;
2011-06-08 00:23:02 +00:00
n = - 1 ;
2015-01-03 09:22:58 +00:00
} else {
2003-02-27 17:43:38 +00:00
sslsock - > ssl_active = 1 ;
2006-04-30 23:43:46 +00:00
2014-08-07 11:55:37 +00:00
if ( PHP_STREAM_CONTEXT ( stream ) ) {
2014-05-06 21:09:42 +00:00
zval * val ;
2014-08-07 11:55:37 +00:00
if ( NULL ! = ( val = php_stream_context_get_option ( PHP_STREAM_CONTEXT ( stream ) ,
2015-02-28 20:23:59 +00:00
" ssl " , " capture_session_meta " ) ) & & zend_is_true ( val )
2014-03-02 14:03:39 +00:00
) {
2014-05-06 21:09:42 +00:00
zval meta_arr ;
ZVAL_ARR ( & meta_arr , capture_session_meta ( sslsock - > ssl_handle ) ) ;
2014-08-07 11:55:37 +00:00
php_stream_context_set_option ( PHP_STREAM_CONTEXT ( stream ) , " ssl " , " session_meta " , & meta_arr ) ;
2014-05-06 21:09:42 +00:00
zval_dtor ( & meta_arr ) ;
2014-02-11 13:42:29 +00:00
}
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 ;
2015-02-28 20:23:59 +00:00
/* We want to capture the peer cert even if verification fails*/
2014-03-02 14:03:39 +00:00
peer_cert = SSL_get_peer_certificate ( sslsock - > ssl_handle ) ;
2014-08-07 11:55:37 +00:00
if ( peer_cert & & PHP_STREAM_CONTEXT ( stream ) ) {
2014-12-13 22:06:14 +00:00
cert_captured = capture_peer_certs ( stream , sslsock , peer_cert ) ;
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 20:14:47 +00:00
static size_t php_openssl_sockop_read ( php_stream * stream , char * buf , size_t count ) /* { { { */
2003-02-27 17:43:38 +00:00
{
2015-02-09 20:14:47 +00:00
return php_openssl_sockop_io ( 1 , stream , buf , count ) ;
2013-02-24 14:54:39 +00:00
}
2014-12-31 00:44:45 +00:00
/* }}} */
2013-01-25 01:22:01 +00:00
2015-02-09 20:14:47 +00:00
static size_t php_openssl_sockop_write ( php_stream * stream , const char * buf , size_t count ) /* { { { */
2013-02-24 14:54:39 +00:00
{
2015-02-09 20:14:47 +00:00
return php_openssl_sockop_io ( 0 , stream , ( char * ) buf , count ) ;
2003-02-27 17:43:38 +00:00
}
2014-12-31 00:44:45 +00:00
/* }}} */
2003-02-27 17:43:38 +00:00
2013-02-24 14:54:39 +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
*
*/
2015-01-23 15:03:20 +00:00
static size_t php_openssl_sockop_io ( int read , php_stream * stream , char * buf , size_t count ) /* { { { */
2014-03-04 01:57:33 +00:00
{
php_openssl_netstream_data_t * sslsock = ( php_openssl_netstream_data_t * ) stream - > abstract ;
2015-01-03 09:22:58 +00:00
2014-12-31 15:49:21 +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 ;
2015-03-09 21:53:26 +00:00
struct timeval * timeout = NULL ;
int began_blocked = sslsock - > s . is_blocked ;
2015-02-09 16:42:17 +00:00
int has_timeout = 0 ;
2015-01-30 13:36:33 +00:00
int nr_bytes = 0 ;
/* prevent overflow in openssl */
if ( count > INT_MAX ) {
count = INT_MAX ;
}
2013-02-24 14:54:39 +00:00
2015-03-09 21:53:26 +00:00
/* never use a timeout with non-blocking sockets */
if ( began_blocked & & & sslsock - > s . timeout ) {
timeout = & sslsock - > s . timeout ;
2013-01-25 01:22:01 +00:00
}
2013-02-24 14:54:39 +00:00
2015-03-09 21:53:26 +00:00
if ( timeout & & php_set_sock_blocking ( sslsock - > s . socket , 0 TSRMLS_CC ) = = SUCCESS ) {
sslsock - > s . is_blocked = 0 ;
}
2013-02-24 14:54:39 +00:00
2015-03-09 21:53:26 +00:00
if ( ! sslsock - > s . is_blocked & & timeout & & ( timeout - > tv_sec | | timeout - > tv_usec ) ) {
has_timeout = 1 ;
/* gettimeofday is not monotonic; using it here is not strictly correct */
2013-01-25 01:22:01 +00:00
gettimeofday ( & start_time , NULL ) ;
}
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* Main IO loop. */
2014-03-04 01:57:33 +00:00
do {
2015-01-23 14:04:29 +00:00
struct timeval cur_time , elapsed_time , left_time ;
2013-02-24 14:54:39 +00:00
/* If we have a timeout to check, figure out how much time has elapsed since we started. */
2013-01-25 01:22:01 +00:00
if ( has_timeout ) {
gettimeofday ( & cur_time , NULL ) ;
2013-02-24 14:54:39 +00:00
/* Determine how much time we've taken so far. */
2015-03-09 21:53:26 +00:00
elapsed_time = subtract_timeval ( cur_time , start_time ) ;
2013-02-24 14:54:39 +00:00
/* and return an error if we've taken too long. */
2015-03-09 21:53:26 +00:00
if ( compare_timeval ( elapsed_time , * timeout ) > 0 ) {
2013-02-24 14:54:39 +00:00
/* If the socket was originally blocking, set it back. */
2015-03-09 21:53:26 +00:00
if ( began_blocked ) {
2015-01-23 15:03:20 +00:00
php_set_sock_blocking ( sslsock - > s . socket , 1 ) ;
2013-01-25 01:22:01 +00:00
sslsock - > s . is_blocked = 1 ;
}
2015-03-09 21:53:26 +00:00
sslsock - > s . timeout_event = 1 ;
2013-01-25 01:22:01 +00:00
return - 1 ;
}
}
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* Now, do the IO operation. Don't block if we can't complete... */
if ( read ) {
2015-01-30 13:36:33 +00:00
nr_bytes = SSL_read ( sslsock - > ssl_handle , buf , ( int ) count ) ;
2014-03-04 01:57:33 +00:00
2014-12-31 15:49:21 +00:00
if ( sslsock - > reneg & & sslsock - > reneg - > should_close ) {
/* renegotiation rate limiting triggered */
php_stream_xport_shutdown ( stream , ( stream_shutdown_t ) SHUT_RDWR ) ;
nr_bytes = 0 ;
stream - > eof = 1 ;
break ;
}
2014-03-04 01:57:33 +00:00
} else {
2015-01-30 13:36:33 +00:00
nr_bytes = SSL_write ( sslsock - > ssl_handle , buf , ( int ) count ) ;
2014-03-04 01:57:33 +00:00
}
2013-02-24 14:54:39 +00:00
/* Now, how much time until we time out? */
2013-01-25 01:22:01 +00:00
if ( has_timeout ) {
2015-01-21 15:20:32 +00:00
left_time = subtract_timeval ( * timeout , elapsed_time ) ;
2013-01-25 01:22:01 +00:00
}
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* If we didn't do anything on the last loop (or an error) check to see if we should retry or exit. */
2013-01-25 01:22:01 +00:00
if ( nr_bytes < = 0 ) {
2015-01-03 09:22:58 +00:00
2013-02-24 14:54:39 +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 ) ;
2015-01-23 15:03:20 +00:00
retry = handle_ssl_error ( stream , nr_bytes , 0 ) ;
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* If we get this (the above doesn't check) then we'll retry as well. */
2015-01-27 18:22:47 +00:00
if ( errno = = EAGAIN & & err = = SSL_ERROR_WANT_READ & & read ) {
2013-02-24 14:54:39 +00:00
retry = 1 ;
}
2015-02-13 12:39:46 +00:00
if ( errno = = EAGAIN & & err = = SSL_ERROR_WANT_WRITE & & read = = 0 ) {
2015-01-27 01:40:52 +00:00
retry = 1 ;
}
2013-02-24 14:54:39 +00:00
/* Also, on reads, we may get this condition on an EOF. We should check properly. */
if ( read ) {
stream - > eof = ( retry = = 0 & & errno ! = EAGAIN & & ! SSL_pending ( sslsock - > ssl_handle ) ) ;
}
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +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 . . .
*/
2015-03-09 21:53:26 +00:00
if ( retry & & began_blocked ) {
2013-02-24 14:54:39 +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 ) ;
}
}
2013-01-25 01:22:01 +00:00
} else {
2013-02-24 14:54:39 +00:00
/* Else, if we got bytes back, check for possible errors. */
2015-02-09 20:14:47 +00:00
int err = SSL_get_error ( sslsock - > ssl_handle , nr_bytes ) ;
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* If we didn't get any error, then let's return it to PHP. */
2015-01-30 21:49:44 +00:00
if ( err = = SSL_ERROR_NONE ) {
2013-01-25 01:22:01 +00:00
break ;
2015-01-30 21:49:44 +00:00
}
2015-01-03 09:22:58 +00:00
2013-02-24 14:54:39 +00:00
/* Otherwise, we need to wait again (up to time_left or we get an error) */
2015-03-09 21:53:26 +00:00
if ( began_blocked ) {
2013-02-24 14:54:39 +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-01-30 21:49:44 +00:00
}
2014-03-04 01:57:33 +00:00
}
2015-01-27 01:40:52 +00:00
/* Finally, we keep going until we got data, and an SSL_ERROR_NONE, unless we had an error. */
} while ( retry ) ;
2014-03-04 01:57:33 +00:00
2013-02-24 14:54:39 +00:00
/* Tell PHP if we read / wrote bytes. */
2014-03-04 01:57:33 +00:00
if ( nr_bytes > 0 ) {
2014-08-07 11:55:37 +00:00
php_stream_notify_progress_increment ( PHP_STREAM_CONTEXT ( stream ) , nr_bytes , 0 ) ;
2014-03-04 01:57:33 +00:00
}
2013-02-24 14:54:39 +00:00
/* And if we were originally supposed to be blocking, let's reset the socket to that. */
2015-03-09 22:02:04 +00:00
if ( began_blocked & & php_set_sock_blocking ( sslsock - > s . socket , 1 ) = = SUCCESS ) {
2015-02-09 16:42:17 +00:00
sslsock - > s . is_blocked = 1 ;
2013-01-25 01:22:01 +00:00
}
2015-01-30 13:36:33 +00:00
return 0 > nr_bytes ? 0 : nr_bytes ;
2014-12-31 15:49:21 +00:00
} else {
2015-01-30 13:36:33 +00:00
size_t nr_bytes = 0 ;
2014-12-31 15:49:21 +00:00
/*
2015-02-09 20:14:47 +00:00
* This block is if we had no timeout . . . We will just sit and wait forever on the IO operation .
2014-12-31 15:49:21 +00:00
*/
if ( read ) {
2015-01-23 15:03:20 +00:00
nr_bytes = php_stream_socket_ops . read ( stream , buf , count ) ;
2014-12-31 15:49:21 +00:00
} else {
2015-01-23 15:03:20 +00:00
nr_bytes = php_stream_socket_ops . write ( stream , buf , count ) ;
2014-12-31 15:49:21 +00:00
}
2014-03-04 01:57:33 +00:00
2015-01-30 13:36:33 +00:00
return nr_bytes ;
2014-03-04 01:57:33 +00:00
}
}
/* }}} */
2015-01-21 15:20:32 +00:00
static struct timeval subtract_timeval ( struct timeval a , struct timeval b )
2013-02-24 14:54:39 +00:00
{
2014-12-31 15:49:21 +00:00
struct timeval difference ;
2013-01-25 01:22:01 +00:00
2013-02-24 14:54:39 +00:00
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 ;
2013-01-25 01:22:01 +00:00
}
2013-02-24 14:54:39 +00:00
return difference ;
2003-02-27 17:43:38 +00:00
}
2015-01-21 15:20:32 +00:00
static int compare_timeval ( struct timeval a , struct timeval b )
2013-02-24 14:54:39 +00:00
{
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-12-13 22:06:14 +00:00
static int php_openssl_sockop_close ( php_stream * stream , int close_handle ) /* { { { */
2014-03-04 01:57:33 +00:00
{
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 ;
}
/* }}} */
2014-12-13 22:06:14 +00:00
static int php_openssl_sockop_flush ( php_stream * stream ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2014-12-13 22:06:14 +00:00
return php_stream_socket_ops . flush ( stream ) ;
2014-03-04 01:57:33 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static int php_openssl_sockop_stat ( php_stream * stream , php_stream_statbuf * ssb ) /* { { { */
2014-03-04 01:57:33 +00:00
{
2014-12-13 22:06:14 +00:00
return php_stream_socket_ops . stat ( stream , ssb ) ;
2014-03-04 01:57:33 +00:00
}
/* }}} */
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 ,
2014-12-13 22:06:14 +00:00
php_stream_xport_param * xparam STREAMS_DC )
2003-02-28 19:53:21 +00:00
{
int clisock ;
xparam - > outputs . client = NULL ;
clisock = php_network_accept_incoming ( sock - > s . socket ,
xparam - > want_textaddr ? & xparam - > outputs . textaddr : 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
2014-12-14 13:07:59 +00:00
) ;
2003-02-28 19:53:21 +00:00
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 ;
2015-01-03 09:22:58 +00:00
2003-02-28 19:53:21 +00:00
xparam - > outputs . client = php_stream_alloc_rel ( stream - > ops , clisockdata , NULL , " r+ " ) ;
if ( xparam - > outputs . client ) {
2014-08-07 11:55:37 +00:00
xparam - > outputs . client - > ctx = stream - > ctx ;
if ( stream - > ctx ) {
GC_REFCOUNT ( stream - > ctx ) + + ;
2008-07-11 10:25:15 +00:00
}
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 ,
2014-12-13 22:06:14 +00:00
NULL ) < 0 | | php_stream_xport_crypto_enable (
xparam - > outputs . client , 1 ) < 0 ) {
php_error_docref ( NULL , E_WARNING , " Failed to enable crypto " ) ;
2004-04-21 23:02:06 +00:00
php_stream_close ( xparam - > outputs . client ) ;
xparam - > outputs . client = NULL ;
xparam - > outputs . returncode = - 1 ;
}
}
2003-02-28 19:53:21 +00:00
}
2015-01-03 09:22:58 +00:00
2003-02-28 19:53:21 +00:00
return xparam - > outputs . client = = NULL ? - 1 : 0 ;
}
2014-03-04 01:57:33 +00:00
2014-12-13 22:06:14 +00:00
static int php_openssl_sockop_set_option ( php_stream * stream , int option , int value , void * ptrparam )
2003-02-27 17:43:38 +00:00
{
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 ) {
2015-01-30 13:36:33 +00:00
# ifdef _WIN32
tv . tv_sec = ( long ) FG ( default_socket_timeout ) ;
# else
tv . tv_sec = ( time_t ) FG ( default_socket_timeout ) ;
# endif
2004-09-17 12:44:56 +00:00
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 ;
}
2015-01-03 09:22:58 +00:00
2003-02-27 17:43:38 +00:00
case PHP_STREAM_OPTION_CRYPTO_API :
switch ( cparam - > op ) {
case STREAM_XPORT_CRYPTO_OP_SETUP :
2014-12-13 22:06:14 +00:00
cparam - > outputs . returncode = php_openssl_setup_crypto ( stream , sslsock , cparam ) ;
2003-02-27 17:43:38 +00:00
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 :
2014-12-13 22:06:14 +00:00
cparam - > outputs . returncode = php_openssl_enable_crypto ( stream , sslsock , cparam ) ;
2003-02-27 17:43:38 +00:00
return PHP_STREAM_OPTION_RETURN_OK ;
2003-02-27 23:50:55 +00:00
break ;
2015-02-28 20:32:15 +00:00
case STREAM_XPORT_CRYPTO_OP_INFO :
return ( php_openssl_crypto_info ( stream , sslsock , cparam ) = = SUCCESS )
? PHP_STREAM_OPTION_RETURN_OK
: PHP_STREAM_OPTION_RETURN_ERR ;
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 */
2014-12-13 22:06:14 +00:00
php_stream_socket_ops . set_option ( stream , option , value , ptrparam ) ;
2003-02-27 17:43:38 +00:00
2009-07-28 19:28:08 +00:00
if ( ( sslsock - > enable_on_connect ) & &
( ( xparam - > outputs . returncode = = 0 ) | |
2015-01-03 09:22:58 +00:00
( xparam - > op = = STREAM_XPORT_OP_CONNECT_ASYNC & &
2009-07-28 19:28:08 +00:00
xparam - > outputs . returncode = = 1 & & xparam - > outputs . error_code = = EINPROGRESS ) ) )
{
2014-12-13 22:06:14 +00:00
if ( php_stream_xport_crypto_setup ( stream , sslsock - > method , NULL ) < 0 | |
php_stream_xport_crypto_enable ( stream , 1 ) < 0 ) {
php_error_docref ( NULL , E_WARNING , " Failed to enable crypto " ) ;
2003-02-27 17:43:38 +00:00
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 */
2014-12-13 22:06:14 +00:00
xparam - > outputs . returncode = php_openssl_tcp_sockop_accept ( stream , sslsock , xparam STREAMS_CC ) ;
2004-04-21 23:02:06 +00:00
2015-01-03 09:22:58 +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
}
}
2014-12-13 22:06:14 +00:00
return php_stream_socket_ops . set_option ( stream , option , value , ptrparam ) ;
2003-02-27 17:43:38 +00:00
}
2014-12-13 22:06:14 +00:00
static int php_openssl_sockop_cast ( php_stream * stream , int castas , void * * ret )
2003-02-27 17:43:38 +00:00
{
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 ) ;
}
}
2015-03-06 15:49:08 +00:00
* ( php_socket_t * ) 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-08-25 17:24:55 +00:00
static zend_long get_crypto_method ( php_stream_context * ctx , zend_long crypto_method )
2014-02-11 15:12:01 +00:00
{
2014-05-06 21:09:42 +00:00
zval * val ;
2014-02-11 15:12:01 +00:00
2014-05-06 21:09:42 +00:00
if ( ctx & & ( val = php_stream_context_get_option ( ctx , " ssl " , " crypto_method " ) ) ! = NULL ) {
2014-08-25 19:51:49 +00:00
convert_to_long_ex ( val ) ;
2014-08-25 17:24:55 +00:00
crypto_method = ( zend_long ) Z_LVAL_P ( 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-12-13 22:06:14 +00:00
static char * get_url_name ( const char * resourcename , size_t resourcenamelen , int is_persistent )
2014-02-11 15:12:01 +00:00
{
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 ,
2014-12-13 22:06:14 +00:00
php_stream_context * context STREAMS_DC )
2003-02-27 17:43:38 +00:00
{
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 */
2015-01-30 13:36:33 +00:00
# ifdef _WIN32
2015-01-30 12:52:11 +00:00
sslsock - > s . timeout . tv_sec = ( long ) FG ( default_socket_timeout ) ;
2015-01-30 13:36:33 +00:00
# else
sslsock - > s . timeout . tv_sec = ( time_t ) FG ( default_socket_timeout ) ;
# endif
2007-03-14 19:22:14 +00:00
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 */
2015-01-03 09:22:58 +00:00
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 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_SSL2
2004-09-10 11:43:47 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT ;
2015-02-28 20:23:59 +00:00
# else
php_error_docref ( NULL , E_WARNING , " SSLv2 support is not compiled into the OpenSSL library against which PHP is linked " ) ;
return NULL ;
2011-04-24 23:27:48 +00:00
# endif
2004-09-10 11:43:47 +00:00
} else if ( strncmp ( proto , " sslv3 " , protolen ) = = 0 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_SSL3
2004-09-10 11:43:47 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT ;
2015-02-28 20:23:59 +00:00
# else
php_error_docref ( NULL , E_WARNING , " SSLv3 support is not compiled into the OpenSSL library against which PHP is linked " ) ;
return NULL ;
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 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS11
2013-10-08 17:54:22 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT ;
# else
2015-02-28 20:23:59 +00:00
php_error_docref ( NULL , E_WARNING , " TLSv1.1 support is not compiled into the OpenSSL library against which PHP is linked " ) ;
2013-10-08 17:54:22 +00:00
return NULL ;
# endif
} else if ( strncmp ( proto , " tlsv1.2 " , protolen ) = = 0 ) {
2015-02-28 20:23:59 +00:00
# ifdef HAVE_TLS12
2013-10-08 17:54:22 +00:00
sslsock - > enable_on_connect = 1 ;
sslsock - > method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT ;
# else
2015-02-28 20:23:59 +00:00
php_error_docref ( NULL , E_WARNING , " TLSv1.2 support is not compiled into the OpenSSL library against which PHP is linked " ) ;
2013-10-08 17:54:22 +00:00
return NULL ;
# endif
2003-02-27 17:43:38 +00:00
}
2014-01-28 17:05:56 +00:00
2014-12-13 22:06:14 +00:00
sslsock - > url_name = get_url_name ( resourcename , resourcenamelen , ! ! persistent_id ) ;
2014-01-28 17:05:56 +00:00
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
*/