lws_tls_server_certs_load: separate to allow cert attach later

This commit is contained in:
Andy Green 2017-11-02 09:20:17 +08:00
parent a5514d2b2b
commit 813b019bd1
6 changed files with 584 additions and 358 deletions

View file

@ -611,6 +611,30 @@ lws_create_vhost(struct lws_context *context,
else
vh->timeout_secs_ah_idle = 10;
#ifdef LWS_OPENSSL_SUPPORT
if (info->ecdh_curve)
strncpy(vh->ecdh_curve, info->ecdh_curve, sizeof(vh->ecdh_curve) - 1);
#endif
/* carefully allocate and take a copy of cert + key paths if present */
n = 0;
if (info->ssl_cert_filepath)
n += (int)strlen(info->ssl_cert_filepath) + 1;
if (info->ssl_private_key_filepath)
n += (int)strlen(info->ssl_private_key_filepath) + 1;
if (n) {
vh->key_path = vh->alloc_cert_path = lws_malloc(n, "vh paths");
if (info->ssl_cert_filepath) {
n = (int)strlen(info->ssl_cert_filepath) + 1;
memcpy(vh->alloc_cert_path, info->ssl_cert_filepath, n);
vh->key_path += n;
}
if (info->ssl_private_key_filepath)
memcpy(vh->key_path, info->ssl_private_key_filepath,
strlen(info->ssl_private_key_filepath) + 1);
}
/*
* give the vhost a unified list of protocols including the
* ones that came from plugins
@ -1527,6 +1551,8 @@ lws_vhost_destroy2(struct lws_vhost *vh)
close(vh->log_fd);
#endif
lws_free_set_NULL(vh->alloc_cert_path);
/*
* although async event callbacks may still come for wsi handles with
* pending close in the case of asycn event library like libuv,

View file

@ -5612,6 +5612,29 @@ LWS_VISIBLE LWS_EXTERN 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);
/**
* lws_tls_cert_updated() - update every vhost using the given cert path
*
* \param context: our lws_context
* \param certpath: the filepath to the certificate
* \param keypath: the filepath to the private key of the certificate
* \param mem_cert: copy of the cert in memory
* \param len_mem_cert: length of the copy of the cert in memory
* \param mem_privkey: copy of the private key in memory
* \param len_mem_privkey: length of the copy of the private key in memory
*
* Checks every vhost to see if it is the using certificate described by the
* the given filepaths. If so, it attempts to update the vhost ssl_ctx to use
* the new certificate.
*
* Returns 0 on success or nonzero for failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_tls_cert_updated(struct lws_context *context, const char *certpath,
const char *keypath,
const char *mem_cert, size_t len_mem_cert,
const char *mem_privkey, size_t len_mem_privkey);
///@}
/** \defgroup lws_ring LWS Ringbuffer APIs

View file

@ -996,6 +996,8 @@ struct lws_vhost {
struct lws *lserv_wsi;
const char *name;
const char *iface;
char *alloc_cert_path;
char *key_path;
#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
int bind_iface;
#endif
@ -1008,6 +1010,7 @@ struct lws_vhost {
lws_tls_ctx *ssl_ctx;
lws_tls_ctx *ssl_client_ctx;
struct lws_tls_ss_pieces *ss; /* for acme tls certs */
char ecdh_curve[16];
#endif
#if defined(LWS_WITH_MBEDTLS)
lws_tls_x509 *x509_client_CA;
@ -2426,6 +2429,18 @@ lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len);
LWS_EXTERN int
lws_tls_check_all_cert_lifetimes(struct lws_context *context);
LWS_EXTERN 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 len_mem_cert,
const char *mem_privkey, size_t mem_privkey_len);
LWS_EXTERN enum lws_tls_extant
lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
const char *private_key);
LWS_EXTERN int
lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
const char *inbuf, lws_filepos_t inlen,
uint8_t **buf, lws_filepos_t *amount);
#ifndef LWS_NO_SERVER
LWS_EXTERN int
lws_context_init_server_ssl(struct lws_context_creation_info *info,

View file

@ -42,146 +42,6 @@ lws_tls_server_client_cert_verify_config(struct lws_context_creation_info *info,
return 0;
}
#if defined(LWS_WITH_ESP32)
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
nvs_handle nvh;
size_t s;
int n = 0;
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
nvs_close(nvh);
return n;
}
#else
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
FILE *f;
size_t s;
int n = 0;
f = fopen(filename, "rb");
if (f == NULL) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_END) != 0) {
n = 1;
goto bail;
}
s = ftell(f);
if (s == (size_t)-1) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_SET) != 0) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (fread(*buf, s, 1, f) != 1) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
if (f)
fclose(f);
return n;
}
#endif
static int
alloc_pem_to_der_file(struct lws_context *context, const char *filename,
uint8_t **buf, lws_filepos_t *amount)
{
uint8_t *pem, *p, *q, *end;
lws_filepos_t len;
int n;
n = alloc_file(context, filename, &pem, &len);
if (n)
return n;
/* trim the first line */
p = pem;
end = p + len;
if (strncmp((char *)p, "-----", 5))
goto bail;
p += 5;
while (p < end && *p != '\n' && *p != '-')
p++;
if (*p != '-')
goto bail;
while (p < end && *p != '\n')
p++;
if (p >= end)
goto bail;
p++;
/* trim the last line */
q = end - 2;
while (q > pem && *q != '\n')
q--;
if (*q != '\n')
goto bail;
*q = '\0';
*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
*buf = pem;
return 0;
bail:
lws_free(pem);
return 4;
}
static int
lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx,
const unsigned char *servername, size_t len)
@ -226,14 +86,112 @@ lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx,
return 0;
}
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 len_mem_cert,
const char *mem_privkey, size_t mem_privkey_len)
{
int n = lws_tls_generic_cert_checks(vhost, cert, private_key), f = 0;
const char *filepath = private_key;
uint8_t *mem = NULL, *p = NULL;
size_t mem_len = 0;
lws_filepos_t flen;
long err;
if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey))
return 0;
/*
* we can't read the root-privs files. But if mem_cert is provided,
* we should use that.
*/
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) {
/*
* 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.
*/
/* mem cert is already DER */
p = (uint8_t *)mem_cert;
flen = len_mem_cert;
/* mem private key is PEM, so go through the motions */
mem = (uint8_t *)mem_privkey;
mem_len = mem_privkey_len;
filepath = NULL;
} else {
if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, NULL,
0, &p, &flen)) {
lwsl_err("couldn't find cert file %s\n", cert);
return 1;
}
f = 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
if (!err) {
free(p);
lwsl_err("Problem loading cert\n");
return 1;
}
if (f)
free(p);
p = NULL;
if (private_key || n == LWS_TLS_EXTANT_ALTERNATIVE) {
if (lws_tls_alloc_pem_to_der_file(vhost->context, filepath,
(char *)mem, mem_len, &p,
&flen)) {
lwsl_err("couldn't find private key file %s\n",
private_key);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
if (!err) {
free(p);
lwsl_err("Problem loading key\n");
return 1;
}
}
if (p && !mem_privkey) {
free(p);
p = NULL;
}
if (!private_key && !mem_privkey &&
vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
vhost->skipped_certs = 0;
return 0;
}
int
lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
struct lws_vhost *vhost, struct lws *wsi)
{
const SSL_METHOD *method = TLS_server_method();
uint8_t *p;
lws_filepos_t flen;
int n, m, err;
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
@ -244,79 +202,10 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
if (!vhost->use_ssl || !info->ssl_cert_filepath)
return 0;
/*
* The user code can choose to either pass the cert and
* key filepaths using the info members like this, or it can
* leave them NULL; force the vhost SSL_CTX init using the info
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
* set up the cert himself using the user callback
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* happened just above and has the vhost SSL_CTX * in the user
* parameter.
*/
n = lws_tls_use_any_upgrade_check_extant(info->ssl_cert_filepath);
if (n == LWS_TLS_EXTANT_ALTERNATIVE)
return 1;
m = lws_tls_use_any_upgrade_check_extant(info->ssl_private_key_filepath);
if (m == LWS_TLS_EXTANT_ALTERNATIVE)
return 1;
if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
(info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
lwsl_notice("Ignoring missing %s or %s\n",
info->ssl_cert_filepath,
info->ssl_private_key_filepath);
vhost->skipped_certs = 1;
return 0;
}
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
&flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
if (!err) {
lwsl_err("Problem loading cert\n");
return 1;
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
if (info->ssl_private_key_filepath) {
if (alloc_pem_to_der_file(vhost->context,
info->ssl_private_key_filepath,
&p, &flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
if (!err) {
lwsl_err("Problem loading key\n");
return 1;
}
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
if (!info->ssl_private_key_filepath && vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
return 0;
return lws_tls_server_certs_load(vhost, wsi,
info->ssl_cert_filepath,
info->ssl_private_key_filepath,
NULL, 0, NULL, 0);
}
int

View file

@ -126,7 +126,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
return SSL_TLSEXT_ERR_OK;
}
lwsl_notice("SNI: Found: %s:%d\n", servername, vh->listen_port);
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->ssl_ctx);
@ -135,10 +135,15 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
}
#endif
/*
* this may now get called after the vhost creation, when certs become
* available.
*/
int
lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
struct lws_vhost *vhost,
struct lws *wsi)
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 len_mem_cert,
const char *mem_privkey, size_t mem_privkey_len)
{
#if defined(LWS_HAVE_OPENSSL_ECDH_H)
const char *ecdh_curve = "prime256v1";
@ -151,9 +156,177 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
STACK_OF(X509) *extra_certs = NULL;
#endif
#endif
SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method();
unsigned long error;
int n, m;
uint8_t *p;
lws_filepos_t flen;
int n = lws_tls_generic_cert_checks(vhost, cert, private_key), m;
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) {
/*
* 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.
*/
if (SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx,
(int)len_mem_cert,
(uint8_t *)mem_cert) != 1) {
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;
}
if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->ssl_ctx,
p, (long)(long long)flen) != 1) {
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->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->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->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
}
check_key:
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
#if defined(LWS_HAVE_OPENSSL_ECDH_H)
if (vhost->ecdh_curve[0])
ecdh_curve = vhost->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->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
SSL_CTX_set_options(vhost->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_HAVE_SSL_EXTRA_CHAIN_CERTS)
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
#else
SSL_CTX_get_extra_chain_certs_only(vhost->ssl_ctx, &extra_certs);
if (extra_certs)
x = sk_X509_value(extra_certs, 0);
else
lwsl_err("%s: no extra certs\n", __func__);
#endif
if (!x) {
lwsl_err("%s: x is NULL\n", __func__);
goto post_ecdh;
}
/* 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->ssl_ctx, EC_key);
EC_KEY_free(EC_key);
#else
lwsl_notice(" OpenSSL doesn't support ECDH\n");
#endif
post_ecdh:
vhost->skipped_certs = 0;
return 0;
}
int
lws_tls_server_vhost_backend_init(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();
@ -212,140 +385,9 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
/*
* The user code can choose to either pass the cert and
* key filepaths using the info members like this, or it can
* leave them NULL; force the vhost SSL_CTX init using the info
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
* set up the cert himself using the user callback
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* happened just above and has the vhost SSL_CTX * in the user
* parameter.
*/
n = lws_tls_use_any_upgrade_check_extant(info->ssl_cert_filepath);
if (n == LWS_TLS_EXTANT_ALTERNATIVE)
return 1;
m = lws_tls_use_any_upgrade_check_extant(info->ssl_private_key_filepath);
if (m == LWS_TLS_EXTANT_ALTERNATIVE)
return 1;
if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
(info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
lwsl_notice("Ignoring missing %s or %s\n",
info->ssl_cert_filepath,
info->ssl_private_key_filepath);
vhost->skipped_certs = 1;
return 0;
}
/* set the local certificate from CertFile */
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
info->ssl_cert_filepath);
if (n != 1) {
error = ERR_get_error();
lwsl_err("problem getting cert '%s' %lu: %s\n",
info->ssl_cert_filepath, error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
if (info->ssl_private_key_filepath != NULL) {
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
info->ssl_private_key_filepath,
SSL_FILETYPE_PEM) != 1) {
error = ERR_get_error();
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
info->ssl_private_key_filepath, 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->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
#if defined(LWS_HAVE_OPENSSL_ECDH_H)
if (info->ecdh_curve)
ecdh_curve = info->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->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
SSL_CTX_set_options(vhost->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_HAVE_SSL_EXTRA_CHAIN_CERTS)
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
#else
SSL_CTX_get_extra_chain_certs_only(vhost->ssl_ctx, &extra_certs);
if (extra_certs)
x = sk_X509_value(extra_certs, 0);
else
lwsl_err("%s: no extra certs\n", __func__);
#endif
if (!x) {
lwsl_err("%s: x is NULL\n", __func__);
return 0; // !!!
}
/* 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->ssl_ctx, EC_key);
EC_KEY_free(EC_key);
#else
lwsl_notice(" OpenSSL doesn't support ECDH\n");
#endif
return 0;
return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath,
info->ssl_private_key_filepath,
NULL, 0, NULL, 0);
}
int
@ -761,7 +803,7 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
BIO_free(bio);
goto bail3;
}
memcpy(*privkey_pem, p, bio_len);
memcpy(*privkey_pem, p, (int)(long long)bio_len);
BIO_free(bio);
ret = lws_ptr_diff(csr, csr_in);

View file

@ -98,6 +98,154 @@ lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
wsi->pending_read_list_next = NULL;
}
#if defined(LWS_WITH_ESP32)
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
nvs_handle nvh;
size_t s;
int n = 0;
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
nvs_close(nvh);
return n;
}
#else
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
FILE *f;
size_t s;
int n = 0;
f = fopen(filename, "rb");
if (f == NULL) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_END) != 0) {
n = 1;
goto bail;
}
s = ftell(f);
if (s == (size_t)-1) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_SET) != 0) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (fread(*buf, s, 1, f) != 1) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
if (f)
fclose(f);
return n;
}
#endif
int
lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
const char *inbuf, lws_filepos_t inlen,
uint8_t **buf, lws_filepos_t *amount)
{
const uint8_t *pem, *p, *end;
uint8_t *q;
lws_filepos_t len;
int n;
if (filename) {
n = alloc_file(context, filename, (uint8_t **)&pem, &len);
if (n)
return n;
} else {
pem = (const uint8_t *)inbuf;
len = inlen;
}
/* trim the first line */
p = pem;
end = p + len;
if (strncmp((char *)p, "-----", 5))
goto bail;
p += 5;
while (p < end && *p != '\n' && *p != '-')
p++;
if (*p != '-')
goto bail;
while (p < end && *p != '\n')
p++;
if (p >= end)
goto bail;
p++;
/* trim the last line */
q = (uint8_t *)end - 2;
while (q > pem && *q != '\n')
q--;
if (*q != '\n')
goto bail;
*q = '\0';
*amount = lws_b64_decode_string((char *)p, (char *)pem,
(int)(long long)len);
*buf = (uint8_t *)pem;
return 0;
bail:
lws_free((uint8_t *)pem);
return 4;
}
int
lws_tls_check_cert_lifetime(struct lws_vhost *v)
{
@ -165,7 +313,8 @@ lws_tls_extant(const char *name)
* There are four situations and three results possible:
*
* 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
* be provisioned)
* be provisioned). We also feel like this if we need privs we don't have
* any more to look in the directory.
*
* 2) There are provisioned certs written (xxx.upd) and we still have root
* privs... in this case we rename any existing cert to have a backup name
@ -177,6 +326,10 @@ lws_tls_extant(const char *name)
* case, indicate that the caller should use temp copies if any we do have
* rights to access. This is normal after we have updated the cert.
*
* But if we dropped privs, we can't detect the provisioned xxx.upd cert +
* key, because we can't see in the dir. So we have to upgrade NO to
* ALTERNATIVE when we actually have the in-memory alternative.
*
* 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
* have the rights to read them.
*/
@ -219,6 +372,84 @@ lws_tls_use_any_upgrade_check_extant(const char *name)
return LWS_TLS_EXTANT_YES;
}
/*
* LWS_TLS_EXTANT_NO : skip adding the cert
* LWS_TLS_EXTANT_YES : use the cert and private key paths normally
* LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss
*/
enum lws_tls_extant
lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
const char *private_key)
{
int n, m;
/*
* The user code can choose to either pass the cert and
* key filepaths using the info members like this, or it can
* leave them NULL; force the vhost SSL_CTX init using the info
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
* set up the cert himself using the user callback
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* happened just above and has the vhost SSL_CTX * in the user
* parameter.
*/
n = lws_tls_use_any_upgrade_check_extant(cert);
if (n == LWS_TLS_EXTANT_ALTERNATIVE)
return LWS_TLS_EXTANT_ALTERNATIVE;
m = lws_tls_use_any_upgrade_check_extant(private_key);
if (m == LWS_TLS_EXTANT_ALTERNATIVE)
return LWS_TLS_EXTANT_ALTERNATIVE;
if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
(vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
lwsl_notice("Ignoring missing %s or %s\n", cert, private_key);
vhost->skipped_certs = 1;
return LWS_TLS_EXTANT_NO;
}
/*
* the cert + key exist
*/
return LWS_TLS_EXTANT_YES;
}
#if !defined(LWS_NO_SERVER)
/*
* update the cert for every vhost using the given path
*/
LWS_VISIBLE int
lws_tls_cert_updated(struct lws_context *context, const char *certpath,
const char *keypath,
const char *mem_cert, size_t len_mem_cert,
const char *mem_privkey, size_t len_mem_privkey)
{
struct lws wsi;
wsi.context = context;
lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
wsi.vhost = v;
if (v->alloc_cert_path && v->key_path &&
!strcmp(v->alloc_cert_path, certpath) &&
!strcmp(v->key_path, keypath)) {
lws_tls_server_certs_load(v, &wsi, certpath, keypath,
mem_cert, len_mem_cert,
mem_privkey, len_mem_privkey);
if (v->skipped_certs)
lwsl_notice("%s: vhost %s: cert unset\n",
__func__, v->name);
}
} lws_end_foreach_ll(v, vhost_next);
return 0;
}
#endif
int
lws_gate_accepts(struct lws_context *context, int on)
{