X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=ustream-openssl.c;h=b080081c172ffc14786ef61b8e5c1e197b4e3003;hb=HEAD;hp=efae44c28dd035022a5a3d783426191455636c1d;hpb=fc0b5ec804ee43c532978dd04ab0509c34baefb0;p=project%2Fustream-ssl.git diff --git a/ustream-openssl.c b/ustream-openssl.c index efae44c..b080081 100644 --- a/ustream-openssl.c +++ b/ustream-openssl.c @@ -18,49 +18,169 @@ #include #include -#include #include "ustream-ssl.h" #include "ustream-internal.h" +#if !defined(HAVE_WOLFSSL) +#include +#endif + +#if defined(HAVE_WOLFSSL) && defined(DEBUG) +#include +#endif + +/* Ciphersuite preference: + * - for server, no weak ciphers are used if you use an ECDSA key. + * - forward-secret (pfs), authenticated (AEAD) ciphers are at the top: + * chacha20-poly1305, the fastest in software, 256-bits + * aes128-gcm, 128-bits + * aes256-gcm, 256-bits + * - key exchange: prefer ECDHE, then DHE (client only) + * - forward-secret ECDSA CBC ciphers (client-only) + * - forward-secret RSA CBC ciphers + * - non-pfs ciphers + * aes128, aes256, 3DES(client only) + */ + +#ifdef WOLFSSL_SSL_H +# define top_ciphers \ + "TLS13-CHACHA20-POLY1305-SHA256:" \ + "TLS13-AES128-GCM-SHA256:" \ + "TLS13-AES256-GCM-SHA384:" \ + ecdhe_aead_ciphers +#else +# define tls13_ciphersuites "TLS_CHACHA20_POLY1305_SHA256:" \ + "TLS_AES_128_GCM_SHA256:" \ + "TLS_AES_256_GCM_SHA384" + +# define top_ciphers \ + ecdhe_aead_ciphers +#endif + +#define ecdhe_aead_ciphers \ + "ECDHE-ECDSA-CHACHA20-POLY1305:" \ + "ECDHE-ECDSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:" \ + "ECDHE-RSA-CHACHA20-POLY1305:" \ + "ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-GCM-SHA384" + +#define dhe_aead_ciphers \ + "DHE-RSA-CHACHA20-POLY1305:" \ + "DHE-RSA-AES128-GCM-SHA256:" \ + "DHE-RSA-AES256-GCM-SHA384" + +#define ecdhe_ecdsa_cbc_ciphers \ + "ECDHE-ECDSA-AES128-SHA:" \ + "ECDHE-ECDSA-AES256-SHA" + +#define ecdhe_rsa_cbc_ciphers \ + "ECDHE-RSA-AES128-SHA:" \ + "ECDHE-RSA-AES256-SHA" + +#define dhe_cbc_ciphers \ + "DHE-RSA-AES128-SHA:" \ + "DHE-RSA-AES256-SHA:" \ + "DHE-DES-CBC3-SHA" + +#define non_pfs_aes \ + "AES128-GCM-SHA256:" \ + "AES256-GCM-SHA384:" \ + "AES128-SHA:" \ + "AES256-SHA" + +#define server_cipher_list \ + top_ciphers ":" \ + ecdhe_rsa_cbc_ciphers ":" \ + non_pfs_aes + +#define client_cipher_list \ + top_ciphers ":" \ + dhe_aead_ciphers ":" \ + ecdhe_ecdsa_cbc_ciphers ":" \ + ecdhe_rsa_cbc_ciphers ":" \ + dhe_cbc_ciphers ":" \ + non_pfs_aes ":" \ + "DES-CBC3-SHA" + __hidden struct ustream_ssl_ctx * __ustream_ssl_context_new(bool server) { - static bool _init = false; + struct ustream_ssl_ctx *ctx; const void *m; SSL_CTX *c; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + static bool _init = false; + if (!_init) { SSL_load_error_strings(); SSL_library_init(); _init = true; } - -#ifdef CYASSL_OPENSSL_H_ - if (server) - m = SSLv23_server_method(); - else - m = SSLv23_client_method(); -#else - if (server) - m = TLSv1_server_method(); - else - m = TLSv1_client_method(); +# ifndef TLS_server_method +# define TLS_server_method SSLv23_server_method +# endif +# ifndef TLS_client_method +# define TLS_client_method SSLv23_client_method +# endif #endif + if (server) { + m = TLS_server_method(); + } else + m = TLS_client_method(); + c = SSL_CTX_new((void *) m); if (!c) return NULL; + ctx = calloc(1, sizeof(*ctx)); + ctx->ssl = c; + +#if defined(HAVE_WOLFSSL) + if (server) + SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); + else + SSL_CTX_set_verify(c, SSL_VERIFY_PEER, NULL); +#else SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); +#endif - return (void *) c; + SSL_CTX_set_options(c, SSL_OP_NO_COMPRESSION | SSL_OP_SINGLE_ECDH_USE | + SSL_OP_CIPHER_SERVER_PREFERENCE); +#if defined(SSL_CTX_set_ecdh_auto) && OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(c, 1); +#elif OPENSSL_VERSION_NUMBER >= 0x10101000L + SSL_CTX_set_ciphersuites(c, tls13_ciphersuites); +#endif + if (server) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_CTX_set_min_proto_version(c, TLS1_2_VERSION); +#else + SSL_CTX_set_options(c, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | + SSL_OP_NO_TLSv1_1); +#endif +#if defined(HAVE_WOLFSSL) + SSL_CTX_set_options(c, SSL_AD_NO_RENEGOTIATION); +#else + SSL_CTX_set_options(c, SSL_OP_NO_RENEGOTIATION); +#endif + + SSL_CTX_set_cipher_list(c, server_cipher_list); + } else { + SSL_CTX_set_cipher_list(c, client_cipher_list); + } + SSL_CTX_set_quiet_shutdown(c, 1); + + return ctx; } __hidden int __ustream_ssl_add_ca_crt_file(struct ustream_ssl_ctx *ctx, const char *file) { int ret; - ret = SSL_CTX_load_verify_locations((void *) ctx, file, NULL); + ret = SSL_CTX_load_verify_locations(ctx->ssl, file, NULL); if (ret < 1) return -1; @@ -71,9 +191,9 @@ __hidden int __ustream_ssl_set_crt_file(struct ustream_ssl_ctx *ctx, const char { int ret; - ret = SSL_CTX_use_certificate_chain_file((void *) ctx, file); + ret = SSL_CTX_use_certificate_chain_file(ctx->ssl, file); if (ret < 1) - ret = SSL_CTX_use_certificate_file((void *) ctx, file, SSL_FILETYPE_ASN1); + ret = SSL_CTX_use_certificate_file(ctx->ssl, file, SSL_FILETYPE_ASN1); if (ret < 1) return -1; @@ -85,9 +205,9 @@ __hidden int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char { int ret; - ret = SSL_CTX_use_PrivateKey_file((void *) ctx, file, SSL_FILETYPE_PEM); + ret = SSL_CTX_use_PrivateKey_file(ctx->ssl, file, SSL_FILETYPE_PEM); if (ret < 1) - ret = SSL_CTX_use_PrivateKey_file((void *) ctx, file, SSL_FILETYPE_ASN1); + ret = SSL_CTX_use_PrivateKey_file(ctx->ssl, file, SSL_FILETYPE_ASN1); if (ret < 1) return -1; @@ -95,134 +215,84 @@ __hidden int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char return 0; } -__hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx) -{ - SSL_CTX_free((void *) ctx); -} - -static void ustream_ssl_error(struct ustream_ssl *us, int ret) +__hidden int __ustream_ssl_set_ciphers(struct ustream_ssl_ctx *ctx, const char *ciphers) { - us->error = ret; - uloop_timeout_set(&us->error_timer, 0); -} + int ret = SSL_CTX_set_cipher_list(ctx->ssl, ciphers); -#ifndef CYASSL_OPENSSL_H_ + if (ret == 0) + return -1; -static bool host_pattern_match(const unsigned char *pattern, const char *cn) -{ - char c; - - for (; (c = tolower(*pattern++)) != 0; cn++) { - if (c != '*') { - if (c != *cn) - return false; - continue; - } - - do { - c = tolower(*pattern++); - } while (c == '*'); - - while (*cn) { - if (c == tolower(*cn) && - host_pattern_match(pattern, cn)) - return true; - if (*cn == '.') - return false; - cn++; - } - - return !c; - } - return !*cn; + return 0; } -static bool host_pattern_match_asn1(ASN1_STRING *asn1, const char *cn) +__hidden int __ustream_ssl_set_require_validation(struct ustream_ssl_ctx *ctx, bool require) { - unsigned char *pattern; - bool ret = false; - - if (ASN1_STRING_to_UTF8(&pattern, asn1) < 0) - return false; - - if (!pattern) - return false; + int mode = SSL_VERIFY_PEER; - if (strlen((char *) pattern) == ASN1_STRING_length(asn1)) - ret = host_pattern_match(pattern, cn); + if (!require) + mode = SSL_VERIFY_NONE; - OPENSSL_free(pattern); + SSL_CTX_set_verify(ctx->ssl, mode, NULL); - return ret; + return 0; } -static bool ustream_ssl_verify_cn_alt(struct ustream_ssl *us, X509 *cert) +__hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx) { - GENERAL_NAMES *alt_names; - int i, n_alt; - - alt_names = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL); - if (!alt_names) - return false; + SSL_CTX_free(ctx->ssl); + if (ctx->debug_bio) + BIO_free(ctx->debug_bio); + free(ctx); +} - n_alt = sk_GENERAL_NAME_num(alt_names); - for (i = 0; i < n_alt; i++) { - const GENERAL_NAME *name = sk_GENERAL_NAME_value(alt_names, i); +__hidden void __ustream_ssl_session_free(struct ustream_ssl *us) +{ + BIO *bio = SSL_get_wbio(us->ssl); + struct bio_ctx *ctx; - if (!name) - continue; + SSL_shutdown(us->ssl); + SSL_free(us->ssl); - if (name->type != GEN_DNS) - continue; + if (!us->conn) + return; - if (host_pattern_match_asn1(name->d.dNSName, us->peer_cn)) - return true; + ctx = BIO_get_data(bio); + if (ctx) { + BIO_meth_free(ctx->meth); + free(ctx); } +} - return false; +static void ustream_ssl_error(struct ustream_ssl *us, int ret) +{ + us->error = ret; + uloop_timeout_set(&us->error_timer, 0); } static bool ustream_ssl_verify_cn(struct ustream_ssl *us, X509 *cert) { - ASN1_STRING *astr; - X509_NAME *xname; - int i, last; + int ret; if (!us->peer_cn) return false; - if (ustream_ssl_verify_cn_alt(us, cert)) - return true; - - xname = X509_get_subject_name(cert); - - last = -1; - while (1) { - i = X509_NAME_get_index_by_NID(xname, NID_commonName, last); - if (i < 0) - break; - - last = i; - } - - if (last < 0) - return false; - - astr = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(xname, last)); - - return host_pattern_match_asn1(astr, us->peer_cn); +# ifndef WOLFSSL_OPENSSL_H_ + ret = X509_check_host(cert, us->peer_cn, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL); +# else + ret = wolfSSL_X509_check_host(cert, us->peer_cn, 0, 0, NULL); +# endif + return ret == 1; } - static void ustream_ssl_verify_cert(struct ustream_ssl *us) { void *ssl = us->ssl; X509 *cert; int res; - cert = SSL_get_peer_certificate(ssl); - if (!cert) - return; +#if defined(HAVE_WOLFSSL) && defined(DEBUG) + showPeer(ssl); +#endif res = SSL_get_verify_result(ssl); if (res != X509_V_OK) { @@ -231,10 +301,65 @@ static void ustream_ssl_verify_cert(struct ustream_ssl *us) return; } + cert = SSL_get_peer_certificate(ssl); + if (!cert) + return; + us->valid_cert = true; us->valid_cn = ustream_ssl_verify_cn(us, cert); + + X509_free(cert); } +#ifdef WOLFSSL_SSL_H +static bool handle_wolfssl_asn_error(struct ustream_ssl *us, int r) +{ + switch (r) { + case ASN_PARSE_E: + case ASN_VERSION_E: + case ASN_GETINT_E: + case ASN_RSA_KEY_E: + case ASN_OBJECT_ID_E: + case ASN_TAG_NULL_E: + case ASN_EXPECT_0_E: + case ASN_BITSTR_E: + case ASN_UNKNOWN_OID_E: + case ASN_DATE_SZ_E: + case ASN_BEFORE_DATE_E: + case ASN_AFTER_DATE_E: + case ASN_SIG_OID_E: + case ASN_TIME_E: + case ASN_INPUT_E: + case ASN_SIG_CONFIRM_E: + case ASN_SIG_HASH_E: + case ASN_SIG_KEY_E: + case ASN_DH_KEY_E: +#if LIBWOLFSSL_VERSION_HEX < 0x05000000 + case ASN_NTRU_KEY_E: +#endif + case ASN_CRIT_EXT_E: + case ASN_ALT_NAME_E: + case ASN_NO_PEM_HEADER: + case ASN_ECC_KEY_E: + case ASN_NO_SIGNER_E: + case ASN_CRL_CONFIRM_E: + case ASN_CRL_NO_SIGNER_E: + case ASN_OCSP_CONFIRM_E: + case ASN_NAME_INVALID_E: + case ASN_NO_SKID: + case ASN_NO_AKID: + case ASN_NO_KEYUSAGE: + case ASN_COUNTRY_SIZE_E: + case ASN_PATHLEN_SIZE_E: + case ASN_PATHLEN_INV_E: + case ASN_SELF_SIGNED_E: + if (us->notify_verify_error) + us->notify_verify_error(us, r, wc_GetErrorString(r)); + return true; + } + + return false; +} #endif __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) @@ -242,15 +367,15 @@ __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) void *ssl = us->ssl; int r; + ERR_clear_error(); + if (us->server) r = SSL_accept(ssl); else r = SSL_connect(ssl); if (r == 1) { -#ifndef CYASSL_OPENSSL_H_ ustream_ssl_verify_cert(us); -#endif return U_SSL_OK; } @@ -258,6 +383,11 @@ __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) if (r == SSL_ERROR_WANT_READ || r == SSL_ERROR_WANT_WRITE) return U_SSL_PENDING; +#ifdef WOLFSSL_SSL_H + if (handle_wolfssl_asn_error(us, r)) + return U_SSL_OK; +#endif + ustream_ssl_error(us, r); return U_SSL_ERROR; } @@ -265,7 +395,11 @@ __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) __hidden int __ustream_ssl_write(struct ustream_ssl *us, const char *buf, int len) { void *ssl = us->ssl; - int ret = SSL_write(ssl, buf, len); + int ret; + + ERR_clear_error(); + + ret = SSL_write(ssl, buf, len); if (ret < 0) { int err = SSL_get_error(ssl, ret); @@ -281,7 +415,11 @@ __hidden int __ustream_ssl_write(struct ustream_ssl *us, const char *buf, int le __hidden int __ustream_ssl_read(struct ustream_ssl *us, char *buf, int len) { - int ret = SSL_read(us->ssl, buf, len); + int ret; + + ERR_clear_error(); + + ret = SSL_read(us->ssl, buf, len); if (ret < 0) { ret = SSL_get_error(us->ssl, ret); @@ -295,3 +433,60 @@ __hidden int __ustream_ssl_read(struct ustream_ssl *us, char *buf, int len) return ret; } +#ifndef WOLFSSL_SSL_H +static long +debug_cb(BIO *bio, int cmd, const char *argp, size_t len, int argi, long argl, + int ret, size_t *processed) +{ + struct ustream_ssl_ctx *ctx = (void *)BIO_get_callback_arg(bio); + char buf[256]; + char *str, *sep; + ssize_t cur_len; + + if (cmd != (BIO_CB_WRITE|BIO_CB_RETURN)) + goto out; + + while (1) { + cur_len = BIO_get_mem_data(bio, (void *)&str); + if (!cur_len) + break; + + sep = memchr(str, '\n', cur_len); + if (!sep) + break; + + cur_len = sep + 1 - str; + if (cur_len >= (ssize_t)sizeof(buf)) + cur_len = sizeof(buf) - 1; + + cur_len = BIO_read(bio, buf, cur_len); + if (cur_len <= 1) + break; + + cur_len--; + buf[cur_len] = 0; + if (ctx->debug_cb) + ctx->debug_cb(ctx->debug_cb_priv, 1, buf); + } + +out: + return ret; +} +#endif + +__hidden void __ustream_ssl_set_debug(struct ustream_ssl_ctx *ctx, int level, + ustream_ssl_debug_cb cb, void *cb_priv) +{ +#ifndef WOLFSSL_SSL_H + if (!ctx->debug_bio) + ctx->debug_bio = BIO_new(BIO_s_mem()); + + ctx->debug_cb = cb; + ctx->debug_cb_priv = cb_priv; + SSL_CTX_set_msg_callback(ctx->ssl, SSL_trace); + SSL_CTX_set_msg_callback_arg(ctx->ssl, ctx->debug_bio); + + BIO_set_callback_ex(ctx->debug_bio, debug_cb); + BIO_set_callback_arg(ctx->debug_bio, (void *)ctx); +#endif +}