lws_tls_server_certs_load: separate to allow cert attach later
This commit is contained in:
parent
a5514d2b2b
commit
813b019bd1
6 changed files with 584 additions and 358 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
233
lib/tls/tls.c
233
lib/tls/tls.c
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue