1161 lines
30 KiB
C
Executable File
1161 lines
30 KiB
C
Executable File
/*
|
|
+----------------------------------------------------------------------+
|
|
| Swoole |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 2.0 of the Apache license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.apache.org/licenses/LICENSE-2.0.html |
|
|
| If you did not receive a copy of the Apache2.0 license and are unable|
|
|
| to obtain it through the world-wide-web, please send a note to |
|
|
| license@swoole.com so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "swoole.h"
|
|
#include "Connection.h"
|
|
|
|
#ifdef SW_USE_OPENSSL
|
|
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
static int openssl_init = 0;
|
|
static pthread_mutex_t *lock_array;
|
|
|
|
static const SSL_METHOD *swSSL_get_method(int method);
|
|
static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store);
|
|
#ifndef OPENSSL_NO_RSA
|
|
static RSA* swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length);
|
|
#endif
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
static int swSSL_set_default_dhparam(SSL_CTX* ssl_context);
|
|
#endif
|
|
static int swSSL_set_dhparam(SSL_CTX* ssl_context, char *file);
|
|
static int swSSL_set_ecdh_curve(SSL_CTX* ssl_context);
|
|
|
|
#ifdef TLSEXT_TYPE_next_proto_neg
|
|
static int swSSL_npn_advertised(SSL *ssl, const uchar **out, uint32_t *outlen, void *arg);
|
|
#endif
|
|
|
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
|
static int swSSL_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg);
|
|
#endif
|
|
|
|
static void swSSL_lock_callback(int mode, int type, char *file, int line);
|
|
|
|
static const SSL_METHOD *swSSL_get_method(int method)
|
|
{
|
|
switch (method)
|
|
{
|
|
#ifndef OPENSSL_NO_SSL3_METHOD
|
|
case SW_SSLv3_METHOD:
|
|
return SSLv3_method();
|
|
case SW_SSLv3_SERVER_METHOD:
|
|
return SSLv3_server_method();
|
|
case SW_SSLv3_CLIENT_METHOD:
|
|
return SSLv3_client_method();
|
|
#endif
|
|
case SW_SSLv23_SERVER_METHOD:
|
|
return SSLv23_server_method();
|
|
case SW_SSLv23_CLIENT_METHOD:
|
|
return SSLv23_client_method();
|
|
/**
|
|
* openssl 1.1.0
|
|
*/
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
case SW_TLSv1_METHOD:
|
|
return TLSv1_method();
|
|
case SW_TLSv1_SERVER_METHOD:
|
|
return TLSv1_server_method();
|
|
case SW_TLSv1_CLIENT_METHOD:
|
|
return TLSv1_client_method();
|
|
#ifdef TLS1_1_VERSION
|
|
case SW_TLSv1_1_METHOD:
|
|
return TLSv1_1_method();
|
|
case SW_TLSv1_1_SERVER_METHOD:
|
|
return TLSv1_1_server_method();
|
|
case SW_TLSv1_1_CLIENT_METHOD:
|
|
return TLSv1_1_client_method();
|
|
#endif
|
|
#ifdef TLS1_2_VERSION
|
|
case SW_TLSv1_2_METHOD:
|
|
return TLSv1_2_method();
|
|
case SW_TLSv1_2_SERVER_METHOD:
|
|
return TLSv1_2_server_method();
|
|
case SW_TLSv1_2_CLIENT_METHOD:
|
|
return TLSv1_2_client_method();
|
|
#endif
|
|
case SW_DTLSv1_METHOD:
|
|
return DTLSv1_method();
|
|
case SW_DTLSv1_SERVER_METHOD:
|
|
return DTLSv1_server_method();
|
|
case SW_DTLSv1_CLIENT_METHOD:
|
|
return DTLSv1_client_method();
|
|
#endif
|
|
case SW_SSLv23_METHOD:
|
|
default:
|
|
return SSLv23_method();
|
|
}
|
|
return SSLv23_method();
|
|
}
|
|
|
|
void swSSL_init(void)
|
|
{
|
|
if (openssl_init)
|
|
{
|
|
return;
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100003L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
|
|
#else
|
|
OPENSSL_config(NULL);
|
|
SSL_library_init();
|
|
SSL_load_error_strings();
|
|
OpenSSL_add_all_algorithms();
|
|
#endif
|
|
openssl_init = 1;
|
|
}
|
|
|
|
void swSSL_destroy()
|
|
{
|
|
if (!openssl_init)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CRYPTO_set_locking_callback(NULL);
|
|
int i;
|
|
for (i = 0; i < CRYPTO_num_locks(); i++)
|
|
{
|
|
pthread_mutex_destroy(&(lock_array[i]));
|
|
}
|
|
openssl_init = 0;
|
|
#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0
|
|
CRYPTO_THREADID_set_callback(NULL);
|
|
#else
|
|
CRYPTO_set_id_callback(NULL);
|
|
#endif
|
|
CRYPTO_set_locking_callback(NULL);
|
|
}
|
|
|
|
static void swSSL_lock_callback(int mode, int type, char *file, int line)
|
|
{
|
|
if (mode & CRYPTO_LOCK)
|
|
{
|
|
pthread_mutex_lock(&(lock_array[type]));
|
|
}
|
|
else
|
|
{
|
|
pthread_mutex_unlock(&(lock_array[type]));
|
|
}
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0
|
|
static void swSSL_id_callback(CRYPTO_THREADID * id)
|
|
{
|
|
CRYPTO_THREADID_set_numeric(id, (ulong_t) pthread_self());
|
|
}
|
|
#else
|
|
static ulong_t swSSL_id_callback(void)
|
|
{
|
|
return (ulong_t) pthread_self();
|
|
}
|
|
#endif
|
|
|
|
void swSSL_init_thread_safety()
|
|
{
|
|
int i;
|
|
lock_array = (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
|
|
for (i = 0; i < CRYPTO_num_locks(); i++)
|
|
{
|
|
pthread_mutex_init(&(lock_array[i]), NULL);
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0
|
|
CRYPTO_THREADID_set_callback(swSSL_id_callback);
|
|
#else
|
|
CRYPTO_set_id_callback(swSSL_id_callback);
|
|
#endif
|
|
|
|
CRYPTO_set_locking_callback((void (*)()) swSSL_lock_callback);
|
|
}
|
|
|
|
void swSSL_server_http_advise(SSL_CTX* ssl_context, swSSL_config *cfg)
|
|
{
|
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
|
SSL_CTX_set_alpn_select_cb(ssl_context, swSSL_alpn_advertised, cfg);
|
|
#endif
|
|
|
|
#ifdef TLSEXT_TYPE_next_proto_neg
|
|
SSL_CTX_set_next_protos_advertised_cb(ssl_context, swSSL_npn_advertised, cfg);
|
|
#endif
|
|
|
|
if (cfg->http)
|
|
{
|
|
SSL_CTX_set_session_id_context(ssl_context, (const unsigned char *) "HTTP", strlen("HTTP"));
|
|
SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_SERVER);
|
|
SSL_CTX_sess_set_cache_size(ssl_context, 1);
|
|
}
|
|
}
|
|
|
|
int swSSL_server_set_cipher(SSL_CTX* ssl_context, swSSL_config *cfg)
|
|
{
|
|
#ifndef TLS1_2_VERSION
|
|
return SW_OK;
|
|
#endif
|
|
SSL_CTX_set_read_ahead(ssl_context, 1);
|
|
|
|
if (strlen(cfg->ciphers) > 0)
|
|
{
|
|
if (SSL_CTX_set_cipher_list(ssl_context, cfg->ciphers) == 0)
|
|
{
|
|
swWarn("SSL_CTX_set_cipher_list(\"%s\") failed", cfg->ciphers);
|
|
return SW_ERR;
|
|
}
|
|
if (cfg->prefer_server_ciphers)
|
|
{
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
}
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_RSA
|
|
SSL_CTX_set_tmp_rsa_callback(ssl_context, swSSL_rsa_key_callback);
|
|
#endif
|
|
|
|
if (cfg->dhparam && strlen(cfg->dhparam) > 0)
|
|
{
|
|
swSSL_set_dhparam(ssl_context, cfg->dhparam);
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
else
|
|
{
|
|
swSSL_set_default_dhparam(ssl_context);
|
|
}
|
|
#endif
|
|
if (cfg->ecdh_curve && strlen(cfg->ecdh_curve) > 0)
|
|
{
|
|
swSSL_set_ecdh_curve(ssl_context);
|
|
}
|
|
return SW_OK;
|
|
}
|
|
|
|
static int swSSL_passwd_callback(char *buf, int num, int verify, void *data)
|
|
{
|
|
swSSL_option *option = (swSSL_option *) data;
|
|
if (option->passphrase)
|
|
{
|
|
size_t len = strlen(option->passphrase);
|
|
if (len < num - 1)
|
|
{
|
|
memcpy(buf, option->passphrase, len + 1);
|
|
return (int) len;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SSL_CTX* swSSL_get_context(swSSL_option *option)
|
|
{
|
|
if (!openssl_init)
|
|
{
|
|
swSSL_init();
|
|
}
|
|
|
|
SSL_CTX *ssl_context = SSL_CTX_new(swSSL_get_method(option->method));
|
|
if (ssl_context == NULL)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
return NULL;
|
|
}
|
|
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_MSIE_SSLV2_RSA_PADDING);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_TLS_D5_BUG);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_TLS_BLOCK_PADDING_BUG);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_SINGLE_DH_USE);
|
|
|
|
if (option->passphrase)
|
|
{
|
|
SSL_CTX_set_default_passwd_cb_userdata(ssl_context, option);
|
|
SSL_CTX_set_default_passwd_cb(ssl_context, swSSL_passwd_callback);
|
|
}
|
|
|
|
if (option->cert_file)
|
|
{
|
|
/*
|
|
* set the local certificate from CertFile
|
|
*/
|
|
if (SSL_CTX_use_certificate_file(ssl_context, option->cert_file, SSL_FILETYPE_PEM) <= 0)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* if the crt file have many certificate entry ,means certificate chain
|
|
* we need call this function
|
|
*/
|
|
if (SSL_CTX_use_certificate_chain_file(ssl_context, option->cert_file) <= 0)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* set the private key from KeyFile (may be the same as CertFile)
|
|
*/
|
|
if (SSL_CTX_use_PrivateKey_file(ssl_context, option->key_file, SSL_FILETYPE_PEM) <= 0)
|
|
{
|
|
ERR_print_errors_fp(stderr);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* verify private key
|
|
*/
|
|
if (!SSL_CTX_check_private_key(ssl_context))
|
|
{
|
|
swWarn("Private key does not match the public certificate");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return ssl_context;
|
|
}
|
|
|
|
static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store)
|
|
{
|
|
#if 0
|
|
char *subject, *issuer;
|
|
int err, depth;
|
|
X509 *cert;
|
|
X509_NAME *sname, *iname;
|
|
X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx());
|
|
cert = X509_STORE_CTX_get_current_cert(x509_store);
|
|
err = X509_STORE_CTX_get_error(x509_store);
|
|
depth = X509_STORE_CTX_get_error_depth(x509_store);
|
|
|
|
sname = X509_get_subject_name(cert);
|
|
subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
|
|
|
|
iname = X509_get_issuer_name(cert);
|
|
issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
|
|
swWarn("verify:%d, error:%d, depth:%d, subject:\"%s\", issuer:\"%s\"", ok, err, depth, subject, issuer);
|
|
|
|
if (sname)
|
|
{
|
|
OPENSSL_free(subject);
|
|
}
|
|
if (iname)
|
|
{
|
|
OPENSSL_free(issuer);
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
int swSSL_set_client_certificate(SSL_CTX *ctx, char *cert_file, int depth)
|
|
{
|
|
STACK_OF(X509_NAME) *list;
|
|
|
|
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, swSSL_verify_callback);
|
|
SSL_CTX_set_verify_depth(ctx, depth);
|
|
|
|
if (SSL_CTX_load_verify_locations(ctx, cert_file, NULL) == 0)
|
|
{
|
|
swWarn("SSL_CTX_load_verify_locations(\"%s\") failed.", cert_file);
|
|
return SW_ERR;
|
|
}
|
|
|
|
ERR_clear_error();
|
|
list = SSL_load_client_CA_file(cert_file);
|
|
if (list == NULL)
|
|
{
|
|
swWarn("SSL_load_client_CA_file(\"%s\") failed.", cert_file);
|
|
return SW_ERR;
|
|
}
|
|
|
|
ERR_clear_error();
|
|
SSL_CTX_set_client_CA_list(ctx, list);
|
|
|
|
return SW_OK;
|
|
}
|
|
|
|
int swSSL_set_capath(swSSL_option *cfg, SSL_CTX *ctx)
|
|
{
|
|
if (cfg->cafile || cfg->capath)
|
|
{
|
|
if (!SSL_CTX_load_verify_locations(ctx, cfg->cafile, cfg->capath))
|
|
{
|
|
return SW_ERR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!SSL_CTX_set_default_verify_paths(ctx))
|
|
{
|
|
swWarn("Unable to set default verify locations and no CA settings specified.");
|
|
return SW_ERR;
|
|
}
|
|
}
|
|
|
|
if (cfg->verify_depth > 0)
|
|
{
|
|
SSL_CTX_set_verify_depth(ctx, cfg->verify_depth);
|
|
}
|
|
|
|
return SW_OK;
|
|
}
|
|
|
|
#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
|
|
static int swSSL_check_name(char *name, ASN1_STRING *pattern)
|
|
{
|
|
char *s, *end;
|
|
size_t slen, plen;
|
|
|
|
s = name;
|
|
slen = strlen(name);
|
|
|
|
uchar *p = ASN1_STRING_data(pattern);
|
|
plen = ASN1_STRING_length(pattern);
|
|
|
|
if (slen == plen && strncasecmp(s, (char*) p, plen) == 0)
|
|
{
|
|
return SW_OK;
|
|
}
|
|
|
|
if (plen > 2 && p[0] == '*' && p[1] == '.')
|
|
{
|
|
plen -= 1;
|
|
p += 1;
|
|
|
|
end = s + slen;
|
|
s = swoole_strlchr(s, end, '.');
|
|
|
|
if (s == NULL)
|
|
{
|
|
return SW_ERR;
|
|
}
|
|
|
|
slen = end - s;
|
|
|
|
if (plen == slen && strncasecmp(s, (char*) p, plen) == 0)
|
|
{
|
|
return SW_OK;
|
|
}
|
|
}
|
|
return SW_ERR;
|
|
}
|
|
#endif
|
|
|
|
int swSSL_check_host(swConnection *conn, char *tls_host_name)
|
|
{
|
|
X509 *cert = SSL_get_peer_certificate(conn->ssl);
|
|
if (cert == NULL)
|
|
{
|
|
return SW_ERR;
|
|
}
|
|
|
|
#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
|
|
/* X509_check_host() is only available in OpenSSL 1.0.2+ */
|
|
if (X509_check_host(cert, tls_host_name, strlen(tls_host_name), 0, NULL) != 1)
|
|
{
|
|
swWarn("X509_check_host(): no match");
|
|
goto failed;
|
|
}
|
|
goto found;
|
|
#else
|
|
int n, i;
|
|
X509_NAME *sname;
|
|
ASN1_STRING *str;
|
|
X509_NAME_ENTRY *entry;
|
|
GENERAL_NAME *altname;
|
|
STACK_OF(GENERAL_NAME) *altnames;
|
|
|
|
/*
|
|
* As per RFC6125 and RFC2818, we check subjectAltName extension,
|
|
* and if it's not present - commonName in Subject is checked.
|
|
*/
|
|
altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
|
|
|
if (altnames)
|
|
{
|
|
n = sk_GENERAL_NAME_num(altnames);
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
altname = sk_GENERAL_NAME_value(altnames, i);
|
|
|
|
if (altname->type != GEN_DNS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
str = altname->d.dNSName;
|
|
swTrace("SSL subjectAltName: \"%*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str));
|
|
|
|
if (swSSL_check_name(tls_host_name, str) == SW_OK)
|
|
{
|
|
swTrace("SSL subjectAltName: match");
|
|
GENERAL_NAMES_free(altnames);
|
|
goto found;
|
|
}
|
|
}
|
|
|
|
swTrace("SSL subjectAltName: no match.");
|
|
GENERAL_NAMES_free(altnames);
|
|
goto failed;
|
|
}
|
|
|
|
/*
|
|
* If there is no subjectAltName extension, check commonName
|
|
* in Subject. While RFC2818 requires to only check "most specific"
|
|
* CN, both Apache and OpenSSL check all CNs, and so do we.
|
|
*/
|
|
sname = X509_get_subject_name(cert);
|
|
|
|
if (sname == NULL)
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
i = -1;
|
|
for (;;)
|
|
{
|
|
i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);
|
|
|
|
if (i < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
entry = X509_NAME_get_entry(sname, i);
|
|
str = X509_NAME_ENTRY_get_data(entry);
|
|
|
|
swTrace("SSL commonName: \"%*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str));
|
|
|
|
if (swSSL_check_name(tls_host_name, str) == SW_OK)
|
|
{
|
|
swTrace("SSL commonName: match");
|
|
goto found;
|
|
}
|
|
}
|
|
swTrace("SSL commonName: no match");
|
|
#endif
|
|
|
|
failed: X509_free(cert);
|
|
return SW_ERR;
|
|
|
|
found: X509_free(cert);
|
|
return SW_OK;
|
|
}
|
|
|
|
int swSSL_verify(swConnection *conn, int allow_self_signed)
|
|
{
|
|
int err = SSL_get_verify_result(conn->ssl);
|
|
switch (err)
|
|
{
|
|
case X509_V_OK:
|
|
return SW_OK;
|
|
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
|
if (allow_self_signed)
|
|
{
|
|
return SW_OK;
|
|
}
|
|
else
|
|
{
|
|
return SW_ERR;
|
|
}
|
|
default:
|
|
swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SSL_VEFIRY_FAILED, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err));
|
|
return SW_ERR;
|
|
}
|
|
|
|
return SW_ERR;
|
|
}
|
|
|
|
int swSSL_get_client_certificate(SSL *ssl, char *buffer, size_t length)
|
|
{
|
|
long len;
|
|
BIO *bio;
|
|
X509 *cert;
|
|
|
|
cert = SSL_get_peer_certificate(ssl);
|
|
if (cert == NULL)
|
|
{
|
|
return SW_ERR;
|
|
}
|
|
|
|
bio = BIO_new(BIO_s_mem());
|
|
if (bio == NULL)
|
|
{
|
|
swWarn("BIO_new() failed.");
|
|
X509_free(cert);
|
|
return SW_ERR;
|
|
}
|
|
|
|
if (PEM_write_bio_X509(bio, cert) == 0)
|
|
{
|
|
swWarn("PEM_write_bio_X509() failed.");
|
|
goto failed;
|
|
}
|
|
|
|
len = BIO_pending(bio);
|
|
if (len < 0 && len > length)
|
|
{
|
|
swWarn("certificate length[%ld] is too big.", len);
|
|
goto failed;
|
|
}
|
|
|
|
int n = BIO_read(bio, buffer, len);
|
|
|
|
BIO_free(bio);
|
|
X509_free(cert);
|
|
|
|
return n;
|
|
|
|
failed:
|
|
|
|
BIO_free(bio);
|
|
X509_free(cert);
|
|
|
|
return SW_ERR;
|
|
}
|
|
|
|
int swSSL_accept(swConnection *conn)
|
|
{
|
|
int n = SSL_do_handshake(conn->ssl);
|
|
/**
|
|
* The TLS/SSL handshake was successfully completed
|
|
*/
|
|
if (n == 1)
|
|
{
|
|
conn->ssl_state = SW_SSL_STATE_READY;
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
|
|
if (conn->ssl->s3)
|
|
{
|
|
conn->ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
|
|
}
|
|
#endif
|
|
#endif
|
|
return SW_READY;
|
|
}
|
|
/**
|
|
* The TLS/SSL handshake was not successful but was shutdown.
|
|
*/
|
|
else if (n == 0)
|
|
{
|
|
return SW_ERROR;
|
|
}
|
|
|
|
long err = SSL_get_error(conn->ssl, n);
|
|
if (err == SSL_ERROR_WANT_READ)
|
|
{
|
|
return SW_WAIT;
|
|
}
|
|
else if (err == SSL_ERROR_WANT_WRITE)
|
|
{
|
|
return SW_WAIT;
|
|
}
|
|
else if (err == SSL_ERROR_SSL)
|
|
{
|
|
swWarn("bad SSL client[%s:%d].", swConnection_get_ip(conn), swConnection_get_port(conn));
|
|
return SW_ERROR;
|
|
}
|
|
//EOF was observed
|
|
else if (err == SSL_ERROR_SYSCALL && n == 0)
|
|
{
|
|
return SW_ERROR;
|
|
}
|
|
swWarn("SSL_do_handshake() failed. Error: %s[%ld|%d].", strerror(errno), err, errno);
|
|
return SW_ERROR;
|
|
}
|
|
|
|
int swSSL_connect(swConnection *conn)
|
|
{
|
|
int n = SSL_connect(conn->ssl);
|
|
if (n == 1)
|
|
{
|
|
conn->ssl_state = SW_SSL_STATE_READY;
|
|
conn->ssl_want_read = 0;
|
|
conn->ssl_want_write = 0;
|
|
|
|
#ifdef SW_LOG_TRACE_OPEN
|
|
const char *ssl_version = SSL_get_version(conn->ssl);
|
|
const char *ssl_cipher = SSL_get_cipher_name(conn->ssl);
|
|
swTraceLog(SW_TRACE_SSL, "connected (%s %s)", ssl_version, ssl_cipher);
|
|
#endif
|
|
|
|
return SW_OK;
|
|
}
|
|
|
|
long err = SSL_get_error(conn->ssl, n);
|
|
if (err == SSL_ERROR_WANT_READ)
|
|
{
|
|
conn->ssl_want_read = 1;
|
|
conn->ssl_want_write = 0;
|
|
conn->ssl_state = SW_SSL_STATE_WAIT_STREAM;
|
|
return SW_OK;
|
|
}
|
|
else if (err == SSL_ERROR_WANT_WRITE)
|
|
{
|
|
conn->ssl_want_read = 0;
|
|
conn->ssl_want_write = 1;
|
|
conn->ssl_state = SW_SSL_STATE_WAIT_STREAM;
|
|
return SW_OK;
|
|
}
|
|
else if (err == SSL_ERROR_ZERO_RETURN)
|
|
{
|
|
swDebug("SSL_connect(fd=%d) closed.", conn->fd);
|
|
return SW_ERR;
|
|
}
|
|
else if (err == SSL_ERROR_SYSCALL)
|
|
{
|
|
if (n)
|
|
{
|
|
SwooleG.error = errno;
|
|
return SW_ERR;
|
|
}
|
|
}
|
|
swWarn("SSL_connect(fd=%d) failed. Error: %s[%ld|%d].", conn->fd, ERR_reason_error_string(err), err, errno);
|
|
|
|
return SW_ERR;
|
|
}
|
|
|
|
int swSSL_sendfile(swConnection *conn, int fd, off_t *offset, size_t size)
|
|
{
|
|
char buf[SW_BUFFER_SIZE_BIG];
|
|
int readn = size > sizeof(buf) ? sizeof(buf) : size;
|
|
|
|
int ret;
|
|
int n = pread(fd, buf, readn, *offset);
|
|
|
|
if (n > 0)
|
|
{
|
|
ret = swSSL_send(conn, buf, n);
|
|
if (ret < 0)
|
|
{
|
|
if (swConnection_error(errno) == SW_ERROR)
|
|
{
|
|
swSysError("write() failed.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*offset += ret;
|
|
}
|
|
swTraceLog(SW_TRACE_REACTOR, "fd=%d, readn=%d, n=%d, ret=%d", fd, readn, n, ret);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
swSysError("pread() failed.");
|
|
return SW_ERR;
|
|
}
|
|
}
|
|
|
|
void swSSL_close(swConnection *conn)
|
|
{
|
|
int n, sslerr, err;
|
|
|
|
if (SSL_in_init(conn->ssl))
|
|
{
|
|
/*
|
|
* OpenSSL 1.0.2f complains if SSL_shutdown() is called during
|
|
* an SSL handshake, while previous versions always return 0.
|
|
* Avoid calling SSL_shutdown() if handshake wasn't completed.
|
|
*/
|
|
SSL_free(conn->ssl);
|
|
conn->ssl = NULL;
|
|
return;
|
|
}
|
|
|
|
SSL_set_quiet_shutdown(conn->ssl, 1);
|
|
SSL_set_shutdown(conn->ssl, SSL_RECEIVED_SHUTDOWN | SSL_SENT_SHUTDOWN);
|
|
|
|
n = SSL_shutdown(conn->ssl);
|
|
|
|
swTrace("SSL_shutdown: %d", n);
|
|
|
|
sslerr = 0;
|
|
|
|
/* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */
|
|
if (n != 1 && ERR_peek_error())
|
|
{
|
|
sslerr = SSL_get_error(conn->ssl, n);
|
|
swTrace("SSL_get_error: %d", sslerr);
|
|
}
|
|
|
|
if (!(n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN))
|
|
{
|
|
err = (sslerr == SSL_ERROR_SYSCALL) ? errno : 0;
|
|
swWarn("SSL_shutdown() failed. Error: %d:%d.", sslerr, err);
|
|
}
|
|
|
|
SSL_free(conn->ssl);
|
|
conn->ssl = NULL;
|
|
}
|
|
|
|
static sw_inline void swSSL_connection_error(swConnection *conn)
|
|
{
|
|
int level = SW_LOG_NOTICE;
|
|
int reason = ERR_GET_REASON(ERR_peek_error());
|
|
|
|
#if 0
|
|
/* handshake failures */
|
|
switch (reason)
|
|
{
|
|
case SSL_R_BAD_CHANGE_CIPHER_SPEC: /* 103 */
|
|
case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG: /* 129 */
|
|
case SSL_R_DIGEST_CHECK_FAILED: /* 149 */
|
|
case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /* 151 */
|
|
case SSL_R_EXCESSIVE_MESSAGE_SIZE: /* 152 */
|
|
case SSL_R_LENGTH_MISMATCH:/* 159 */
|
|
case SSL_R_NO_CIPHERS_PASSED:/* 182 */
|
|
case SSL_R_NO_CIPHERS_SPECIFIED:/* 183 */
|
|
case SSL_R_NO_COMPRESSION_SPECIFIED: /* 187 */
|
|
case SSL_R_NO_SHARED_CIPHER:/* 193 */
|
|
case SSL_R_RECORD_LENGTH_MISMATCH: /* 213 */
|
|
#ifdef SSL_R_PARSE_TLSEXT
|
|
case SSL_R_PARSE_TLSEXT:/* 227 */
|
|
#endif
|
|
case SSL_R_UNEXPECTED_MESSAGE:/* 244 */
|
|
case SSL_R_UNEXPECTED_RECORD:/* 245 */
|
|
case SSL_R_UNKNOWN_ALERT_TYPE: /* 246 */
|
|
case SSL_R_UNKNOWN_PROTOCOL:/* 252 */
|
|
case SSL_R_WRONG_VERSION_NUMBER:/* 267 */
|
|
case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC: /* 281 */
|
|
#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
|
|
case SSL_R_RENEGOTIATE_EXT_TOO_LONG:/* 335 */
|
|
case SSL_R_RENEGOTIATION_ENCODING_ERR:/* 336 */
|
|
case SSL_R_RENEGOTIATION_MISMATCH:/* 337 */
|
|
#endif
|
|
#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
|
|
case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED: /* 338 */
|
|
#endif
|
|
#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
|
|
case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:/* 345 */
|
|
#endif
|
|
#ifdef SSL_R_INAPPROPRIATE_FALLBACK
|
|
case SSL_R_INAPPROPRIATE_FALLBACK: /* 373 */
|
|
#endif
|
|
case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
|
|
case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:/* 1010 */
|
|
case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:/* 1020 */
|
|
case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:/* 1021 */
|
|
case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:/* 1022 */
|
|
case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:/* 1030 */
|
|
case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:/* 1040 */
|
|
case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:/* 1041 */
|
|
case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:/* 1042 */
|
|
case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE: /* 1043 */
|
|
case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:/* 1044 */
|
|
case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:/* 1045 */
|
|
case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:/* 1046 */
|
|
case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:/* 1047 */
|
|
case SSL_R_TLSV1_ALERT_UNKNOWN_CA:/* 1048 */
|
|
case SSL_R_TLSV1_ALERT_ACCESS_DENIED:/* 1049 */
|
|
case SSL_R_TLSV1_ALERT_DECODE_ERROR:/* 1050 */
|
|
case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:/* 1051 */
|
|
case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:/* 1060 */
|
|
case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:/* 1070 */
|
|
case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:/* 1071 */
|
|
case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:/* 1080 */
|
|
case SSL_R_TLSV1_ALERT_USER_CANCELLED:/* 1090 */
|
|
case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION: /* 1100 */
|
|
level = SW_LOG_WARNING;
|
|
break;
|
|
#endif
|
|
|
|
swoole_error_log(level, SW_ERROR_SSL_BAD_PROTOCOL, "SSL connection[%s:%d] protocol error[%d].",
|
|
swConnection_get_ip(conn), swConnection_get_port(conn), reason);
|
|
}
|
|
|
|
ssize_t swSSL_recv(swConnection *conn, void *__buf, size_t __n)
|
|
{
|
|
int n = SSL_read(conn->ssl, __buf, __n);
|
|
if (n < 0)
|
|
{
|
|
int _errno = SSL_get_error(conn->ssl, n);
|
|
switch (_errno)
|
|
{
|
|
case SSL_ERROR_WANT_READ:
|
|
conn->ssl_want_read = 1;
|
|
errno = EAGAIN;
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
conn->ssl_want_write = 1;
|
|
errno = EAGAIN;
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_SSL:
|
|
swSSL_connection_error(conn);
|
|
errno = SW_ERROR_SSL_BAD_CLIENT;
|
|
return SW_ERR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
ssize_t swSSL_send(swConnection *conn, void *__buf, size_t __n)
|
|
{
|
|
int n = SSL_write(conn->ssl, __buf, __n);
|
|
if (n < 0)
|
|
{
|
|
int _errno = SSL_get_error(conn->ssl, n);
|
|
switch (_errno)
|
|
{
|
|
case SSL_ERROR_WANT_READ:
|
|
conn->ssl_want_read = 1;
|
|
errno = EAGAIN;
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
conn->ssl_want_write = 1;
|
|
errno = EAGAIN;
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
return SW_ERR;
|
|
|
|
case SSL_ERROR_SSL:
|
|
swSSL_connection_error(conn);
|
|
errno = SW_ERROR_SSL_BAD_CLIENT;
|
|
return SW_ERR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int swSSL_create(swConnection *conn, SSL_CTX* ssl_context, int flags)
|
|
{
|
|
SSL *ssl = SSL_new(ssl_context);
|
|
if (ssl == NULL)
|
|
{
|
|
swWarn("SSL_new() failed.");
|
|
return SW_ERR;
|
|
}
|
|
if (!SSL_set_fd(ssl, conn->fd))
|
|
{
|
|
long err = ERR_get_error();
|
|
swWarn("SSL_set_fd() failed. Error: %s[%ld]", ERR_reason_error_string(err), err);
|
|
return SW_ERR;
|
|
}
|
|
if (flags & SW_SSL_CLIENT)
|
|
{
|
|
SSL_set_connect_state(ssl);
|
|
}
|
|
else
|
|
{
|
|
SSL_set_accept_state(ssl);
|
|
}
|
|
conn->ssl = ssl;
|
|
conn->ssl_state = 0;
|
|
return SW_OK;
|
|
}
|
|
|
|
void swSSL_free_context(SSL_CTX* ssl_context)
|
|
{
|
|
if (ssl_context)
|
|
{
|
|
SSL_CTX_free(ssl_context);
|
|
}
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_RSA
|
|
static RSA* swSSL_rsa_key_callback(SSL *ssl, int is_export, int key_length)
|
|
{
|
|
static RSA *rsa_tmp = NULL;
|
|
if (rsa_tmp)
|
|
{
|
|
return rsa_tmp;
|
|
}
|
|
|
|
BIGNUM *bn = BN_new();
|
|
if (bn == NULL)
|
|
{
|
|
swWarn("allocation error generating RSA key.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!BN_set_word(bn, RSA_F4) || ((rsa_tmp = RSA_new()) == NULL)
|
|
|| !RSA_generate_key_ex(rsa_tmp, key_length, bn, NULL))
|
|
{
|
|
if (rsa_tmp)
|
|
{
|
|
RSA_free(rsa_tmp);
|
|
}
|
|
rsa_tmp = NULL;
|
|
}
|
|
BN_free(bn);
|
|
return rsa_tmp;
|
|
}
|
|
#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
static int swSSL_set_default_dhparam(SSL_CTX* ssl_context)
|
|
{
|
|
DH *dh;
|
|
static unsigned char dh1024_p[] =
|
|
{ 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, 0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3,
|
|
0xF5, 0x68, 0xB4, 0x2D, 0x4B, 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76, 0xE7,
|
|
0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5, 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1,
|
|
0xD2, 0x87, 0xA2, 0xBC, 0x04, 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, 0x8F,
|
|
0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF, 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6,
|
|
0x62, 0x70, 0xCC, 0x9A, 0x50, 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, 0x58,
|
|
0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B,
|
|
0x9B };
|
|
|
|
static unsigned char dh1024_g[] =
|
|
{ 0x02 };
|
|
dh = DH_new();
|
|
if (dh == NULL)
|
|
{
|
|
swWarn("DH_new() failed");
|
|
return SW_ERR;
|
|
}
|
|
|
|
dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
|
|
dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
|
|
|
|
if (dh->p == NULL || dh->g == NULL)
|
|
{
|
|
DH_free(dh);
|
|
}
|
|
SSL_CTX_set_tmp_dh(ssl_context, dh);
|
|
DH_free(dh);
|
|
return SW_OK;
|
|
}
|
|
#endif
|
|
|
|
static int swSSL_set_ecdh_curve(SSL_CTX* ssl_context)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
EC_KEY *ecdh;
|
|
/*
|
|
* Elliptic-Curve Diffie-Hellman parameters are either "named curves"
|
|
* from RFC 4492 section 5.1.1, or explicitly described curves over
|
|
* binary fields. OpenSSL only supports the "named curves", which provide
|
|
* maximum interoperability.
|
|
*/
|
|
int nid = OBJ_sn2nid(SW_SSL_ECDH_CURVE);
|
|
if (nid == 0)
|
|
{
|
|
swWarn("Unknown curve name \"%s\"", SW_SSL_ECDH_CURVE);
|
|
return SW_ERR;
|
|
}
|
|
|
|
ecdh = EC_KEY_new_by_curve_name(nid);
|
|
if (ecdh == NULL)
|
|
{
|
|
swWarn("Unable to create curve \"%s\"", SW_SSL_ECDH_CURVE);
|
|
return SW_ERR;
|
|
}
|
|
|
|
SSL_CTX_set_options(ssl_context, SSL_OP_SINGLE_ECDH_USE);
|
|
SSL_CTX_set_tmp_ecdh(ssl_context, ecdh);
|
|
|
|
EC_KEY_free(ecdh);
|
|
#endif
|
|
#endif
|
|
|
|
return SW_OK;
|
|
}
|
|
|
|
static int swSSL_set_dhparam(SSL_CTX* ssl_context, char *file)
|
|
{
|
|
DH *dh;
|
|
BIO *bio;
|
|
|
|
bio = BIO_new_file((char *) file, "r");
|
|
if (bio == NULL)
|
|
{
|
|
swWarn("BIO_new_file(%s) failed", file);
|
|
return SW_ERR;
|
|
}
|
|
|
|
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
|
if (dh == NULL)
|
|
{
|
|
swWarn("PEM_read_bio_DHparams(%s) failed", file);
|
|
BIO_free(bio);
|
|
return SW_ERR;
|
|
}
|
|
|
|
SSL_CTX_set_tmp_dh(ssl_context, dh);
|
|
|
|
DH_free(dh);
|
|
BIO_free(bio);
|
|
|
|
return SW_OK;
|
|
}
|
|
|
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
|
|
|
static int swSSL_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg)
|
|
{
|
|
unsigned int srvlen;
|
|
unsigned char *srv;
|
|
|
|
#ifdef SW_USE_HTTP2
|
|
swSSL_config *cfg = arg;
|
|
if (cfg->http_v2)
|
|
{
|
|
srv = (unsigned char *) SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE;
|
|
srvlen = sizeof (SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE) - 1;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
srv = (unsigned char *) SW_SSL_NPN_ADVERTISE;
|
|
srvlen = sizeof (SW_SSL_NPN_ADVERTISE) - 1;
|
|
}
|
|
if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen, in, inlen) != OPENSSL_NPN_NEGOTIATED)
|
|
{
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
return SSL_TLSEXT_ERR_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef TLSEXT_TYPE_next_proto_neg
|
|
|
|
static int swSSL_npn_advertised(SSL *ssl, const uchar **out, uint32_t *outlen, void *arg)
|
|
{
|
|
#ifdef SW_USE_HTTP2
|
|
swSSL_config *cfg = arg;
|
|
if (cfg->http_v2)
|
|
{
|
|
*out = (uchar *) SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE;
|
|
*outlen = sizeof (SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE) - 1;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
*out = (uchar *) SW_SSL_NPN_ADVERTISE;
|
|
*outlen = sizeof(SW_SSL_NPN_ADVERTISE) - 1;
|
|
}
|
|
return SSL_TLSEXT_ERR_OK;
|
|
}
|
|
#endif
|
|
|
|
#endif
|