diff options
author | Steve Kim <kwstephenkim@google.com> | 2020-04-11 00:09:51 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-04-11 00:09:51 +0000 |
commit | a288eb3375c5d09138d916fe06404f891be4cf4c (patch) | |
tree | 9307b7d4ab253162aa5280e63d6ed64f30408ed0 /lib/tls/openssl | |
parent | b9a9e26052c8692377c286c1a75330bd173d0128 (diff) | |
parent | 368c849f4cfad6689d476c756f6c0142d0ccddf4 (diff) | |
download | libwebsockets-a288eb3375c5d09138d916fe06404f891be4cf4c.tar.gz |
Merge remote-tracking branch 'aosp/upstream-v4.0-stable' am: d349b563de am: 23ecc0a374 am: f60de91656 am: fa945cc49e am: 368c849f4c
Change-Id: I999dabe6c31b8c31102d93576d1131b23786fcfd
Diffstat (limited to 'lib/tls/openssl')
-rw-r--r-- | lib/tls/openssl/lws-genaes.c | 386 | ||||
-rw-r--r-- | lib/tls/openssl/lws-gencrypto.c | 89 | ||||
-rw-r--r-- | lib/tls/openssl/lws-genec.c | 664 | ||||
-rw-r--r-- | lib/tls/openssl/lws-genhash.c | 174 | ||||
-rw-r--r-- | lib/tls/openssl/lws-genrsa.c | 407 | ||||
-rw-r--r-- | lib/tls/openssl/openssl-client.c | 836 | ||||
-rw-r--r-- | lib/tls/openssl/openssl-server.c | 1036 | ||||
-rw-r--r-- | lib/tls/openssl/openssl-ssl.c | 524 | ||||
-rw-r--r-- | lib/tls/openssl/openssl-tls.c | 196 | ||||
-rw-r--r-- | lib/tls/openssl/openssl-x509.c | 667 | ||||
-rw-r--r-- | lib/tls/openssl/private-lib-tls-openssl.h | 62 |
11 files changed, 5041 insertions, 0 deletions
diff --git a/lib/tls/openssl/lws-genaes.c b/lib/tls/openssl/lws-genaes.c new file mode 100644 index 00000000..360cd360 --- /dev/null +++ b/lib/tls/openssl/lws-genaes.c @@ -0,0 +1,386 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * lws_genaes provides an AES abstraction api in lws that works the + * same whether you are using openssl or mbedtls hash functions underneath. + */ +#include "private-lib-core.h" +#include "private-lib-jose.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +int +lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op, + enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el, + enum enum_aes_padding padding, void *engine) +{ + int n = 0; + + ctx->ctx = EVP_CIPHER_CTX_new(); + if (!ctx->ctx) + return -1; + + ctx->mode = mode; + ctx->k = el; + ctx->engine = engine; + ctx->init = 0; + ctx->op = op; + ctx->padding = padding; + + switch (ctx->k->len) { + case 128 / 8: + switch (mode) { + case LWS_GAESM_KW: +#if defined(LWS_HAVE_EVP_aes_128_wrap) + EVP_CIPHER_CTX_set_flags(ctx->ctx, + EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + ctx->cipher = EVP_aes_128_wrap(); + break; +#else + lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", + __func__); + return -1; +#endif + case LWS_GAESM_CBC: + ctx->cipher = EVP_aes_128_cbc(); + break; +#if defined(LWS_HAVE_EVP_aes_128_cfb128) + case LWS_GAESM_CFB128: + ctx->cipher = EVP_aes_128_cfb128(); + break; +#endif +#if defined(LWS_HAVE_EVP_aes_128_cfb8) + case LWS_GAESM_CFB8: + ctx->cipher = EVP_aes_128_cfb8(); + break; +#endif + case LWS_GAESM_CTR: + ctx->cipher = EVP_aes_128_ctr(); + break; + case LWS_GAESM_ECB: + ctx->cipher = EVP_aes_128_ecb(); + break; + case LWS_GAESM_OFB: + ctx->cipher = EVP_aes_128_ofb(); + break; + case LWS_GAESM_XTS: + lwsl_err("%s: AES XTS requires double-length key\n", + __func__); + break; + case LWS_GAESM_GCM: + ctx->cipher = EVP_aes_128_gcm(); + break; + default: + goto bail; + } + break; + + case 192 / 8: + switch (mode) { + case LWS_GAESM_KW: +#if defined(LWS_HAVE_EVP_aes_128_wrap) + EVP_CIPHER_CTX_set_flags(ctx->ctx, + EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + ctx->cipher = EVP_aes_192_wrap(); + break; +#else + lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", + __func__); + return -1; +#endif + case LWS_GAESM_CBC: + ctx->cipher = EVP_aes_192_cbc(); + break; +#if defined(LWS_HAVE_EVP_aes_192_cfb128) + case LWS_GAESM_CFB128: + ctx->cipher = EVP_aes_192_cfb128(); + break; +#endif +#if defined(LWS_HAVE_EVP_aes_192_cfb8) + case LWS_GAESM_CFB8: + ctx->cipher = EVP_aes_192_cfb8(); + break; +#endif + case LWS_GAESM_CTR: + ctx->cipher = EVP_aes_192_ctr(); + break; + case LWS_GAESM_ECB: + ctx->cipher = EVP_aes_192_ecb(); + break; + case LWS_GAESM_OFB: + ctx->cipher = EVP_aes_192_ofb(); + break; + case LWS_GAESM_XTS: + lwsl_err("%s: AES XTS 192 invalid\n", __func__); + goto bail; + case LWS_GAESM_GCM: + ctx->cipher = EVP_aes_192_gcm(); + break; + default: + goto bail; + } + break; + + case 256 / 8: + switch (mode) { + case LWS_GAESM_KW: +#if defined(LWS_HAVE_EVP_aes_128_wrap) + EVP_CIPHER_CTX_set_flags(ctx->ctx, + EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + ctx->cipher = EVP_aes_256_wrap(); + break; +#else + lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n", + __func__); + return -1; +#endif + case LWS_GAESM_CBC: + ctx->cipher = EVP_aes_256_cbc(); + break; +#if defined(LWS_HAVE_EVP_aes_256_cfb128) + case LWS_GAESM_CFB128: + ctx->cipher = EVP_aes_256_cfb128(); + break; +#endif +#if defined(LWS_HAVE_EVP_aes_256_cfb8) + case LWS_GAESM_CFB8: + ctx->cipher = EVP_aes_256_cfb8(); + break; +#endif + case LWS_GAESM_CTR: + ctx->cipher = EVP_aes_256_ctr(); + break; + case LWS_GAESM_ECB: + ctx->cipher = EVP_aes_256_ecb(); + break; + case LWS_GAESM_OFB: + ctx->cipher = EVP_aes_256_ofb(); + break; +#if defined(LWS_HAVE_EVP_aes_128_xts) + case LWS_GAESM_XTS: + ctx->cipher = EVP_aes_128_xts(); + break; +#endif + case LWS_GAESM_GCM: + ctx->cipher = EVP_aes_256_gcm(); + break; + default: + goto bail; + } + break; + + case 512 / 8: + switch (mode) { + case LWS_GAESM_XTS: + ctx->cipher = EVP_aes_256_xts(); + break; + default: + goto bail; + } + break; + + default: + lwsl_err("%s: unsupported AES size %d bits\n", __func__, + ctx->k->len * 8); + goto bail; + } + + switch (ctx->op) { + case LWS_GAESO_ENC: + n = EVP_EncryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine, + NULL, NULL); + EVP_CIPHER_CTX_set_padding(ctx->ctx, padding); + break; + case LWS_GAESO_DEC: + n = EVP_DecryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine, + NULL, NULL); + EVP_CIPHER_CTX_set_padding(ctx->ctx, padding); + break; + } + if (!n) { + lwsl_err("%s: cipher init failed (cipher %p)\n", __func__, + ctx->cipher); + lws_tls_err_describe_clear(); + goto bail; + } + + return 0; +bail: + EVP_CIPHER_CTX_free(ctx->ctx); + ctx->ctx = NULL; + return -1; +} + +int +lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen) +{ + uint8_t buf[256]; + int outl = sizeof(buf), n = 0; + + if (!ctx->ctx) + return 0; + + if (ctx->init) { + switch (ctx->op) { + case LWS_GAESO_ENC: + + if (EVP_EncryptFinal_ex(ctx->ctx, buf, &outl) != 1) { + lwsl_err("%s: enc final failed\n", __func__); + n = -1; + } + + if (ctx->mode == LWS_GAESM_GCM) { + if (EVP_CIPHER_CTX_ctrl(ctx->ctx, + EVP_CTRL_GCM_GET_TAG, + ctx->taglen, tag) != 1) { + lwsl_err("get tag ctrl failed\n"); + //lws_tls_err_describe_clear(); + n = 1; + } + } + if (ctx->mode == LWS_GAESM_CBC) + memcpy(tag, buf, outl); + + break; + + case LWS_GAESO_DEC: + if (EVP_DecryptFinal_ex(ctx->ctx, buf, &outl) != 1) { + lwsl_err("%s: dec final failed\n", __func__); + lws_tls_err_describe_clear(); + n = -1; + } + + break; + } + if (outl) + lwsl_debug("%s: final len %d\n", __func__, outl); + } + + ctx->k = NULL; + EVP_CIPHER_CTX_free(ctx->ctx); + ctx->ctx = NULL; + + return n; +} + +int +lws_genaes_crypt(struct lws_genaes_ctx *ctx, + const uint8_t *in, size_t len, uint8_t *out, + uint8_t *iv_or_nonce_ctr_or_data_unit_16, + uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen) +{ + int n = 0, outl, olen; + + if (!ctx->init) { + + EVP_CIPHER_CTX_set_key_length(ctx->ctx, ctx->k->len); + + if (ctx->mode == LWS_GAESM_GCM) { + n = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_IVLEN, + (int)*nc_or_iv_off, NULL); + if (n != 1) { + lwsl_err("%s: SET_IVLEN failed\n", __func__); + return -1; + } + memcpy(ctx->tag, stream_block_16, taglen); + ctx->taglen = taglen; + } + + switch (ctx->op) { + case LWS_GAESO_ENC: + n = EVP_EncryptInit_ex(ctx->ctx, NULL, NULL, + ctx->k->buf, + iv_or_nonce_ctr_or_data_unit_16); + break; + case LWS_GAESO_DEC: + if (ctx->mode == LWS_GAESM_GCM) + EVP_CIPHER_CTX_ctrl(ctx->ctx, + EVP_CTRL_GCM_SET_TAG, + ctx->taglen, ctx->tag); + n = EVP_DecryptInit_ex(ctx->ctx, NULL, NULL, + ctx->k->buf, + iv_or_nonce_ctr_or_data_unit_16); + break; + } + + if (!n) { + lws_tls_err_describe_clear(); + lwsl_err("%s: init failed (cipher %p)\n", + __func__, ctx->cipher); + + return -1; + } + ctx->init = 1; + } + + if (ctx->mode == LWS_GAESM_GCM && !out) { + /* AAD */ + + if (!len) + return 0; + + switch (ctx->op) { + case LWS_GAESO_ENC: + n = EVP_EncryptUpdate(ctx->ctx, NULL, &olen, in, (int)len); + break; + case LWS_GAESO_DEC: + n = EVP_DecryptUpdate(ctx->ctx, NULL, &olen, in, (int)len); + break; + default: + return -1; + } + if (n != 1) { + lwsl_err("%s: set AAD failed\n", __func__); + lws_tls_err_describe_clear(); + lwsl_hexdump_err(in, len); + return -1; + } + + return 0; + } + + switch (ctx->op) { + case LWS_GAESO_ENC: + n = EVP_EncryptUpdate(ctx->ctx, out, &outl, in, (int)len); + break; + case LWS_GAESO_DEC: + n = EVP_DecryptUpdate(ctx->ctx, out, &outl, in, (int)len); + break; + default: + return -1; + } + + // lwsl_notice("discarding outl %d\n", (int)outl); + + if (!n) { + lwsl_notice("%s: update failed\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} diff --git a/lib/tls/openssl/lws-gencrypto.c b/lib/tls/openssl/lws-gencrypto.c new file mode 100644 index 00000000..efd5e202 --- /dev/null +++ b/lib/tls/openssl/lws-gencrypto.c @@ -0,0 +1,89 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * lws-gencrypto openssl-specific common code + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +int +lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type) +{ + int h = -1; + + switch (hash_type) { + case LWS_GENHASH_TYPE_UNKNOWN: + break; + case LWS_GENHASH_TYPE_MD5: + h = NID_md5; + break; + case LWS_GENHASH_TYPE_SHA1: + h = NID_sha1; + break; + case LWS_GENHASH_TYPE_SHA256: + h = NID_sha256; + break; + case LWS_GENHASH_TYPE_SHA384: + h = NID_sha384; + break; + case LWS_GENHASH_TYPE_SHA512: + h = NID_sha512; + break; + } + + return h; +} + +const EVP_MD * +lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type) +{ + const EVP_MD *h = NULL; + + switch (hash_type) { + case LWS_GENHASH_TYPE_UNKNOWN: + break; + case LWS_GENHASH_TYPE_MD5: + h = EVP_md5(); + break; + case LWS_GENHASH_TYPE_SHA1: + h = EVP_sha1(); + break; + case LWS_GENHASH_TYPE_SHA256: + h = EVP_sha256(); + break; + case LWS_GENHASH_TYPE_SHA384: + h = EVP_sha384(); + break; + case LWS_GENHASH_TYPE_SHA512: + h = EVP_sha512(); + break; + } + + return h; +} diff --git a/lib/tls/openssl/lws-genec.c b/lib/tls/openssl/lws-genec.c new file mode 100644 index 00000000..573a6c0c --- /dev/null +++ b/lib/tls/openssl/lws-genec.c @@ -0,0 +1,664 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * lws_genec provides an EC abstraction api in lws that works the + * same whether you are using openssl or mbedtls crypto functions underneath. + */ +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +#if !defined(LWS_HAVE_ECDSA_SIG_set0) +static void +ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +static int +ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + + return 1; +} +#endif +#if !defined(LWS_HAVE_BN_bn2binpad) +int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen) +{ + int i; + BN_ULONG l; + + bn_check_top(a); + i = BN_num_bytes(a); + + /* Add leading zeroes if necessary */ + if (tolen > i) { + memset(to, 0, tolen - i); + to += tolen - i; + } + while (i--) { + l = a->d[i / BN_BYTES]; + *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; + } + return tolen; +} +#endif + +const struct lws_ec_curves lws_ec_curves[4] = { + /* + * These are the curves we are willing to use by default... + * + * The 3 recommended+ (P-256) and optional curves in RFC7518 7.6 + * + * Specific keys lengths from RFC8422 p20 + */ + { "P-256", NID_X9_62_prime256v1, 32 }, + { "P-384", NID_secp384r1, 48 }, + { "P-521", NID_secp521r1, 66 }, + + { NULL, 0, 0 } +}; + +static int +lws_genec_eckey_import(int nid, EVP_PKEY *pkey, struct lws_gencrypto_keyelem *el) +{ + EC_KEY *ec = EC_KEY_new_by_curve_name(nid); + BIGNUM *bn_d, *bn_x, *bn_y; + int n; + + if (!ec) + return -1; + + /* + * EC_KEY contains + * + * EC_GROUP * group + * EC_POINT * pub_key + * BIGNUM * priv_key (ie, d) + */ + + bn_x = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_X].buf, + el[LWS_GENCRYPTO_EC_KEYEL_X].len, NULL); + if (!bn_x) { + lwsl_err("%s: BN_bin2bn (x) fail\n", __func__); + goto bail; + } + bn_y = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_Y].buf, + el[LWS_GENCRYPTO_EC_KEYEL_Y].len, NULL); + if (!bn_y) { + lwsl_err("%s: BN_bin2bn (y) fail\n", __func__); + goto bail1; + } + + n = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y); + BN_free(bn_x); + BN_free(bn_y); + if (n != 1) { + lwsl_err("%s: EC_KEY_set_public_key_affine_coordinates fail:\n", + __func__); + lws_tls_err_describe_clear(); + goto bail; + } + + if (el[LWS_GENCRYPTO_EC_KEYEL_D].len) { + bn_d = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_D].buf, + el[LWS_GENCRYPTO_EC_KEYEL_D].len, NULL); + if (!bn_d) { + lwsl_err("%s: BN_bin2bn (d) fail\n", __func__); + goto bail; + } + + n = EC_KEY_set_private_key(ec, bn_d); + BN_clear_free(bn_d); + if (n != 1) { + lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__); + goto bail; + } + } + + /* explicitly confirm the key pieces are consistent */ + + if (EC_KEY_check_key(ec) != 1) { + lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__); + goto bail; + } + + n = EVP_PKEY_assign_EC_KEY(pkey, ec); + if (n != 1) { + lwsl_err("%s: EVP_PKEY_set1_EC_KEY failed\n", __func__); + return -1; + } + + return 0; + +bail1: + BN_free(bn_x); +bail: + EC_KEY_free(ec); + + return -1; +} + +static int +lws_genec_keypair_import(struct lws_genec_ctx *ctx, + const struct lws_ec_curves *curve_table, + EVP_PKEY_CTX **pctx, struct lws_gencrypto_keyelem *el) +{ + EVP_PKEY *pkey = NULL; + const struct lws_ec_curves *curve; + + if (el[LWS_GENCRYPTO_EC_KEYEL_CRV].len < 4) + return -2; + + curve = lws_genec_curve(curve_table, + (char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf); + if (!curve) + return -3; + + if ((el[LWS_GENCRYPTO_EC_KEYEL_D].len && + el[LWS_GENCRYPTO_EC_KEYEL_D].len != curve->key_bytes) || + el[LWS_GENCRYPTO_EC_KEYEL_X].len != curve->key_bytes || + el[LWS_GENCRYPTO_EC_KEYEL_Y].len != curve->key_bytes) + return -4; + + ctx->has_private = !!el[LWS_GENCRYPTO_EC_KEYEL_D].len; + + pkey = EVP_PKEY_new(); + if (!pkey) + return -7; + + if (lws_genec_eckey_import(curve->tls_lib_nid, pkey, el)) { + lwsl_err("%s: lws_genec_eckey_import fail\n", __func__); + goto bail; + } + + *pctx = EVP_PKEY_CTX_new(pkey, NULL); + EVP_PKEY_free(pkey); + pkey = NULL; + + if (!*pctx) + goto bail; + + return 0; + +bail: + if (pkey) + EVP_PKEY_free(pkey); + + if (*pctx) { + EVP_PKEY_CTX_free(*pctx); + *pctx = NULL; + } + + return -9; +} + +int +lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context, + const struct lws_ec_curves *curve_table) +{ + ctx->context = context; + ctx->ctx[0] = NULL; + ctx->ctx[1] = NULL; + ctx->curve_table = curve_table; + ctx->genec_alg = LEGENEC_ECDH; + + return 0; +} + +int +lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context, + const struct lws_ec_curves *curve_table) +{ + ctx->context = context; + ctx->ctx[0] = NULL; + ctx->ctx[1] = NULL; + ctx->curve_table = curve_table; + ctx->genec_alg = LEGENEC_ECDSA; + + return 0; +} + +int +lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, + enum enum_lws_dh_side side) +{ + if (ctx->genec_alg != LEGENEC_ECDH) + return -1; + + return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[side], el); +} + +int +lws_genecdsa_set_key(struct lws_genec_ctx *ctx, + struct lws_gencrypto_keyelem *el) +{ + if (ctx->genec_alg != LEGENEC_ECDSA) + return -1; + + return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[0], el); +} + +static void +lws_genec_keypair_destroy(EVP_PKEY_CTX **pctx) +{ + if (!*pctx) + return; + +// lwsl_err("%p\n", EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx))); + +// EC_KEY_free(EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx))); + + EVP_PKEY_CTX_free(*pctx); + *pctx = NULL; +} + +void +lws_genec_destroy(struct lws_genec_ctx *ctx) +{ + if (ctx->ctx[0]) + lws_genec_keypair_destroy(&ctx->ctx[0]); + if (ctx->ctx[1]) + lws_genec_keypair_destroy(&ctx->ctx[1]); +} + +static int +lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, + const char *curve_name, struct lws_gencrypto_keyelem *el) +{ + const struct lws_ec_curves *curve; + const EC_POINT *pubkey; + EVP_PKEY *pkey = NULL; + int ret = -29, n, m; + BIGNUM *bn[3]; + EC_KEY *ec; + + curve = lws_genec_curve(ctx->curve_table, curve_name); + if (!curve) { + lwsl_err("%s: curve '%s' not supported\n", + __func__, curve_name); + + return -22; + } + + ec = EC_KEY_new_by_curve_name(curve->tls_lib_nid); + if (!ec) { + lwsl_err("%s: unknown nid %d\n", __func__, curve->tls_lib_nid); + return -23; + } + + if (EC_KEY_generate_key(ec) != 1) { + lwsl_err("%s: EC_KEY_generate_key failed\n", __func__); + goto bail; + } + + pkey = EVP_PKEY_new(); + if (!pkey) + goto bail; + + if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) { + lwsl_err("%s: EVP_PKEY_assign_EC_KEY failed\n", __func__); + goto bail1; + } + + ctx->ctx[side] = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx->ctx[side]) { + lwsl_err("%s: EVP_PKEY_CTX_new failed\n", __func__); + goto bail1; + } + + /* + * we need to capture the individual element BIGNUMs into + * lws_gencrypto_keyelem, so they can be serialized, used in jwk etc + */ + + pubkey = EC_KEY_get0_public_key(ec); + if (!pubkey) { + lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__); + goto bail1; + } + + bn[0] = BN_new(); + bn[1] = (BIGNUM *)EC_KEY_get0_private_key(ec); + bn[2] = BN_new(); + +#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates) + if (EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec), +#else + if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec), +#endif + pubkey, bn[0], bn[2], NULL) != 1) { + lwsl_err("%s: EC_POINT_get_affine_coordinates_GFp failed\n", + __func__); + goto bail2; + } + + el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve_name) + 1; + el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf = + lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec"); + if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) { + lwsl_err("%s: OOM\n", __func__); + goto bail2; + } + + strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name); + + for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; + n++) { + el[n].len = curve->key_bytes; + el[n].buf = lws_malloc(curve->key_bytes, "ec"); + if (!el[n].buf) + goto bail2; + + m = BN_bn2binpad(bn[n - 1], el[n].buf, el[n].len); + if ((uint32_t)m != el[n].len) + goto bail2; + } + + ctx->has_private = 1; + + ret = 0; + +bail2: + BN_clear_free(bn[0]); + BN_clear_free(bn[2]); +bail1: + EVP_PKEY_free(pkey); +bail: + EC_KEY_free(ec); + + return ret; +} + +int +lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, + const char *curve_name, + struct lws_gencrypto_keyelem *el) +{ + if (ctx->genec_alg != LEGENEC_ECDH) + return -1; + + return lws_genec_new_keypair(ctx, side, curve_name, el); +} + +int +lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name, + struct lws_gencrypto_keyelem *el) +{ + if (ctx->genec_alg != LEGENEC_ECDSA) + return -1; + + return lws_genec_new_keypair(ctx, LDHS_OURS, curve_name, el); +} + +#if 0 +int +lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in, + enum lws_genhash_types hash_type, + uint8_t *sig, size_t sig_len) +{ + const EVP_MD *md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); + EVP_MD_CTX *mdctx = NULL; + + if (ctx->genec_alg != LEGENEC_ECDSA) + return -1; + + if (!md) + return -1; + + mdctx = EVP_MD_CTX_create(); + if (!mdctx) + goto bail; + + if (EVP_DigestSignInit(mdctx, NULL, md, NULL, + EVP_PKEY_CTX_get0_pkey(ctx->ctx))) { + lwsl_err("%s: EVP_DigestSignInit failed\n", __func__); + + goto bail; + } + if (EVP_DigestSignUpdate(mdctx, in, EVP_MD_size(md))) { + lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__); + + goto bail; + } + if (EVP_DigestSignFinal(mdctx, sig, &sig_len)) { + lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__); + + goto bail; + } + + EVP_MD_CTX_free(mdctx); + + return (int)sig_len; +bail: + if (mdctx) + EVP_MD_CTX_free(mdctx); + + return -1; +} +#endif + +int +lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, + enum lws_genhash_types hash_type, int keybits, + uint8_t *sig, size_t sig_len) +{ + int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits); + const BIGNUM *r = NULL, *s = NULL; + ECDSA_SIG *ecdsasig; + EC_KEY *eckey; + + if (ctx->genec_alg != LEGENEC_ECDSA) { + lwsl_notice("%s: ctx alg %d\n", __func__, ctx->genec_alg); + return -1; + } + + if (!ctx->has_private) + return -1; + + if ((int)sig_len < keybytes * 2) { + lwsl_notice("%s: sig buff %d < %d\n", __func__, + (int)sig_len, keybytes * 2); + return -1; + } + + eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0])); + + /* + * The ECDSA P-256 SHA-256 digital signature is generated as follows: + * + * 1. Generate a digital signature of the JWS Signing Input using ECDSA + * P-256 SHA-256 with the desired private key. The output will be + * the pair (R, S), where R and S are 256-bit unsigned integers. + * + * 2. Turn R and S into octet sequences in big-endian order, with each + * array being be 32 octets long. The octet sequence + * representations MUST NOT be shortened to omit any leading zero + * octets contained in the values. + * + * 3. Concatenate the two octet sequences in the order R and then S. + * (Note that many ECDSA implementations will directly produce this + * concatenation as their output.) + * + * 4. The resulting 64-octet sequence is the JWS Signature value. + */ + + ecdsasig = ECDSA_do_sign(in, (int)lws_genhash_size(hash_type), eckey); + EC_KEY_free(eckey); + if (!ecdsasig) { + lwsl_notice("%s: ECDSA_do_sign fail\n", __func__); + goto bail; + } + + ECDSA_SIG_get0(ecdsasig, &r, &s); + + /* + * in the 521-bit case, we have to pad the last byte as it only + * generates 65 bytes + */ + + n = BN_bn2binpad(r, sig, keybytes); + if (n != keybytes) { + lwsl_notice("%s: bignum r fail %d %d\n", __func__, n, keybytes); + goto bail; + } + + n = BN_bn2binpad(s, sig + keybytes, keybytes); + if (n != keybytes) { + lwsl_notice("%s: bignum s fail %d %d\n", __func__, n, keybytes); + goto bail; + } + + ret = 0; + +bail: + if (ecdsasig) + ECDSA_SIG_free(ecdsasig); + + return ret; +} + +/* in is the JWS Signing Input hash */ + +int +lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, + enum lws_genhash_types hash_type, int keybits, + const uint8_t *sig, size_t sig_len) +{ + int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits), + hlen = (int)lws_genhash_size(hash_type); + ECDSA_SIG *ecsig = ECDSA_SIG_new(); + BIGNUM *r = NULL, *s = NULL; + EC_KEY *eckey; + + if (!ecsig) + return -1; + + if (ctx->genec_alg != LEGENEC_ECDSA) + goto bail; + + if ((int)sig_len != keybytes * 2) { + lwsl_err("%s: sig buf too small %d vs %d\n", __func__, + (int)sig_len, keybytes * 2); + goto bail; + } + /* + * 1. The JWS Signature value MUST be a 64-octet sequence. If it is + * not a 64-octet sequence, the validation has failed. + * + * 2. Split the 64-octet sequence into two 32-octet sequences. The + * first octet sequence represents R and the second S. The values R + * and S are represented as octet sequences using the Integer-to- + * OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1] + * (in big-endian octet order). + * + * 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to + * the ECDSA P-256 SHA-256 validator. + */ + + r = BN_bin2bn(sig, keybytes, NULL); + if (!r) { + lwsl_err("%s: BN_bin2bn (r) fail\n", __func__); + goto bail; + } + + s = BN_bin2bn(sig + keybytes, keybytes, NULL); + if (!s) { + lwsl_err("%s: BN_bin2bn (s) fail\n", __func__); + goto bail1; + } + + if (ECDSA_SIG_set0(ecsig, r, s) != 1) { + lwsl_err("%s: ECDSA_SIG_set0 fail\n", __func__); + goto bail1; + } + + eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0])); + + n = ECDSA_do_verify(in, hlen, ecsig, eckey); + EC_KEY_free(eckey); + if (n != 1) { + lwsl_err("%s: ECDSA_do_verify fail\n", __func__); + lws_tls_err_describe_clear(); + goto bail; + } + + ret = 0; + goto bail; + +bail1: + if (r) + BN_free(r); + if (s) + BN_free(s); + +bail: + ECDSA_SIG_free(ecsig); + + return ret; +} + +int +lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss, + int *ss_len) +{ + int len, ret = -1; + EC_KEY *eckey[2]; + + if (!ctx->ctx[LDHS_OURS] || !ctx->ctx[LDHS_THEIRS]) { + lwsl_err("%s: both sides must be set up\n", __func__); + + return -1; + } + + eckey[LDHS_OURS] = EVP_PKEY_get1_EC_KEY( + EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_OURS])); + eckey[LDHS_THEIRS] = EVP_PKEY_get1_EC_KEY( + EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_THEIRS])); + + len = (EC_GROUP_get_degree(EC_KEY_get0_group(eckey[LDHS_OURS])) + 7) / 8; + if (len <= *ss_len) { + *ss_len = ECDH_compute_key(ss, len, + EC_KEY_get0_public_key(eckey[LDHS_THEIRS]), + eckey[LDHS_OURS], NULL); + ret = -(*ss_len < 0); + } + + EC_KEY_free(eckey[LDHS_OURS]); + EC_KEY_free(eckey[LDHS_THEIRS]); + + return ret; +} diff --git a/lib/tls/openssl/lws-genhash.c b/lib/tls/openssl/lws-genhash.c new file mode 100644 index 00000000..04d881fd --- /dev/null +++ b/lib/tls/openssl/lws-genhash.c @@ -0,0 +1,174 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * lws_genhash provides a hash / hmac abstraction api in lws that works the + * same whether you are using openssl or mbedtls hash functions underneath. + */ +#include "libwebsockets.h" +#include <openssl/obj_mac.h> +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +int +lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type) +{ + ctx->type = type; + ctx->mdctx = EVP_MD_CTX_create(); + if (!ctx->mdctx) + return 1; + + switch (ctx->type) { + case LWS_GENHASH_TYPE_MD5: + ctx->evp_type = EVP_md5(); + break; + case LWS_GENHASH_TYPE_SHA1: + ctx->evp_type = EVP_sha1(); + break; + case LWS_GENHASH_TYPE_SHA256: + ctx->evp_type = EVP_sha256(); + break; + case LWS_GENHASH_TYPE_SHA384: + ctx->evp_type = EVP_sha384(); + break; + case LWS_GENHASH_TYPE_SHA512: + ctx->evp_type = EVP_sha512(); + break; + default: + return 1; + } + + if (EVP_DigestInit_ex(ctx->mdctx, ctx->evp_type, NULL) != 1) { + EVP_MD_CTX_destroy(ctx->mdctx); + + return 1; + } + + return 0; +} + +int +lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len) +{ + if (!len) + return 0; + + return EVP_DigestUpdate(ctx->mdctx, in, len) != 1; +} + +int +lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result) +{ + unsigned int len; + int ret = 0; + + if (result) + ret = EVP_DigestFinal_ex(ctx->mdctx, result, &len) != 1; + + (void)len; + + EVP_MD_CTX_destroy(ctx->mdctx); + + return ret; +} + + +int +lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, + const uint8_t *key, size_t key_len) +{ +#if defined(LWS_HAVE_HMAC_CTX_new) + ctx->ctx = HMAC_CTX_new(); + if (!ctx->ctx) + return -1; +#else + HMAC_CTX_init(&ctx->ctx); +#endif + + ctx->evp_type = 0; + ctx->type = type; + + switch (type) { + case LWS_GENHMAC_TYPE_SHA256: + ctx->evp_type = EVP_sha256(); + break; + case LWS_GENHMAC_TYPE_SHA384: + ctx->evp_type = EVP_sha384(); + break; + case LWS_GENHMAC_TYPE_SHA512: + ctx->evp_type = EVP_sha512(); + break; + default: + lwsl_err("%s: unknown HMAC type %d\n", __func__, type); + goto bail; + } + +#if defined(LWS_HAVE_HMAC_CTX_new) + if (HMAC_Init_ex(ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1) +#else + if (HMAC_Init_ex(&ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1) +#endif + goto bail; + + return 0; + +bail: +#if defined(LWS_HAVE_HMAC_CTX_new) + HMAC_CTX_free(ctx->ctx); +#endif + + return -1; +} + +int +lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len) +{ +#if defined(LWS_HAVE_HMAC_CTX_new) + if (HMAC_Update(ctx->ctx, in, len) != 1) +#else + if (HMAC_Update(&ctx->ctx, in, len) != 1) +#endif + return -1; + + return 0; +} + +int +lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result) +{ + unsigned int size = (unsigned int)lws_genhmac_size(ctx->type); +#if defined(LWS_HAVE_HMAC_CTX_new) + int n = HMAC_Final(ctx->ctx, result, &size); + + HMAC_CTX_free(ctx->ctx); +#else + int n = HMAC_Final(&ctx->ctx, result, &size); +#endif + + if (n != 1) + return -1; + + return 0; +} + diff --git a/lib/tls/openssl/lws-genrsa.c b/lib/tls/openssl/lws-genrsa.c new file mode 100644 index 00000000..d2797e7e --- /dev/null +++ b/lib/tls/openssl/lws-genrsa.c @@ -0,0 +1,407 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * lws_genrsa provides an RSA abstraction api in lws that works the + * same whether you are using openssl or mbedtls crypto functions underneath. + */ +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +void +lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el) +{ + lws_gencrypto_destroy_elements(el, LWS_GENCRYPTO_RSA_KEYEL_COUNT); +} + +static int mode_map_crypt[] = { RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING }, + mode_map_sig[] = { RSA_PKCS1_PADDING, RSA_PKCS1_PSS_PADDING }; + +static int +rsa_pkey_wrap(struct lws_genrsa_ctx *ctx, RSA *rsa) +{ + EVP_PKEY *pkey; + + /* we have the RSA object filled up... wrap in a PKEY */ + + pkey = EVP_PKEY_new(); + if (!pkey) + return 1; + + /* bind the PKEY to the RSA key we just prepared */ + + if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) { + lwsl_err("%s: EVP_PKEY_assign_RSA_KEY failed\n", __func__); + goto bail; + } + + /* pepare our PKEY_CTX with the PKEY */ + + ctx->ctx = EVP_PKEY_CTX_new(pkey, NULL); + EVP_PKEY_free(pkey); + pkey = NULL; + if (!ctx->ctx) + goto bail; + + return 0; + +bail: + if (pkey) + EVP_PKEY_free(pkey); + + return 1; +} + +int +lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, + struct lws_context *context, enum enum_genrsa_mode mode, + enum lws_genhash_types oaep_hashid) +{ + int n; + + memset(ctx, 0, sizeof(*ctx)); + ctx->context = context; + ctx->mode = mode; + + /* Step 1: + * + * convert the MPI for e and n to OpenSSL BIGNUMs + */ + + for (n = 0; n < 5; n++) { + ctx->bn[n] = BN_bin2bn(el[n].buf, el[n].len, NULL); + if (!ctx->bn[n]) { + lwsl_notice("mpi load failed\n"); + goto bail; + } + } + + /* Step 2: + * + * assemble the OpenSSL RSA from the BIGNUMs + */ + + ctx->rsa = RSA_new(); + if (!ctx->rsa) { + lwsl_notice("Failed to create RSA\n"); + goto bail; + } + +#if defined(LWS_HAVE_RSA_SET0_KEY) + if (RSA_set0_key(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N], + ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E], + ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D]) != 1) { + lwsl_notice("RSA_set0_key failed\n"); + goto bail; + } + RSA_set0_factors(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P], + ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q]); +#else + ctx->rsa->e = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E]; + ctx->rsa->n = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N]; + ctx->rsa->d = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D]; + ctx->rsa->p = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P]; + ctx->rsa->q = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q]; +#endif + + if (!rsa_pkey_wrap(ctx, ctx->rsa)) + return 0; + +bail: + for (n = 0; n < 5; n++) + if (ctx->bn[n]) { + BN_clear_free(ctx->bn[n]); + ctx->bn[n] = NULL; + } + + if (ctx->rsa) { + RSA_free(ctx->rsa); + ctx->rsa = NULL; + } + + return 1; +} + +int +lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, + enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el, + int bits) +{ + BIGNUM *bn; + int n; + + memset(ctx, 0, sizeof(*ctx)); + ctx->context = context; + ctx->mode = mode; + + ctx->rsa = RSA_new(); + if (!ctx->rsa) { + lwsl_notice("Failed to create RSA\n"); + return -1; + } + + bn = BN_new(); + if (!bn) + goto cleanup_1; + if (BN_set_word(bn, RSA_F4) != 1) { + BN_free(bn); + goto cleanup_1; + } + + n = RSA_generate_key_ex(ctx->rsa, bits, bn, NULL); + BN_clear_free(bn); + if (n != 1) + goto cleanup_1; + +#if defined(LWS_HAVE_RSA_SET0_KEY) + { + const BIGNUM *mpi[5]; + + RSA_get0_key(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_N], + &mpi[LWS_GENCRYPTO_RSA_KEYEL_E], &mpi[LWS_GENCRYPTO_RSA_KEYEL_D]); + RSA_get0_factors(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_P], + &mpi[LWS_GENCRYPTO_RSA_KEYEL_Q]); +#else + { + BIGNUM *mpi[5] = { ctx->rsa->e, ctx->rsa->n, ctx->rsa->d, + ctx->rsa->p, ctx->rsa->q, }; +#endif + for (n = 0; n < 5; n++) + if (BN_num_bytes(mpi[n])) { + el[n].buf = lws_malloc( + BN_num_bytes(mpi[n]), "genrsakey"); + if (!el[n].buf) + goto cleanup; + el[n].len = BN_num_bytes(mpi[n]); + BN_bn2bin(mpi[n], el[n].buf); + } + } + + if (!rsa_pkey_wrap(ctx, ctx->rsa)) + return 0; + +cleanup: + for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) + if (el[n].buf) + lws_free_set_NULL(el[n].buf); +cleanup_1: + RSA_free(ctx->rsa); + ctx->rsa = NULL; + + return -1; +} + +/* + * in_len must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5 + * based padding modes + */ + +int +lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out) +{ + int n = RSA_public_encrypt((int)in_len, in, out, ctx->rsa, + mode_map_crypt[ctx->mode]); + if (n < 0) { + lwsl_err("%s: RSA_public_encrypt failed\n", __func__); + lws_tls_err_describe_clear(); + return -1; + } + + return n; +} + +int +lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out) +{ + int n = RSA_private_encrypt((int)in_len, in, out, ctx->rsa, + mode_map_crypt[ctx->mode]); + if (n < 0) { + lwsl_err("%s: RSA_private_encrypt failed\n", __func__); + lws_tls_err_describe_clear(); + return -1; + } + + return n; +} + +int +lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out, size_t out_max) +{ + int n = RSA_public_decrypt((int)in_len, in, out, ctx->rsa, + mode_map_crypt[ctx->mode]); + if (n < 0) { + lwsl_err("%s: RSA_public_decrypt failed\n", __func__); + return -1; + } + + return n; +} + +int +lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out, size_t out_max) +{ + int n = RSA_private_decrypt((int)in_len, in, out, ctx->rsa, + mode_map_crypt[ctx->mode]); + if (n < 0) { + lwsl_err("%s: RSA_private_decrypt failed\n", __func__); + lws_tls_err_describe_clear(); + return -1; + } + + return n; +} + +int +lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in, + enum lws_genhash_types hash_type, const uint8_t *sig, + size_t sig_len) +{ + int n = lws_gencrypto_openssl_hash_to_NID(hash_type), + h = (int)lws_genhash_size(hash_type); + const EVP_MD *md = NULL; + + if (n < 0) + return -1; + + switch(ctx->mode) { + case LGRSAM_PKCS1_1_5: + n = RSA_verify(n, in, h, (uint8_t *)sig, (int)sig_len, ctx->rsa); + break; + case LGRSAM_PKCS1_OAEP_PSS: + md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); + if (!md) + return -1; + +#if defined(LWS_HAVE_RSA_verify_pss_mgf1) + n = RSA_verify_pss_mgf1(ctx->rsa, in, h, md, NULL, -1, + (uint8_t *)sig, +#else + n = RSA_verify_PKCS1_PSS(ctx->rsa, in, md, (uint8_t *)sig, +#endif + (int)sig_len); + break; + default: + return -1; + } + + if (n != 1) { + lwsl_notice("%s: fail\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} + +int +lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in, + enum lws_genhash_types hash_type, uint8_t *sig, + size_t sig_len) +{ + int n = lws_gencrypto_openssl_hash_to_NID(hash_type), + h = (int)lws_genhash_size(hash_type); + unsigned int used = 0; + EVP_MD_CTX *mdctx = NULL; + const EVP_MD *md = NULL; + + if (n < 0) + return -1; + + switch(ctx->mode) { + case LGRSAM_PKCS1_1_5: + if (RSA_sign(n, in, h, sig, &used, ctx->rsa) != 1) { + lwsl_err("%s: RSA_sign failed\n", __func__); + + goto bail; + } + break; + + case LGRSAM_PKCS1_OAEP_PSS: + + md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); + if (!md) + return -1; + + if (EVP_PKEY_CTX_set_rsa_padding(ctx->ctx, + mode_map_sig[ctx->mode]) != 1) { + lwsl_err("%s: set_rsa_padding failed\n", __func__); + + goto bail; + } + + mdctx = EVP_MD_CTX_create(); + if (!mdctx) + goto bail; + + if (EVP_DigestSignInit(mdctx, NULL, md, NULL, + EVP_PKEY_CTX_get0_pkey(ctx->ctx))) { + lwsl_err("%s: EVP_DigestSignInit failed\n", __func__); + + goto bail; + } + if (EVP_DigestSignUpdate(mdctx, in, EVP_MD_size(md))) { + lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__); + + goto bail; + } + if (EVP_DigestSignFinal(mdctx, sig, &sig_len)) { + lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__); + + goto bail; + } + EVP_MD_CTX_free(mdctx); + used = (int)sig_len; + break; + + default: + return -1; + } + + return used; + +bail: + if (mdctx) + EVP_MD_CTX_free(mdctx); + + return -1; +} + +void +lws_genrsa_destroy(struct lws_genrsa_ctx *ctx) +{ + if (!ctx->ctx) + return; + + EVP_PKEY_CTX_free(ctx->ctx); + ctx->ctx = NULL; + ctx->rsa = NULL; +} diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c new file mode 100644 index 00000000..9a258b10 --- /dev/null +++ b/lib/tls/openssl/openssl-client.c @@ -0,0 +1,836 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +int lws_openssl_describe_cipher(struct lws *wsi); + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +#if !defined(USE_WOLFSSL) + +static int +OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct lws *wsi; + + /* keep old behaviour accepting self-signed server certs */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, + openssl_websocket_private_data_index); + if (!wsi) { + lwsl_err("%s: can't get wsi from ssl privdata\n", + __func__); + + return 0; + } + + if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED) { + lwsl_notice("accepting self-signed " + "certificate (verify_callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || + err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) && + wsi->tls.use_ssl & LCCSCF_ALLOW_INSECURE) { + lwsl_notice("accepting non-trusted certificate\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; /* ok */ + } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID || + err == X509_V_ERR_CERT_HAS_EXPIRED) && + wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED) { + if (err == X509_V_ERR_CERT_NOT_YET_VALID) + lwsl_notice("accepting not yet valid " + "certificate (verify_" + "callback)\n"); + else if (err == X509_V_ERR_CERT_HAS_EXPIRED) + lwsl_notice("accepting expired " + "certificate (verify_" + "callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } + } + } + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + if (!wsi) { + lwsl_err("%s: can't get wsi from ssl privdata\n", __func__); + + return 0; + } + + n = lws_get_context_protocol(wsi->context, 0).callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* keep old behaviour if something wrong with server certs */ + /* if ssl error is overruled in callback and cert is ok, + * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and + * return value is 0 from callback */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { + /* cert validation error was not handled in callback */ + int depth = X509_STORE_CTX_get_error_depth(x509_ctx); + const char *msg = X509_verify_cert_error_string(err); + + lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;" + "depth=%d)\n", msg, preverify_ok, err, depth); + + return preverify_ok; // not ok + } + } + /* + * convert callback return code from 0 = OK to verify callback + * return value 1 = OK + */ + return !n; +} +#endif + + +int +lws_ssl_client_bio_create(struct lws *wsi) +{ + char hostname[128], *p; +#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ + defined(LWS_HAVE_SSL_get0_alpn_selected) + uint8_t openssl_alpn[40]; + const char *alpn_comma = wsi->context->tls.alpn_default; + int n; +#endif + + if (wsi->stash) { + lws_strncpy(hostname, wsi->stash->cis[CIS_HOST], sizeof(hostname)); +#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ + defined(LWS_HAVE_SSL_get0_alpn_selected) + alpn_comma = wsi->stash->cis[CIS_ALPN]; +#endif + } else { +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + if (lws_hdr_copy(wsi, hostname, sizeof(hostname), + _WSI_TOKEN_CLIENT_HOST) <= 0) +#endif + { + lwsl_err("%s: Unable to get hostname\n", __func__); + + return -1; + } + } + + /* + * remove any :port part on the hostname... necessary for network + * connection but typical certificates do not contain it + */ + p = hostname; + while (*p) { + if (*p == ':') { + *p = '\0'; + break; + } + p++; + } + + wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx); + if (!wsi->tls.ssl) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); + lws_tls_err_describe_clear(); + return -1; + } + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->tls.ssl_info_event_mask) + SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); +#endif + +#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host + if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { + X509_VERIFY_PARAM *param = SSL_get0_param(wsi->tls.ssl); + +#if !defined(USE_WOLFSSL) + /* Enable automatic hostname checks */ + X509_VERIFY_PARAM_set_hostflags(param, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); +#endif + // Handle the case where the hostname is an IP address. + if (!X509_VERIFY_PARAM_set1_ip_asc(param, hostname)) + X509_VERIFY_PARAM_set1_host(param, hostname, 0); + } +#else + if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { + lwsl_err("%s: your tls lib is too old to have " + "X509_VERIFY_PARAM_set1_host, failing all client tls\n", + __func__); + return -1; + } +#endif + +#if !defined(USE_WOLFSSL) +#ifndef USE_OLD_CYASSL + /* OpenSSL_client_verify_callback will be called @ SSL_connect() */ + SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER, + OpenSSL_client_verify_callback); +#endif +#endif + +#if !defined(USE_WOLFSSL) + SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->tls.ssl, CYASSL_SNI_HOST_NAME, hostname, + strlen(hostname)); +#endif +#else +#ifdef WOLFSSL_SNI_HOST_NAME + wolfSSL_UseSNI(wsi->tls.ssl, WOLFSSL_SNI_HOST_NAME, hostname, + strlen(hostname)); +#endif +#endif +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->tls.ssl, hostname); +#endif +#endif + +#ifdef USE_WOLFSSL + /* + * wolfSSL/CyaSSL does certificate verification differently + * from OpenSSL. + * If we should ignore the certificate, we need to set + * this before SSL_new and SSL_connect is called. + * Otherwise the connect will simply fail with error code -155 + */ +#ifdef USE_OLD_CYASSL + if (wsi->tls.use_ssl == 2) + CyaSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL); +#else + if (wsi->tls.use_ssl == 2) + wolfSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL); +#endif +#endif /* USE_WOLFSSL */ + + wsi->tls.client_bio = BIO_new_socket((int)(long long)wsi->desc.sockfd, + BIO_NOCLOSE); + SSL_set_bio(wsi->tls.ssl, wsi->tls.client_bio, wsi->tls.client_bio); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); +#endif +#else + BIO_set_nbio(wsi->tls.client_bio, 1); /* nonblocking */ +#endif + +#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ + defined(LWS_HAVE_SSL_get0_alpn_selected) + if (wsi->vhost->tls.alpn) + alpn_comma = wsi->vhost->tls.alpn; + if (wsi->stash) + alpn_comma = wsi->stash->cis[CIS_ALPN]; +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + if (lws_hdr_copy(wsi, hostname, sizeof(hostname), + _WSI_TOKEN_CLIENT_ALPN) > 0) + alpn_comma = hostname; +#endif + + lwsl_info("%s client conn using alpn list '%s'\n", wsi->role_ops->name, alpn_comma); + + n = lws_alpn_comma_to_openssl(alpn_comma, openssl_alpn, + sizeof(openssl_alpn) - 1); + + SSL_set_alpn_protos(wsi->tls.ssl, openssl_alpn, n); +#endif + + SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, + wsi); + + if (wsi->sys_tls_client_cert) { + lws_system_blob_t *b = lws_system_get_blob(wsi->context, + LWS_SYSBLOB_TYPE_CLIENT_CERT_DER, + wsi->sys_tls_client_cert - 1); + const uint8_t *data; + size_t size; + + if (!b) + goto no_client_cert; + + /* + * Set up the per-connection client cert + */ + + size = lws_system_blob_get_size(b); + if (!size) + goto no_client_cert; + + if (lws_system_blob_get_single_ptr(b, &data)) + goto no_client_cert; + + if (SSL_use_certificate_ASN1(wsi->tls.ssl, +#if defined(USE_WOLFSSL) + (unsigned char *) +#endif + data, (int)size) != 1) { + lwsl_err("%s: use_certificate failed\n", __func__); + lws_tls_err_describe_clear(); + goto no_client_cert; + } + + b = lws_system_get_blob(wsi->context, + LWS_SYSBLOB_TYPE_CLIENT_KEY_DER, + wsi->sys_tls_client_cert - 1); + if (!b) + goto no_client_cert; + + size = lws_system_blob_get_size(b); + if (!size) + goto no_client_cert; + + if (lws_system_blob_get_single_ptr(b, &data)) + goto no_client_cert; + + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, wsi->tls.ssl, +#if defined(USE_WOLFSSL) + (unsigned char *) +#endif + + data, (int)size) != 1 && + SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, wsi->tls.ssl, +#if defined(USE_WOLFSSL) + (unsigned char *) +#endif + data, (int)size) != 1) { + lwsl_err("%s: use_privkey failed\n", __func__); + lws_tls_err_describe_clear(); + goto no_client_cert; + } + + if (SSL_check_private_key(wsi->tls.ssl) != 1) { + lwsl_err("Private SSL key doesn't match cert\n"); + lws_tls_err_describe_clear(); + return 1; + } + + lwsl_notice("%s: set system client cert %u\n", __func__, + wsi->sys_tls_client_cert - 1); + } + + return 0; + +no_client_cert: + lwsl_err("%s: unable to set up system client cert %d\n", __func__, + wsi->sys_tls_client_cert - 1); + + return 1; +} + +enum lws_ssl_capable_status +lws_tls_client_connect(struct lws *wsi) +{ +#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ + defined(LWS_HAVE_SSL_get0_alpn_selected) + const unsigned char *prot; + char a[32]; + unsigned int len; +#endif + int m, n; +#if defined(WIN32) || defined(_DEBUG) + int en; +#endif + + errno = 0; + ERR_clear_error(); + n = SSL_connect(wsi->tls.ssl); +#if defined(WIN32) || defined(_DEBUG) + en = errno; +#endif + m = lws_ssl_get_error(wsi, n); + + if (m == SSL_ERROR_SYSCALL +#if defined(WIN32) + && en +#endif + ) { +#if defined(WIN32) || defined(_DEBUG) + lwsl_info("%s: n %d, m %d, errno %d\n", __func__, n, m, en); +#endif + return LWS_SSL_CAPABLE_ERROR; + } + + if (m == SSL_ERROR_SSL) + return LWS_SSL_CAPABLE_ERROR; + + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) + return LWS_SSL_CAPABLE_MORE_SERVICE_READ; + + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) + return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; + + if (n == 1 || m == SSL_ERROR_SYSCALL) { +#if defined(LWS_HAVE_SSL_set_alpn_protos) && \ + defined(LWS_HAVE_SSL_get0_alpn_selected) + SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len); + + if (len >= sizeof(a)) + len = sizeof(a) - 1; + memcpy(a, (const char *)prot, len); + a[len] = '\0'; + + lws_role_call_alpn_negotiated(wsi, (const char *)a); +#endif + lwsl_info("client connect OK\n"); + lws_openssl_describe_cipher(wsi); + return LWS_SSL_CAPABLE_DONE; + } + + if (!n) /* we don't know what he wants, but he says to retry */ + return LWS_SSL_CAPABLE_MORE_SERVICE; + + return LWS_SSL_CAPABLE_ERROR; +} + +int +lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) +{ +#if !defined(USE_WOLFSSL) + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + int n; + + errno = 0; + ERR_clear_error(); + n = SSL_get_verify_result(wsi->tls.ssl); + + lwsl_debug("get_verify says %d\n", n); + + if (n == X509_V_OK) + return 0; + + if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { + lwsl_info("accepting self-signed certificate\n"); + + return 0; + } + if ((n == X509_V_ERR_CERT_NOT_YET_VALID || + n == X509_V_ERR_CERT_HAS_EXPIRED) && + (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) { + lwsl_info("accepting expired certificate\n"); + return 0; + } + if (n == X509_V_ERR_CERT_NOT_YET_VALID) { + lwsl_info("Cert is from the future... " + "probably our clock... accepting...\n"); + return 0; + } + lws_snprintf(ebuf, ebuf_len, + "server's cert didn't look good, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lwsl_info("%s\n", ebuf); + lws_tls_err_describe_clear(); + + return -1; + +#else /* USE_WOLFSSL */ + return 0; +#endif +} + +int +lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, + const uint8_t *der, size_t der_len) +{ + X509_STORE *st; + X509 *x = d2i_X509(NULL, &der, (long)der_len); + int n; + + if (!x) { + lwsl_err("%s: Failed to load DER\n", __func__); + lws_tls_err_describe_clear(); + return 1; + } + + st = SSL_CTX_get_cert_store(vh->tls.ssl_client_ctx); + if (!st) { + lwsl_err("%s: failed to get cert store\n", __func__); + X509_free(x); + return 1; + } + + n = X509_STORE_add_cert(st, x); + if (n != 1) + lwsl_err("%s: failed to add cert\n", __func__); + + X509_free(x); + + return n != 1; +} + +int +lws_tls_client_create_vhost_context(struct lws_vhost *vh, + const struct lws_context_creation_info *info, + const char *cipher_list, + const char *ca_filepath, + const void *ca_mem, + unsigned int ca_mem_len, + const char *cert_filepath, + const void *cert_mem, + unsigned int cert_mem_len, + const char *private_key_filepath) +{ + struct lws_tls_client_reuse *tcr; + X509_STORE *x509_store; + unsigned long error; + SSL_METHOD *method; + EVP_MD_CTX *mdctx; + unsigned int len; + uint8_t hash[32]; + X509 *client_CA; + char c; + int n; + + /* basic openssl init already happened in context init */ + + /* choose the most recent spin of the api */ +#if defined(LWS_HAVE_TLS_CLIENT_METHOD) + method = (SSL_METHOD *)TLS_client_method(); +#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD) + method = (SSL_METHOD *)TLSv1_2_client_method(); +#else + method = (SSL_METHOD *)SSLv23_client_method(); +#endif + + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)vh->context->pt[0].serv_buf)); + return 1; + } + + /* + * OpenSSL client contexts are quite expensive, because they bring in + * the system certificate bundle for each one. So if you have multiple + * vhosts, each with a client context, it can add up to several + * megabytes of heap. In the case the client contexts are configured + * identically, they could perfectly well have shared just the one. + * + * For that reason, use a hash to fingerprint the context configuration + * and prefer to reuse an existing one with the same fingerprint if + * possible. + */ + + mdctx = EVP_MD_CTX_create(); + if (!mdctx) + return 1; + + if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) { + EVP_MD_CTX_destroy(mdctx); + + return 1; + } + + if (info->ssl_client_options_set) + EVP_DigestUpdate(mdctx, &info->ssl_client_options_set, + sizeof(info->ssl_client_options_set)); + +#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) + if (info->ssl_client_options_clear) + EVP_DigestUpdate(mdctx, &info->ssl_client_options_clear, + sizeof(info->ssl_client_options_clear)); +#endif + + if (cipher_list) + EVP_DigestUpdate(mdctx, cipher_list, strlen(cipher_list)); + +#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) + if (info->client_tls_1_3_plus_cipher_list) + EVP_DigestUpdate(mdctx, info->client_tls_1_3_plus_cipher_list, + strlen(info->client_tls_1_3_plus_cipher_list)); +#endif + + if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) { + c = 1; + EVP_DigestUpdate(mdctx, &c, 1); + } + + if (ca_filepath) + EVP_DigestUpdate(mdctx, ca_filepath, strlen(ca_filepath)); + + if (cert_filepath) + EVP_DigestUpdate(mdctx, cert_filepath, strlen(cert_filepath)); + + if (private_key_filepath) + EVP_DigestUpdate(mdctx, private_key_filepath, + strlen(private_key_filepath)); + if (ca_mem && ca_mem_len) + EVP_DigestUpdate(mdctx, ca_mem, ca_mem_len); + + if (cert_mem && cert_mem_len) + EVP_DigestUpdate(mdctx, cert_mem, cert_mem_len); + + len = sizeof(hash); + EVP_DigestFinal_ex(mdctx, hash, &len); + EVP_MD_CTX_destroy(mdctx); + + /* look for existing client context with same config already */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&vh->context->tls.cc_owner)) { + tcr = lws_container_of(p, struct lws_tls_client_reuse, cc_list); + + if (!memcmp(hash, tcr->hash, len)) { + + /* it's a match */ + + tcr->refcount++; + vh->tls.ssl_client_ctx = tcr->ssl_client_ctx; + + lwsl_info("%s: vh %s: reusing client ctx %d: use %d\n", + __func__, vh->name, tcr->index, + tcr->refcount); + + return 0; + } + } lws_end_foreach_dll_safe(p, tp); + + /* no existing one the same... create new client SSL_CTX */ + + errno = 0; + ERR_clear_error(); + vh->tls.ssl_client_ctx = SSL_CTX_new(method); + if (!vh->tls.ssl_client_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)vh->context->pt[0].serv_buf)); + return 1; + } + + tcr = lws_zalloc(sizeof(*tcr), "client ctx tcr"); + if (!tcr) { + SSL_CTX_free(vh->tls.ssl_client_ctx); + return 1; + } + + tcr->ssl_client_ctx = vh->tls.ssl_client_ctx; + tcr->refcount = 1; + memcpy(tcr->hash, hash, len); + tcr->index = vh->context->tls.count_client_contexts++; + lws_dll2_add_head(&tcr->cc_list, &vh->context->tls.cc_owner); + + lwsl_info("%s: vh %s: created new client ctx %d\n", __func__, + vh->name, tcr->index); + + /* bind the tcr to the client context */ + + SSL_CTX_set_ex_data(vh->tls.ssl_client_ctx, + openssl_SSL_CTX_private_data_index, + (char *)tcr); + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vh->tls.ssl_client_ctx, SSL_OP_NO_COMPRESSION); +#endif + + SSL_CTX_set_options(vh->tls.ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + + SSL_CTX_set_mode(vh->tls.ssl_client_ctx, + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS); + + if (info->ssl_client_options_set) + SSL_CTX_set_options(vh->tls.ssl_client_ctx, + info->ssl_client_options_set); + + /* SSL_clear_options introduced in 0.9.8m */ +#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) + if (info->ssl_client_options_clear) + SSL_CTX_clear_options(vh->tls.ssl_client_ctx, + info->ssl_client_options_clear); +#endif + + if (cipher_list) + SSL_CTX_set_cipher_list(vh->tls.ssl_client_ctx, cipher_list); + +#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) + if (info->client_tls_1_3_plus_cipher_list) + SSL_CTX_set_ciphersuites(vh->tls.ssl_client_ctx, + info->client_tls_1_3_plus_cipher_list); +#endif + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(vh->tls.ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!ca_filepath && (!ca_mem || !ca_mem_len)) { + if (!SSL_CTX_load_verify_locations( + vh->tls.ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err("Unable to load SSL Client certs from %s " + "(set by LWS_OPENSSL_CLIENT_CERTS) -- " + "client ssl isn't going to work\n", + LWS_OPENSSL_CLIENT_CERTS); + } else if (ca_filepath) { + if (!SSL_CTX_load_verify_locations( + vh->tls.ssl_client_ctx, ca_filepath, NULL)) { + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work\n", ca_filepath); + lws_tls_err_describe_clear(); + } + else + lwsl_info("loaded ssl_ca_filepath\n"); + } else { + + lws_filepos_t amount = 0; + uint8_t *up1; + const uint8_t *up; + + if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem, + ca_mem_len, &up1, + &amount)) { + lwsl_err("%s: Unable to decode x.509 mem\n", __func__); + lwsl_hexdump_notice(ca_mem, ca_mem_len); + return 1; + } + + up = up1; + client_CA = d2i_X509(NULL, &up, (long)amount); + if (!client_CA) { + lwsl_err("%s: d2i_X509 failed\n", __func__); + lwsl_hexdump_notice(up1, (size_t)amount); + lws_tls_err_describe_clear(); + } else { + x509_store = X509_STORE_new(); + if (!X509_STORE_add_cert(x509_store, client_CA)) { + X509_STORE_free(x509_store); + lwsl_err("Unable to load SSL Client certs from " + "ssl_ca_mem -- client ssl isn't going to " + "work\n"); + lws_tls_err_describe_clear(); + } else { + /* it doesn't increment x509_store ref counter */ + SSL_CTX_set_cert_store(vh->tls.ssl_client_ctx, + x509_store); + lwsl_info("loaded ssl_ca_mem\n"); + } + } + if (client_CA) + X509_free(client_CA); + lws_free(up1); + // lws_tls_client_vhost_extra_cert_mem(vh, ca_mem, ca_mem_len); + } + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (cert_filepath) { + if (lws_tls_use_any_upgrade_check_extant(cert_filepath) != + LWS_TLS_EXTANT_YES && + (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) + return 0; + + lwsl_notice("%s: doing cert filepath %s\n", __func__, + cert_filepath); + n = SSL_CTX_use_certificate_chain_file(vh->tls.ssl_client_ctx, + cert_filepath); + if (n < 1) { + lwsl_err("problem %d getting cert '%s'\n", n, + cert_filepath); + lws_tls_err_describe_clear(); + return 1; + } + lwsl_notice("Loaded client cert %s\n", cert_filepath); + } else if (cert_mem && cert_mem_len) { + n = SSL_CTX_use_certificate_ASN1(vh->tls.ssl_client_ctx, + cert_mem_len, cert_mem); + if (n < 1) { + lwsl_err("%s: problem interpreting client cert\n", + __func__); + lws_tls_err_describe_clear(); + return 1; + } + } + if (private_key_filepath) { + lwsl_notice("%s: doing private key filepath\n", __func__); + lws_ssl_bind_passphrase(vh->tls.ssl_client_ctx, 1, info); + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vh->tls.ssl_client_ctx, + private_key_filepath, SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s'\n", + private_key_filepath); + lws_tls_err_describe_clear(); + return 1; + } + lwsl_notice("Loaded client cert private key %s\n", + private_key_filepath); + + /* verify private key */ + if (!SSL_CTX_check_private_key(vh->tls.ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + } + + return 0; +} + + diff --git a/lib/tls/openssl/openssl-server.c b/lib/tls/openssl/openssl-server.c new file mode 100644 index 00000000..9adfbb89 --- /dev/null +++ b/lib/tls/openssl/openssl-server.c @@ -0,0 +1,1036 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +int lws_openssl_describe_cipher(struct lws *wsi); + +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct lws *wsi; + union lws_tls_cert_info_results ir; + X509 *topcert = X509_STORE_CTX_get_current_cert(x509_ctx); + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = lws_tls_openssl_cert_info(topcert, LWS_TLS_CERT_INFO_COMMON_NAME, + &ir, sizeof(ir.ns.name)); + if (!n) + lwsl_info("%s: client cert CN '%s'\n", __func__, ir.ns.name); + else + lwsl_info("%s: couldn't get client cert CN\n", __func__); + + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + return !n; +} + +int +lws_tls_server_client_cert_verify_config(struct lws_vhost *vh) +{ + int verify_options = SSL_VERIFY_PEER; + + /* as a server, are we requiring clients to identify themselves? */ + + if (!lws_check_opt(vh->options, + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) + return 0; + + if (!lws_check_opt(vh->options, + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) + verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + SSL_CTX_set_session_id_context(vh->tls.ssl_ctx, (uint8_t *)vh->context, + sizeof(void *)); + + /* absolutely require the client cert */ + SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, + OpenSSL_verify_callback); + + return 0; +} + +#if defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) +static int +lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) +{ + struct lws_context *context = (struct lws_context *)arg; + struct lws_vhost *vhost, *vh; + const char *servername; + + if (!ssl) + return SSL_TLSEXT_ERR_NOACK; + + /* + * We can only get ssl accepted connections by using a vhost's ssl_ctx + * find out which listening one took us and only match vhosts on the + * same port. + */ + vh = context->vhost_list; + while (vh) { + if (!vh->being_destroyed && + vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl)) + break; + vh = vh->vhost_next; + } + + if (!vh) { + assert(vh); /* can't match the incoming vh? */ + return SSL_TLSEXT_ERR_OK; + } + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) { + /* the client doesn't know what hostname it wants */ + lwsl_info("SNI: Unknown ServerName\n"); + + return SSL_TLSEXT_ERR_OK; + } + + vhost = lws_select_vhost(context, vh->listen_port, servername); + if (!vhost) { + lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); + + return SSL_TLSEXT_ERR_OK; + } + + lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); + + /* select the ssl ctx from the selected vhost for this conn */ + SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); + + return SSL_TLSEXT_ERR_OK; +} +#endif + +/* + * this may now get called after the vhost creation, when certs become + * available. + */ +int +lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, + const char *cert, const char *private_key, + const char *mem_cert, size_t mem_cert_len, + const char *mem_privkey, size_t mem_privkey_len) +{ +#if !defined(OPENSSL_NO_EC) + const char *ecdh_curve = "prime256v1"; +#if !defined(LWS_WITH_BORINGSSL) && defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) + STACK_OF(X509) *extra_certs = NULL; +#endif + EC_KEY *ecdh, *EC_key = NULL; + EVP_PKEY *pkey; + X509 *x = NULL; + int ecdh_nid; + int KeyType; +#endif + unsigned long error; + lws_filepos_t flen; + uint8_t *p; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int ret; +#endif + int n = lws_tls_generic_cert_checks(vhost, cert, private_key), m; + + if (!cert && !private_key) + n = LWS_TLS_EXTANT_ALTERNATIVE; + + if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) + return 0; + if (n == LWS_TLS_EXTANT_NO) + n = LWS_TLS_EXTANT_ALTERNATIVE; + + if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) + return 1; /* no alternative */ + + if (n == LWS_TLS_EXTANT_ALTERNATIVE) { + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + + /* + * Although we have prepared update certs, we no longer have + * the rights to read our own cert + key we saved. + * + * If we were passed copies in memory buffers, use those + * in favour of the filepaths we normally want. + */ + cert = NULL; + private_key = NULL; + } + + /* + * use the multi-cert interface for backwards compatibility in the + * both simple files case + */ + + if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { + + /* set the local certificate from CertFile */ + m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); + if (m != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + cert, error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + + return 1; + } + + if (private_key) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + private_key, error, + ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + } else { + if (vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->tls.ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } + } + + return 0; + } + + /* otherwise allow for DER or PEM, file or memory image */ + + if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, + mem_cert_len, &p, &flen)) { + lwsl_err("%s: couldn't read cert file\n", __func__); + + return 1; + } + +#if !defined(USE_WOLFSSL) + ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, (int)flen, p); +#else + ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, + (uint8_t *)p, (int)flen, + WOLFSSL_FILETYPE_ASN1); +#endif + lws_free_set_NULL(p); + if (ret != 1) { + lwsl_err("%s: Problem loading cert\n", __func__); + + return 1; + } + + if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, + mem_privkey, mem_privkey_len, + &p, &flen)) { + lwsl_notice("unable to convert memory privkey\n"); + + return 1; + } + +#if !defined(USE_WOLFSSL) + ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, + (long)(long long)flen); + if (ret != 1) { + ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, + vhost->tls.ssl_ctx, p, + (long)(long long)flen); + } +#else + ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, flen, + WOLFSSL_FILETYPE_ASN1); +#endif + lws_free_set_NULL(p); + if (ret != 1) { + lwsl_notice("unable to use memory privkey\n"); + + return 1; + } + +#else + /* + * Although we have prepared update certs, we no longer have + * the rights to read our own cert + key we saved. + * + * If we were passed copies in memory buffers, use those + * instead. + * + * The passed memory-buffer cert image is in DER, and the + * memory-buffer private key image is PEM. + */ +#ifndef USE_WOLFSSL + if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, + mem_cert_len, &p, &flen)) { + lwsl_err("%s: couldn't convert pem to der\n", __func__); + return 1; + } + if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, + (int)flen, + (uint8_t *)p) != 1) { +#else + if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, + (uint8_t *)mem_cert, + (int)mem_cert_len, + WOLFSSL_FILETYPE_ASN1) != 1) { + +#endif + lwsl_err("Problem loading update cert\n"); + + return 1; + } + + if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, + mem_privkey, mem_privkey_len, + &p, &flen)) { + lwsl_notice("unable to convert memory privkey\n"); + + return 1; + } +#ifndef USE_WOLFSSL + if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, + vhost->tls.ssl_ctx, p, + (long)(long long)flen) != 1) { +#else + if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, + flen, WOLFSSL_FILETYPE_ASN1) != 1) { +#endif + lwsl_notice("unable to use memory privkey\n"); + + return 1; + } + + goto check_key; + } + + /* set the local certificate from CertFile */ + m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); + if (m != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + cert, error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + + return 1; + } + + if (n != LWS_TLS_EXTANT_ALTERNATIVE && private_key) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + private_key, error, + ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + } else { + if (vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->tls.ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } + } + +check_key: +#endif + + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + + return 1; + } + + +#if !defined(OPENSSL_NO_EC) + if (vhost->tls.ecdh_curve[0]) + ecdh_curve = vhost->tls.ecdh_curve; + + ecdh_nid = OBJ_sn2nid(ecdh_curve); + if (NID_undef == ecdh_nid) { + lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); + return 1; + } + + ecdh = EC_KEY_new_by_curve_name(ecdh_nid); + if (NULL == ecdh) { + lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + + lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); + + if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) + lwsl_notice(" Using ECDH certificate support\n"); + + /* Get X509 certificate from ssl context */ +#if !defined(LWS_WITH_BORINGSSL) +#if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) + x = sk_X509_value(vhost->tls.ssl_ctx->extra_certs, 0); +#else + SSL_CTX_get_extra_chain_certs_only(vhost->tls.ssl_ctx, &extra_certs); + if (extra_certs) + x = sk_X509_value(extra_certs, 0); + else + lwsl_info("%s: no extra certs\n", __func__); +#endif + if (!x) { + //lwsl_err("%s: x is NULL\n", __func__); + goto post_ecdh; + } +#else + return 0; +#endif + /* Get the public key from certificate */ + pkey = X509_get_pubkey(x); + if (!pkey) { + lwsl_err("%s: pkey is NULL\n", __func__); + + return 1; + } + /* Get the key type */ + KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey)); + + if (EVP_PKEY_EC != KeyType) { + lwsl_notice("Key type is not EC\n"); + return 0; + } + /* Get the key */ + EC_key = EVP_PKEY_get1_EC_KEY(pkey); + /* Set ECDH parameter */ + if (!EC_key) { + lwsl_err("%s: ECDH key is NULL \n", __func__); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, EC_key); + + EC_KEY_free(EC_key); +#else + lwsl_notice(" OpenSSL doesn't support ECDH\n"); +#endif +#if !defined(OPENSSL_NO_EC) && !defined(LWS_WITH_BORINGSSL) +post_ecdh: +#endif + vhost->tls.skipped_certs = 0; + + return 0; +} + +int +lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, + struct lws_vhost *vhost, struct lws *wsi) +{ + unsigned long error; + SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); + + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->tls.ssl_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + + SSL_CTX_set_ex_data(vhost->tls.ssl_ctx, + openssl_SSL_CTX_private_data_index, + (char *)vhost->context); + /* Disable SSLv2 and SSLv3 */ + SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_SSLv2 | + SSL_OP_NO_SSLv3); +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(vhost->tls.ssl_ctx, info->ssl_cipher_list); + +#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) + if (info->tls1_3_plus_cipher_list) + SSL_CTX_set_ciphersuites(vhost->tls.ssl_ctx, + info->tls1_3_plus_cipher_list); +#endif + +#if !defined(OPENSSL_NO_TLSEXT) + SSL_CTX_set_tlsext_servername_callback(vhost->tls.ssl_ctx, + lws_ssl_server_name_cb); + SSL_CTX_set_tlsext_servername_arg(vhost->tls.ssl_ctx, vhost->context); +#endif + + if (info->ssl_ca_filepath && + !SSL_CTX_load_verify_locations(vhost->tls.ssl_ctx, + info->ssl_ca_filepath, NULL)) { + lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", + __func__); + } + + if (info->ssl_options_set) + SSL_CTX_set_options(vhost->tls.ssl_ctx, info->ssl_options_set); + +/* SSL_clear_options introduced in 0.9.8m */ +#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) + if (info->ssl_options_clear) + SSL_CTX_clear_options(vhost->tls.ssl_ctx, + info->ssl_options_clear); +#endif + + lwsl_info(" SSL options 0x%lX\n", + (unsigned long)SSL_CTX_get_options(vhost->tls.ssl_ctx)); + if (!vhost->tls.use_ssl || + (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) + return 0; + + lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, 0, info); + + return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, + info->ssl_private_key_filepath, + info->server_ssl_cert_mem, + info->server_ssl_cert_mem_len, + info->server_ssl_private_key_mem, + info->server_ssl_private_key_mem_len); +} + +int +lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) +{ +#if !defined(USE_WOLFSSL) + BIO *bio; +#endif + + errno = 0; + ERR_clear_error(); + wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx); + if (wsi->tls.ssl == NULL) { + lwsl_err("SSL_new failed: %d (errno %d)\n", + lws_ssl_get_error(wsi, 0), errno); + + lws_tls_err_describe_clear(); + return 1; + } + + SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, wsi); + SSL_set_fd(wsi->tls.ssl, (int)(long long)accept_fd); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); +#endif +#else + + SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS); + bio = SSL_get_rbio(wsi->tls.ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); + bio = SSL_get_wbio(wsi->tls.ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); +#endif + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->tls.ssl_info_event_mask) + SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); +#endif + + return 0; +} + +int +lws_tls_server_abort_connection(struct lws *wsi) +{ + SSL_shutdown(wsi->tls.ssl); + SSL_free(wsi->tls.ssl); + + return 0; +} + +enum lws_ssl_capable_status +lws_tls_server_accept(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + union lws_tls_cert_info_results ir; + int m, n; + + errno = 0; + ERR_clear_error(); + n = SSL_accept(wsi->tls.ssl); + + wsi->skip_fallback = 1; + + if (n == 1) { + n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, + sizeof(ir.ns.name)); + if (!n) + lwsl_notice("%s: client cert CN '%s'\n", __func__, + ir.ns.name); + else + lwsl_info("%s: no client cert CN\n", __func__); + + lws_openssl_describe_cipher(wsi); + + if (SSL_pending(wsi->tls.ssl) && + lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) + lws_dll2_add_head(&wsi->tls.dll_pending_tls, + &pt->tls.dll_pending_tls_owner); + + return LWS_SSL_CAPABLE_DONE; + } + + m = lws_ssl_get_error(wsi, n); + lws_tls_err_describe_clear(); + + if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) + return LWS_SSL_CAPABLE_ERROR; + + if (m == SSL_ERROR_WANT_READ || + (m != SSL_ERROR_ZERO_RETURN && SSL_want_read(wsi->tls.ssl))) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: WANT_READ change_pollfd failed\n", + __func__); + return LWS_SSL_CAPABLE_ERROR; + } + + lwsl_info("SSL_ERROR_WANT_READ: m %d\n", m); + return LWS_SSL_CAPABLE_MORE_SERVICE_READ; + } + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_info("%s: WANT_WRITE change_pollfd failed\n", + __func__); + return LWS_SSL_CAPABLE_ERROR; + } + return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; + } + + return LWS_SSL_CAPABLE_ERROR; +} + +#if defined(LWS_WITH_ACME) +static int +lws_tls_openssl_rsa_new_key(RSA **rsa, int bits) +{ + BIGNUM *bn = BN_new(); + int n; + + if (!bn) + return 1; + + if (BN_set_word(bn, RSA_F4) != 1) { + BN_free(bn); + return 1; + } + + *rsa = RSA_new(); + if (!*rsa) { + BN_free(bn); + return 1; + } + + n = RSA_generate_key_ex(*rsa, bits, bn, NULL); + BN_free(bn); + if (n == 1) + return 0; + + RSA_free(*rsa); + *rsa = NULL; + + return 1; +} + +struct lws_tls_ss_pieces { + X509 *x509; + EVP_PKEY *pkey; + RSA *rsa; +}; + +int +lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, + const char *san_b) +{ + GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); + GENERAL_NAME *gen = NULL; + ASN1_IA5STRING *ia5 = NULL; + X509_NAME *name; + + if (!gens) + return 1; + + vhost->tls.ss = lws_zalloc(sizeof(*vhost->tls.ss), "sni cert"); + if (!vhost->tls.ss) { + GENERAL_NAMES_free(gens); + return 1; + } + + vhost->tls.ss->x509 = X509_new(); + if (!vhost->tls.ss->x509) + goto bail; + + ASN1_INTEGER_set(X509_get_serialNumber(vhost->tls.ss->x509), 1); + X509_gmtime_adj(X509_get_notBefore(vhost->tls.ss->x509), 0); + X509_gmtime_adj(X509_get_notAfter(vhost->tls.ss->x509), 3600); + + vhost->tls.ss->pkey = EVP_PKEY_new(); + if (!vhost->tls.ss->pkey) + goto bail0; + + if (lws_tls_openssl_rsa_new_key(&vhost->tls.ss->rsa, 4096)) + goto bail1; + + if (!EVP_PKEY_assign_RSA(vhost->tls.ss->pkey, vhost->tls.ss->rsa)) + goto bail2; + + X509_set_pubkey(vhost->tls.ss->x509, vhost->tls.ss->pkey); + + name = X509_get_subject_name(vhost->tls.ss->x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, + (unsigned char *)"GB", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + (unsigned char *)"somecompany", -1, -1, 0); + if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, + (unsigned char *)"temp.acme.invalid", + -1, -1, 0) != 1) { + lwsl_notice("failed to add CN\n"); + goto bail2; + } + X509_set_issuer_name(vhost->tls.ss->x509, name); + + /* add the SAN payloads */ + + gen = GENERAL_NAME_new(); + ia5 = ASN1_IA5STRING_new(); + if (!ASN1_STRING_set(ia5, san_a, -1)) { + lwsl_notice("failed to set ia5\n"); + GENERAL_NAME_free(gen); + goto bail2; + } + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + + if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, + gens, 0, X509V3_ADD_APPEND) != 1) + goto bail2; + + GENERAL_NAMES_free(gens); + + if (san_b && san_b[0]) { + gens = sk_GENERAL_NAME_new_null(); + gen = GENERAL_NAME_new(); + ia5 = ASN1_IA5STRING_new(); + if (!ASN1_STRING_set(ia5, san_a, -1)) { + lwsl_notice("failed to set ia5\n"); + GENERAL_NAME_free(gen); + goto bail2; + } + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + + if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, + gens, 0, X509V3_ADD_APPEND) != 1) + goto bail2; + + GENERAL_NAMES_free(gens); + } + + /* sign it with our private key */ + if (!X509_sign(vhost->tls.ss->x509, vhost->tls.ss->pkey, EVP_sha256())) + goto bail2; + +#if 0 + {/* useful to take a sample of a working cert for mbedtls to crib */ + FILE *fp = fopen("/tmp/acme-temp-cert", "w+"); + + i2d_X509_fp(fp, vhost->tls.ss->x509); + fclose(fp); + } +#endif + + /* tell the vhost to use our crafted certificate */ + SSL_CTX_use_certificate(vhost->tls.ssl_ctx, vhost->tls.ss->x509); + /* and to use our generated private key */ + SSL_CTX_use_PrivateKey(vhost->tls.ssl_ctx, vhost->tls.ss->pkey); + + return 0; + +bail2: + RSA_free(vhost->tls.ss->rsa); +bail1: + EVP_PKEY_free(vhost->tls.ss->pkey); +bail0: + X509_free(vhost->tls.ss->x509); +bail: + lws_free(vhost->tls.ss); + GENERAL_NAMES_free(gens); + + return 1; +} + +void +lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) +{ + if (!vhost->tls.ss) + return; + + EVP_PKEY_free(vhost->tls.ss->pkey); + X509_free(vhost->tls.ss->x509); + lws_free_set_NULL(vhost->tls.ss); +} + +static int +lws_tls_openssl_add_nid(X509_NAME *name, int nid, const char *value) +{ + X509_NAME_ENTRY *e; + int n; + + if (!value || value[0] == '\0') + value = "none"; + + e = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, + (unsigned char *)value, -1); + if (!e) + return 1; + n = X509_NAME_add_entry(name, e, -1, 0); + X509_NAME_ENTRY_free(e); + + return n != 1; +} + +static int nid_list[] = { + NID_countryName, /* LWS_TLS_REQ_ELEMENT_COUNTRY */ + NID_stateOrProvinceName, /* LWS_TLS_REQ_ELEMENT_STATE */ + NID_localityName, /* LWS_TLS_REQ_ELEMENT_LOCALITY */ + NID_organizationName, /* LWS_TLS_REQ_ELEMENT_ORGANIZATION */ + NID_commonName, /* LWS_TLS_REQ_ELEMENT_COMMON_NAME */ + NID_subject_alt_name, /* LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME */ + NID_pkcs9_emailAddress, /* LWS_TLS_REQ_ELEMENT_EMAIL */ +}; + +int +lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], + uint8_t *csr, size_t csr_len, char **privkey_pem, + size_t *privkey_len) +{ + uint8_t *csr_in = csr; + RSA *rsakey; + X509_REQ *req; + X509_NAME *subj; + EVP_PKEY *pkey; + char *p, *end; + BIO *bio; + long bio_len; + int n, ret = -1; + + if (lws_tls_openssl_rsa_new_key(&rsakey, 4096)) + return -1; + + pkey = EVP_PKEY_new(); + if (!pkey) + goto bail0; + if (!EVP_PKEY_set1_RSA(pkey, rsakey)) + goto bail1; + + req = X509_REQ_new(); + if (!req) + goto bail1; + + X509_REQ_set_pubkey(req, pkey); + + subj = X509_NAME_new(); + if (!subj) + goto bail2; + + for (n = 0; n < LWS_TLS_REQ_ELEMENT_COUNT; n++) + if (elements[n] && + lws_tls_openssl_add_nid(subj, nid_list[n], + elements[n])) { + lwsl_notice("%s: failed to add element %d\n", + __func__, n); + goto bail3; + } + + if (X509_REQ_set_subject_name(req, subj) != 1) + goto bail3; + + if (elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]) { + STACK_OF(X509_EXTENSION) *exts; + X509_EXTENSION *ext; + char san[256]; + + exts = sk_X509_EXTENSION_new_null(); + if (!exts) + goto bail3; + + lws_snprintf(san, sizeof(san), "DNS:%s,DNS:%s", + elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME], + elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]); + + ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, + san); + if (!ext) { + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + goto bail3; + } + sk_X509_EXTENSION_push(exts, ext); + + if (!X509_REQ_add_extensions(req, exts)) { + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + goto bail3; + } + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + + if (!X509_REQ_sign(req, pkey, EVP_sha256())) + goto bail3; + + /* + * issue the CSR as PEM to a BIO, and translate to b64urlenc without + * headers, trailers, or whitespace + */ + + bio = BIO_new(BIO_s_mem()); + if (!bio) + goto bail3; + + if (PEM_write_bio_X509_REQ(bio, req) != 1) { + BIO_free(bio); + goto bail3; + } + + bio_len = BIO_get_mem_data(bio, &p); + end = p + bio_len; + + /* strip the header line */ + while (p < end && *p != '\n') + p++; + + while (p < end && csr_len) { + if (*p == '\n') { + p++; + continue; + } + + if (*p == '-') + break; + + if (*p == '+') + *csr++ = '-'; + else + if (*p == '/') + *csr++ = '_'; + else + *csr++ = *p; + p++; + csr_len--; + } + BIO_free(bio); + if (!csr_len) { + lwsl_notice("%s: need %ld for CSR\n", __func__, bio_len); + goto bail3; + } + + /* + * Also return the private key as a PEM in memory + * (platform may not have a filesystem) + */ + bio = BIO_new(BIO_s_mem()); + if (!bio) + goto bail3; + + if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, 0, NULL) != 1) { + BIO_free(bio); + goto bail3; + } + bio_len = BIO_get_mem_data(bio, &p); + *privkey_pem = malloc(bio_len); /* malloc so user code can own / free */ + *privkey_len = (size_t)bio_len; + if (!*privkey_pem) { + lwsl_notice("%s: need %ld for private key\n", __func__, + bio_len); + BIO_free(bio); + goto bail3; + } + memcpy(*privkey_pem, p, (int)(long long)bio_len); + BIO_free(bio); + + ret = lws_ptr_diff(csr, csr_in); + +bail3: + X509_NAME_free(subj); +bail2: + X509_REQ_free(req); +bail1: + EVP_PKEY_free(pkey); +bail0: + RSA_free(rsakey); + + return ret; +} +#endif diff --git a/lib/tls/openssl/openssl-ssl.c b/lib/tls/openssl/openssl-ssl.c new file mode 100644 index 00000000..1f2fd825 --- /dev/null +++ b/lib/tls/openssl/openssl-ssl.c @@ -0,0 +1,524 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" +#include <errno.h> + +int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +/* + * Care: many openssl apis return 1 for success. These are translated to the + * lws convention of 0 for success. + */ + +int lws_openssl_describe_cipher(struct lws *wsi) +{ +#if !defined(LWS_WITH_NO_LOGS) && !defined(USE_WOLFSSL) + int np = -1; + SSL *s = wsi->tls.ssl; + + SSL_get_cipher_bits(s, &np); + lwsl_info("%s: wsi %p: %s, %s, %d bits, %s\n", __func__, wsi, + SSL_get_cipher_name(s), SSL_get_cipher(s), np, + SSL_get_cipher_version(s)); +#endif + + return 0; +} + +int lws_ssl_get_error(struct lws *wsi, int n) +{ + int m; + + if (!wsi->tls.ssl) + return 99; + + m = SSL_get_error(wsi->tls.ssl, n); + lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->tls.ssl, n, m, + errno); + + assert (errno != 9); + + return m; +} + +static int +lws_context_init_ssl_pem_passwd_cb(char *buf, int size, int rwflag, + void *userdata) +{ + struct lws_context_creation_info * info = + (struct lws_context_creation_info *)userdata; + + strncpy(buf, info->ssl_private_key_password, size); + buf[size - 1] = '\0'; + + return (int)strlen(buf); +} + +static int +lws_context_init_ssl_pem_passwd_client_cb(char *buf, int size, int rwflag, + void *userdata) +{ + struct lws_context_creation_info * info = + (struct lws_context_creation_info *)userdata; + const char *p = info->ssl_private_key_password; + + if (info->client_ssl_private_key_password) + p = info->client_ssl_private_key_password; + + strncpy(buf, p, size); + buf[size - 1] = '\0'; + + return (int)strlen(buf); +} + +void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, int is_client, + const struct lws_context_creation_info *info) +{ + if (!info->ssl_private_key_password && + !info->client_ssl_private_key_password) + return; + /* + * password provided, set ssl callback and user data + * for checking password which will be trigered during + * SSL_CTX_use_PrivateKey_file function + */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info); + SSL_CTX_set_default_passwd_cb(ssl_ctx, is_client ? + lws_context_init_ssl_pem_passwd_client_cb: + lws_context_init_ssl_pem_passwd_cb); +} + +static void +lws_ssl_destroy_client_ctx(struct lws_vhost *vhost) +{ + struct lws_tls_client_reuse *tcr; + + if (vhost->tls.user_supplied_ssl_ctx || !vhost->tls.ssl_client_ctx) + return; + + tcr = SSL_CTX_get_ex_data(vhost->tls.ssl_client_ctx, + openssl_SSL_CTX_private_data_index); + + if (!tcr || --tcr->refcount) + return; + + SSL_CTX_free(vhost->tls.ssl_client_ctx); + vhost->tls.ssl_client_ctx = NULL; + + vhost->context->tls.count_client_contexts--; + + lws_dll2_remove(&tcr->cc_list); + lws_free(tcr); +} + +void +lws_ssl_destroy(struct lws_vhost *vhost) +{ + if (!lws_check_opt(vhost->context->options, + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return; + + if (vhost->tls.ssl_ctx) + SSL_CTX_free(vhost->tls.ssl_ctx); + + lws_ssl_destroy_client_ctx(vhost); + +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + /* not needed after 1.1.0 */ +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && \ + (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +} + +int +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n = 0, m; + + if (!wsi->tls.ssl) + return lws_ssl_capable_read_no_ssl(wsi, buf, len); + + lws_stats_bump(pt, LWSSTATS_C_API_READ, 1); + + errno = 0; + ERR_clear_error(); + n = SSL_read(wsi->tls.ssl, buf, len); +#if defined(LWS_PLAT_FREERTOS) + if (!n && errno == LWS_ENOTCONN) { + lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); + return LWS_SSL_CAPABLE_ERROR; + } +#endif +#if defined(LWS_WITH_STATS) + if (!wsi->seen_rx && wsi->accept_start_us) { + lws_stats_bump(pt, LWSSTATS_US_SSL_RX_DELAY_AVG, + lws_now_usecs() - + wsi->accept_start_us); + lws_stats_bump(pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1); + wsi->seen_rx = 1; + } +#endif + + + lwsl_debug("%p: SSL_read says %d\n", wsi, n); + /* manpage: returning 0 means connection shut down + * + * 2018-09-10: https://github.com/openssl/openssl/issues/1903 + * + * So, in summary, if you get a 0 or -1 return from SSL_read() / + * SSL_write(), you should call SSL_get_error(): + * + * - If you get back SSL_ERROR_RETURN_ZERO then you know the connection + * has been cleanly shutdown by the peer. To fully close the + * connection you may choose to call SSL_shutdown() to send a + * close_notify back. + * + * - If you get back SSL_ERROR_SSL then some kind of internal or + * protocol error has occurred. More details will be on the SSL error + * queue. You can also call SSL_get_shutdown(). If this indicates a + * state of SSL_RECEIVED_SHUTDOWN then you know a fatal alert has + * been received from the peer (if it had been a close_notify then + * SSL_get_error() would have returned SSL_ERROR_RETURN_ZERO). + * SSL_ERROR_SSL is considered fatal - you should not call + * SSL_shutdown() in this case. + * + * - If you get back SSL_ERROR_SYSCALL then some kind of fatal (i.e. + * non-retryable) error has occurred in a system call. + */ + if (n <= 0) { + m = lws_ssl_get_error(wsi, n); + lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno); + if (m == SSL_ERROR_ZERO_RETURN) /* cleanly shut down */ + return LWS_SSL_CAPABLE_ERROR; + + /* hm not retryable.. could be 0 size pkt or error */ + + if (m == SSL_ERROR_SSL || m == SSL_ERROR_SYSCALL || + errno == LWS_ENOTCONN) { + + /* unclean, eg closed conn */ + + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; + } + + /* retryable? */ + + if (SSL_want_read(wsi->tls.ssl)) { + lwsl_debug("%s: WANT_READ\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + if (SSL_want_write(wsi->tls.ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + /* keep on trucking it seems */ + } + + lws_stats_bump(pt, LWSSTATS_B_READ, n); + +#if defined(LWS_WITH_SERVER_STATUS) + if (wsi->vhost) + wsi->vhost->conn_stats.rx += n; +#endif + + // lwsl_hexdump_err(buf, n); + +#if defined(LWS_WITH_DETAILED_LATENCY) + if (context->detailed_latency_cb) { + wsi->detlat.req_size = len; + wsi->detlat.acc_size = n; + wsi->detlat.type = LDLT_READ; + wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = + lws_now_usecs() - pt->ust_left_poll; + wsi->detlat.latencies[LAT_DUR_USERCB] = 0; + lws_det_lat_cb(wsi->context, &wsi->detlat); + } +#endif + + /* + * if it was our buffer that limited what we read, + * check if SSL has additional data pending inside SSL buffers. + * + * Because these won't signal at the network layer with POLLIN + * and if we don't realize, this data will sit there forever + */ + if (n != len) + goto bail; + if (!wsi->tls.ssl) + goto bail; + + if (SSL_pending(wsi->tls.ssl) && + lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) + lws_dll2_add_head(&wsi->tls.dll_pending_tls, + &pt->tls.dll_pending_tls_owner); + + return n; +bail: + lws_ssl_remove_wsi_from_buffered_list(wsi); + + return n; +} + +int +lws_ssl_pending(struct lws *wsi) +{ + if (!wsi->tls.ssl) + return 0; + + return SSL_pending(wsi->tls.ssl); +} + +int +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) +{ + int n, m; + + // lwsl_notice("%s: len %d\n", __func__, len); + // lwsl_hexdump_notice(buf, len); + + if (!wsi->tls.ssl) + return lws_ssl_capable_write_no_ssl(wsi, buf, len); + + errno = 0; + ERR_clear_error(); + n = SSL_write(wsi->tls.ssl, buf, len); + if (n > 0) + return n; + + m = lws_ssl_get_error(wsi, n); + if (m != SSL_ERROR_SYSCALL) { + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) { + lwsl_notice("%s: want read\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { + lws_set_blocking_send(wsi); + + lwsl_debug("%s: want write\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + } + + lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL)); + lws_tls_err_describe_clear(); + + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; +} + +void +lws_ssl_info_callback(const SSL *ssl, int where, int ret) +{ + struct lws *wsi; + struct lws_context *context; + struct lws_ssl_info si; + +#ifndef USE_WOLFSSL + context = (struct lws_context *)SSL_CTX_get_ex_data( + SSL_get_SSL_CTX(ssl), + openssl_SSL_CTX_private_data_index); +#else + context = (struct lws_context *)SSL_CTX_get_ex_data( + SSL_get_SSL_CTX((SSL*) ssl), + openssl_SSL_CTX_private_data_index); +#endif + if (!context) + return; + wsi = wsi_from_fd(context, SSL_get_fd(ssl)); + if (!wsi) + return; + + if (!(where & wsi->vhost->tls.ssl_info_event_mask)) + return; + + si.where = where; + si.ret = ret; + + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_SSL_INFO, + wsi->user_space, &si, 0)) + lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1); +} + + +int +lws_ssl_close(struct lws *wsi) +{ + lws_sockfd_type n; + + if (!wsi->tls.ssl) + return 0; /* not handled */ + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + /* kill ssl callbacks, because we will remove the fd from the + * table linking it to the wsi + */ + if (wsi->vhost->tls.ssl_info_event_mask) + SSL_set_info_callback(wsi->tls.ssl, NULL); +#endif + + n = SSL_get_fd(wsi->tls.ssl); + if (!wsi->socket_is_permanently_unusable) + SSL_shutdown(wsi->tls.ssl); + compatible_close(n); + SSL_free(wsi->tls.ssl); + wsi->tls.ssl = NULL; + + lws_tls_restrict_return(wsi->context); + + // lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, + // wsi->context->simultaneous_ssl_restriction, + // wsi->context->simultaneous_ssl); + + return 1; /* handled */ +} + +void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) +{ + if (vhost->tls.ssl_ctx) + SSL_CTX_free(vhost->tls.ssl_ctx); + + lws_ssl_destroy_client_ctx(vhost); + +#if defined(LWS_WITH_ACME) + lws_tls_acme_sni_cert_destroy(vhost); +#endif +} + +void +lws_ssl_context_destroy(struct lws_context *context) +{ +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + // after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +} + +lws_tls_ctx * +lws_tls_ctx_from_wsi(struct lws *wsi) +{ + if (!wsi->tls.ssl) + return NULL; + + return SSL_get_SSL_CTX(wsi->tls.ssl); +} + +enum lws_ssl_capable_status +__lws_tls_shutdown(struct lws *wsi) +{ + int n; + + errno = 0; + ERR_clear_error(); + n = SSL_shutdown(wsi->tls.ssl); + lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd); + switch (n) { + case 1: /* successful completion */ + n = shutdown(wsi->desc.sockfd, SHUT_WR); + return LWS_SSL_CAPABLE_DONE; + + case 0: /* needs a retry */ + __lws_change_pollfd(wsi, 0, LWS_POLLIN); + return LWS_SSL_CAPABLE_MORE_SERVICE; + + default: /* fatal error, or WANT */ + n = SSL_get_error(wsi->tls.ssl, n); + if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { + if (SSL_want_read(wsi->tls.ssl)) { + lwsl_debug("(wants read)\n"); + __lws_change_pollfd(wsi, 0, LWS_POLLIN); + return LWS_SSL_CAPABLE_MORE_SERVICE_READ; + } + if (SSL_want_write(wsi->tls.ssl)) { + lwsl_debug("(wants write)\n"); + __lws_change_pollfd(wsi, 0, LWS_POLLOUT); + return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; + } + } + return LWS_SSL_CAPABLE_ERROR; + } +} + + +static int +tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt) +{ + return lws_tls_fake_POLLIN_for_buffered(pt); +} + +const struct lws_tls_ops tls_ops_openssl = { + /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_openssl, +}; diff --git a/lib/tls/openssl/openssl-tls.c b/lib/tls/openssl/openssl-tls.c new file mode 100644 index 00000000..0b8bfe55 --- /dev/null +++ b/lib/tls/openssl/openssl-tls.c @@ -0,0 +1,196 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +extern int openssl_websocket_private_data_index, +openssl_SSL_CTX_private_data_index; + +char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { + switch (status) { + case SSL_ERROR_NONE: + return lws_strncpy(buf, "SSL_ERROR_NONE", len); + case SSL_ERROR_ZERO_RETURN: + return lws_strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); + case SSL_ERROR_WANT_READ: + return lws_strncpy(buf, "SSL_ERROR_WANT_READ", len); + case SSL_ERROR_WANT_WRITE: + return lws_strncpy(buf, "SSL_ERROR_WANT_WRITE", len); + case SSL_ERROR_WANT_CONNECT: + return lws_strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); + case SSL_ERROR_WANT_ACCEPT: + return lws_strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); + case SSL_ERROR_WANT_X509_LOOKUP: + return lws_strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); + case SSL_ERROR_SYSCALL: + switch (ret) { + case 0: + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); + return buf; + case -1: +#ifndef LWS_PLAT_OPTEE + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", + strerror(errno)); +#else + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); +#endif + return buf; + default: + return strncpy(buf, "SSL_ERROR_SYSCALL", len); + } + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + default: + return "SSL_ERROR_UNKNOWN"; + } +} + +void +lws_tls_err_describe_clear(void) +{ + char buf[160]; + unsigned long l; + + do { + l = ERR_get_error(); + if (!l) + break; + + ERR_error_string_n(l, buf, sizeof(buf)); + lwsl_info(" openssl error: %s\n", buf); + } while (l); + lwsl_info("\n"); +} + +#if LWS_MAX_SMP != 1 + +static pthread_mutex_t *openssl_mutexes; + +static void +lws_openssl_lock_callback(int mode, int type, const char *file, int line) +{ + (void)file; + (void)line; + + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&openssl_mutexes[type]); + else + pthread_mutex_unlock(&openssl_mutexes[type]); +} + +static unsigned long +lws_openssl_thread_id(void) +{ + return (unsigned long)pthread_self(); +} +#endif + + +int +lws_context_init_ssl_library(const struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_info(" Compiled with CyaSSL support\n"); +#else + lwsl_info(" Compiled with wolfSSL support\n"); +#endif +#else +#if defined(LWS_WITH_BORINGSSL) + lwsl_info(" Compiled with BoringSSL support\n"); +#else + lwsl_info(" Compiled with OpenSSL support\n"); +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_info(" SSL disabled: no " + "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* basic openssl init */ + + lwsl_info("Doing SSL library init\n"); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#endif +#if defined(LWS_WITH_NETWORK) + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); +#endif + +#if LWS_MAX_SMP != 1 + { + int n; + + openssl_mutexes = (pthread_mutex_t *) + OPENSSL_malloc(CRYPTO_num_locks() * + sizeof(openssl_mutexes[0])); + + for (n = 0; n < CRYPTO_num_locks(); n++) + pthread_mutex_init(&openssl_mutexes[n], NULL); + + /* + * These "functions" disappeared in later OpenSSL which is + * already threadsafe. + */ + + (void)lws_openssl_thread_id; + (void)lws_openssl_lock_callback; + + CRYPTO_set_id_callback(lws_openssl_thread_id); + CRYPTO_set_locking_callback(lws_openssl_lock_callback); + } +#endif + + return 0; +} + +void +lws_context_deinit_ssl_library(struct lws_context *context) +{ +#if LWS_MAX_SMP != 1 + int n; + + if (!lws_check_opt(context->options, + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return; + + CRYPTO_set_locking_callback(NULL); + + for (n = 0; n < CRYPTO_num_locks(); n++) + pthread_mutex_destroy(&openssl_mutexes[n]); + + OPENSSL_free(openssl_mutexes); +#endif +} diff --git a/lib/tls/openssl/openssl-x509.c b/lib/tls/openssl/openssl-x509.c new file mode 100644 index 00000000..bd6e0162 --- /dev/null +++ b/lib/tls/openssl/openssl-x509.c @@ -0,0 +1,667 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +#if !defined(LWS_PLAT_OPTEE) +static int +dec(char c) +{ + return c - '0'; +} +#endif + +static time_t +lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as) +{ +#if !defined(LWS_PLAT_OPTEE) + + const char *p = (const char *)as->data; + struct tm t; + + /* [YY]YYMMDDHHMMSSZ */ + + memset(&t, 0, sizeof(t)); + + if (strlen(p) == 13) { + t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100; + p += 2; + } else { + t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) + + (dec(p[2]) * 10) + dec(p[3]); + p += 4; + } + t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1; + p += 2; + t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1; + p += 2; + t.tm_hour = (dec(p[0]) * 10) + dec(p[1]); + p += 2; + t.tm_min = (dec(p[0]) * 10) + dec(p[1]); + p += 2; + t.tm_sec = (dec(p[0]) * 10) + dec(p[1]); + t.tm_isdst = 0; + + return mktime(&t); +#else + return (time_t)-1; +#endif +} + +int +lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + X509_NAME *xn; +#if !defined(LWS_PLAT_OPTEE) + char *p; +#endif + + if (!x509) + return -1; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore) +#define X509_get_notBefore(x) X509_getm_notBefore(x) +#define X509_get_notAfter(x) X509_getm_notAfter(x) +#endif + + switch (type) { + case LWS_TLS_CERT_INFO_VALIDITY_FROM: + buf->time = lws_tls_openssl_asn1time_to_unix( + X509_get_notBefore(x509)); + if (buf->time == (time_t)-1) + return -1; + break; + + case LWS_TLS_CERT_INFO_VALIDITY_TO: + buf->time = lws_tls_openssl_asn1time_to_unix( + X509_get_notAfter(x509)); + if (buf->time == (time_t)-1) + return -1; + break; + + case LWS_TLS_CERT_INFO_COMMON_NAME: +#if defined(LWS_PLAT_OPTEE) + return -1; +#else + xn = X509_get_subject_name(x509); + if (!xn) + return -1; + X509_NAME_oneline(xn, buf->ns.name, (int)len - 2); + p = strstr(buf->ns.name, "/CN="); + if (p) + memmove(buf->ns.name, p + 4, strlen(p + 4) + 1); + buf->ns.len = (int)strlen(buf->ns.name); + return 0; +#endif + case LWS_TLS_CERT_INFO_ISSUER_NAME: + xn = X509_get_issuer_name(x509); + if (!xn) + return -1; + X509_NAME_oneline(xn, buf->ns.name, (int)len - 1); + buf->ns.len = (int)strlen(buf->ns.name); + return 0; + + case LWS_TLS_CERT_INFO_USAGE: +#if defined(LWS_HAVE_X509_get_key_usage) + buf->usage = X509_get_key_usage(x509); + break; +#else + return -1; +#endif + + case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY: + { +#ifndef USE_WOLFSSL + size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL); + uint8_t *tmp, *ptmp; + + if (!klen || klen > len) + return -1; + + tmp = (uint8_t *)OPENSSL_malloc(klen); + if (!tmp) + return -1; + + ptmp = tmp; + if (i2d_X509_PUBKEY( + X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen || + !ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) { + lwsl_info("%s: cert public key extraction failed\n", + __func__); + if (ptmp) + OPENSSL_free(tmp); + + return -1; + } + + buf->ns.len = (int)klen; + memcpy(buf->ns.name, tmp, klen); + OPENSSL_free(tmp); +#endif + return 0; + } + default: + return -1; + } + + return 0; +} + +int +lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + return lws_tls_openssl_cert_info(x509->cert, type, buf, len); +} + +#if defined(LWS_WITH_NETWORK) +int +lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ +#if defined(LWS_HAVE_SSL_CTX_get0_certificate) + X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx); + + return lws_tls_openssl_cert_info(x509, type, buf, len); +#else + lwsl_notice("openssl is too old to support %s\n", __func__); + + return -1; +#endif +} + + + +int +lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + int rc = 0; + X509 *x509; + + wsi = lws_get_network_wsi(wsi); + + x509 = SSL_get_peer_certificate(wsi->tls.ssl); + + if (!x509) { + lwsl_debug("no peer cert\n"); + + return -1; + } + + switch (type) { + case LWS_TLS_CERT_INFO_VERIFIED: + buf->verified = SSL_get_verify_result(wsi->tls.ssl) == + X509_V_OK; + break; + default: + rc = lws_tls_openssl_cert_info(x509, type, buf, len); + } + + X509_free(x509); + + return rc; +} +#endif + +int +lws_x509_create(struct lws_x509_cert **x509) +{ + *x509 = lws_malloc(sizeof(**x509), __func__); + if (*x509) + (*x509)->cert = NULL; + + return !(*x509); +} + +int +lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len) +{ + BIO* bio = BIO_new(BIO_s_mem()); + + BIO_write(bio, pem, (int)len); + x509->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!x509->cert) { + lwsl_err("%s: unable to parse PEM cert\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} + +int +lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted, + const char *common_name) +{ + char c[32], *p; + int ret; + + if (common_name) { + X509_NAME *xn = X509_get_subject_name(x509->cert); + if (!xn) + return -1; + X509_NAME_oneline(xn, c, (int)sizeof(c) - 2); + p = strstr(c, "/CN="); + if (p) + p = p + 4; + else + p = c; + + if (strcmp(p, common_name)) { + lwsl_err("%s: common name mismatch\n", __func__); + return -1; + } + } + + ret = X509_check_issued(trusted->cert, x509->cert); + if (ret != X509_V_OK) { + lwsl_err("%s: unable to verify cert relationship\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} + +#if defined(LWS_WITH_JOSE) +int +lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, + const char *curves, int rsa_min_bits) +{ + int id, n, ret = -1, count; + ASN1_OBJECT *obj = NULL; + const EC_POINT *ecpoint; + const EC_GROUP *ecgroup; + EC_KEY *ecpub = NULL; + X509_PUBKEY *pubkey; + RSA *rsapub = NULL; + BIGNUM *mpi[4]; + EVP_PKEY *pkey; + + memset(jwk, 0, sizeof(*jwk)); + + pubkey = X509_get_X509_PUBKEY(x509->cert); + if (!pubkey) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + id = OBJ_obj2nid(obj); + if (id == NID_undef) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id)); + + pkey = X509_get_pubkey(x509->cert); + if (!pkey) { + lwsl_notice("%s: unable to extract pubkey", __func__); + + goto bail; + } + + switch (id) { + case NID_X9_62_id_ecPublicKey: + lwsl_debug("%s: EC key\n", __func__); + jwk->kty = LWS_GENCRYPTO_KTY_EC; + + if (!curves) { + lwsl_err("%s: ec curves not allowed\n", __func__); + + goto bail1; + } + + ecpub = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecpub) { + lwsl_notice("%s: missing EC pubkey\n", __func__); + + goto bail1; + } + + ecpoint = EC_KEY_get0_public_key(ecpub); + if (!ecpoint) { + lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__); + goto bail2; + } + + ecgroup = EC_KEY_get0_group(ecpub); + if (!ecgroup) { + lwsl_err("%s: EC_KEY_get0_group failed\n", __func__); + goto bail2; + } + + /* validate the curve against ones we allow */ + + if (lws_genec_confirm_curve_allowed_by_tls_id(curves, + EC_GROUP_get_curve_name(ecgroup), jwk)) + /* already logged */ + goto bail2; + + mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL; + mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */ + mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL; + mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */ + +#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates) + if (EC_POINT_get_affine_coordinates(ecgroup, ecpoint, +#else + if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint, +#endif + mpi[LWS_GENCRYPTO_EC_KEYEL_X], + mpi[LWS_GENCRYPTO_EC_KEYEL_Y], + NULL) != 1) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + lwsl_err("%s: EC_POINT_get_aff failed\n", __func__); + goto bail2; + } + count = LWS_GENCRYPTO_EC_KEYEL_COUNT; + n = LWS_GENCRYPTO_EC_KEYEL_X; + break; + + case NID_rsaEncryption: + lwsl_debug("%s: rsa key\n", __func__); + jwk->kty = LWS_GENCRYPTO_KTY_RSA; + + rsapub = EVP_PKEY_get1_RSA(pkey); + if (!rsapub) { + lwsl_notice("%s: missing RSA pubkey\n", __func__); + + goto bail1; + } + + if ((size_t)RSA_size(rsapub) * 8 < (size_t)rsa_min_bits) { + lwsl_err("%s: key bits %d less than minimum %d\n", + __func__, RSA_size(rsapub) * 8, rsa_min_bits); + + goto bail2; + } + +#if defined(LWS_HAVE_RSA_SET0_KEY) + /* we don't need d... but the api wants to write it */ + RSA_get0_key(rsapub, + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N], + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E], + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]); +#else + mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e; + mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n; + mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL; +#endif + count = LWS_GENCRYPTO_RSA_KEYEL_D; + n = LWS_GENCRYPTO_RSA_KEYEL_E; + break; + default: + lwsl_err("%s: unknown NID\n", __func__); + goto bail2; + } + + for (; n < count; n++) { + if (!mpi[n]) + continue; + jwk->e[n].len = BN_num_bytes(mpi[n]); + jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp"); + if (!jwk->e[n].buf) { + if (id == NID_X9_62_id_ecPublicKey) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + } + goto bail2; + } + BN_bn2bin(mpi[n], jwk->e[n].buf); + } + + if (id == NID_X9_62_id_ecPublicKey) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + } + + ret = 0; + +bail2: + if (id == NID_X9_62_id_ecPublicKey) + EC_KEY_free(ecpub); + else + RSA_free(rsapub); + +bail1: + EVP_PKEY_free(pkey); +bail: + /* jwk destroy will clean any partial state */ + if (ret) + lws_jwk_destroy(jwk); + + return ret; +} + +static int +lws_x509_jwk_privkey_pem_pp_cb(char *buf, int size, int rwflag, void *u) +{ + const char *pp = (const char *)u; + int n = strlen(pp); + + if (n > size - 1) + return -1; + + memcpy(buf, pp, n + 1); + + return n; +} + +int +lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len, + const char *passphrase) +{ + BIO* bio = BIO_new(BIO_s_mem()); + BIGNUM *mpi, *dummy[6]; + EVP_PKEY *pkey = NULL; + EC_KEY *ecpriv = NULL; + RSA *rsapriv = NULL; + const BIGNUM *cmpi; + int n, m, ret = -1; + + BIO_write(bio, pem, (int)len); + PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb, + (void *)passphrase); + BIO_free(bio); + lws_explicit_bzero((void *)pem, len); + if (!pkey) { + lwsl_err("%s: unable to parse PEM privkey\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + /* confirm the key type matches the existing jwk situation */ + + switch (jwk->kty) { + case LWS_GENCRYPTO_KTY_EC: + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { + lwsl_err("%s: jwk is EC but privkey isn't\n", __func__); + + goto bail; + } + ecpriv = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecpriv) { + lwsl_notice("%s: missing EC key\n", __func__); + + goto bail; + } + + cmpi = EC_KEY_get0_private_key(ecpriv); + + /* quick size check first */ + + n = BN_num_bytes(cmpi); + if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) { + lwsl_err("%s: jwk key size doesn't match\n", __func__); + + goto bail1; + } + + /* TODO.. check public curve / group + point */ + + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = n; + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc(n, "ec"); + if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf) + goto bail1; + + m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len); + if ((unsigned int)m != (unsigned int)BN_num_bytes(cmpi)) + goto bail1; + + break; + + case LWS_GENCRYPTO_KTY_RSA: + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) { + lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__); + + goto bail; + } + rsapriv = EVP_PKEY_get1_RSA(pkey); + if (!rsapriv) { + lwsl_notice("%s: missing RSA key\n", __func__); + + goto bail; + } + +#if defined(LWS_HAVE_RSA_SET0_KEY) + RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */ + (const BIGNUM **)&dummy[1], /* e */ + (const BIGNUM **)&mpi); /* d */ + RSA_get0_factors(rsapriv, (const BIGNUM **)&dummy[4], /* p */ + (const BIGNUM **)&dummy[5]); /* q */ +#else + dummy[0] = rsapriv->n; + dummy[1] = rsapriv->e; + dummy[4] = rsapriv->p; + dummy[5] = rsapriv->q; + mpi = rsapriv->d; +#endif + + /* quick size check first */ + + n = BN_num_bytes(mpi); + if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) { + lwsl_err("%s: jwk key size doesn't match\n", __func__); + + goto bail1; + } + + /* then check that n & e match what we got from the cert */ + + dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf, + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len, + NULL); + dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf, + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len, + NULL); + + m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]); + BN_clear_free(dummy[2]); + BN_clear_free(dummy[3]); + if (m) { + lwsl_err("%s: privkey doesn't match jwk pubkey\n", + __func__); + + goto bail1; + } + + /* accept d from the PEM privkey into the JWK */ + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = n; + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) + goto bail1; + + BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + + /* accept p and q from the PEM privkey into the JWK */ + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].len = BN_num_bytes(dummy[4]); + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) { + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + goto bail1; + } + BN_bn2bin(dummy[4], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].len = BN_num_bytes(dummy[5]); + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf) { + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); + goto bail1; + } + BN_bn2bin(dummy[5], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf); + break; + default: + lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty); + return -1; + } + + ret = 0; + +bail1: + if (jwk->kty == LWS_GENCRYPTO_KTY_EC) + EC_KEY_free(ecpriv); + else + RSA_free(rsapriv); + +bail: + EVP_PKEY_free(pkey); + + return ret; +} +#endif + +void +lws_x509_destroy(struct lws_x509_cert **x509) +{ + if (!*x509) + return; + + if ((*x509)->cert) { + X509_free((*x509)->cert); + (*x509)->cert = NULL; + } + + lws_free_set_NULL(*x509); +} diff --git a/lib/tls/openssl/private-lib-tls-openssl.h b/lib/tls/openssl/private-lib-tls-openssl.h new file mode 100644 index 00000000..004d596a --- /dev/null +++ b/lib/tls/openssl/private-lib-tls-openssl.h @@ -0,0 +1,62 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * gencrypto openssl-specific helper declarations + */ + +#if !defined(__LWS_PRIVATE_LIB_TLS_OPENSSL_H__) +#define __LWS_PRIVATE_LIB_TLS_OPENSSL_H__ + +/* + * one of these per different client context + * cc_owner is in lws_context.lws_context_tls + */ + +struct lws_tls_client_reuse { + lws_tls_ctx *ssl_client_ctx; + uint8_t hash[32]; + struct lws_dll2 cc_list; + int refcount; + int index; +}; + +typedef int (*next_proto_cb)(SSL *, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg); + +struct lws_x509_cert { + X509 *cert; /* X509 is opaque, this has to be a pointer */ +}; + +int +lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type); + +const EVP_MD * +lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type); + +#if !defined(LWS_HAVE_BN_bn2binpad) +int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen); +#endif + +#endif + |