1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-23 00:00:06 +01:00
libwebsockets/lib/tls/openssl/openssl-x509.c
Andy Green c9731c5f17 type comparisons: fixes
This is a huge patch that should be a global NOP.

For unix type platforms it enables -Wconversion to issue warnings (-> error)
for all automatic casts that seem less than ideal but are normally concealed
by the toolchain.

This is things like passing an int to a size_t argument.  Once enabled, I
went through all args on my default build (which build most things) and
tried to make the removed default cast explicit.

With that approach it neither change nor bloat the code, since it compiles
to whatever it was doing before, just with the casts made explicit... in a
few cases I changed some length args from int to size_t but largely left
the causes alone.

From now on, new code that is relying on less than ideal casting
will complain and nudge me to improve it by warnings.
2021-01-05 10:56:38 +00:00

668 lines
16 KiB
C

/*
* 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.
*/
#define WIN32_LEAN_AND_MEAN
#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 = (unsigned int)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 = (unsigned int)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;
size_t n = strlen(pp);
if ((int)n > size - 1)
return -1;
memcpy(buf, pp, n + 1);
return (int)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 = (unsigned int)n;
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc((unsigned int)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,
(int32_t)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,
(int32_t)jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len,
NULL);
dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
(int32_t)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 = (unsigned int)n;
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc((unsigned int)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 = (unsigned int)BN_num_bytes(dummy[4]);
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc((unsigned int)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 = (unsigned int)BN_num_bytes(dummy[5]);
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc((unsigned int)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);
}