aboutsummaryrefslogtreecommitdiff
path: root/lib/tls/openssl
diff options
context:
space:
mode:
authorSteve Kim <kwstephenkim@google.com>2020-04-11 00:09:51 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-04-11 00:09:51 +0000
commita288eb3375c5d09138d916fe06404f891be4cf4c (patch)
tree9307b7d4ab253162aa5280e63d6ed64f30408ed0 /lib/tls/openssl
parentb9a9e26052c8692377c286c1a75330bd173d0128 (diff)
parent368c849f4cfad6689d476c756f6c0142d0ccddf4 (diff)
downloadlibwebsockets-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.c386
-rw-r--r--lib/tls/openssl/lws-gencrypto.c89
-rw-r--r--lib/tls/openssl/lws-genec.c664
-rw-r--r--lib/tls/openssl/lws-genhash.c174
-rw-r--r--lib/tls/openssl/lws-genrsa.c407
-rw-r--r--lib/tls/openssl/openssl-client.c836
-rw-r--r--lib/tls/openssl/openssl-server.c1036
-rw-r--r--lib/tls/openssl/openssl-ssl.c524
-rw-r--r--lib/tls/openssl/openssl-tls.c196
-rw-r--r--lib/tls/openssl/openssl-x509.c667
-rw-r--r--lib/tls/openssl/private-lib-tls-openssl.h62
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
+