1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-23 00:00:06 +01:00
libwebsockets/lib/tls/mbedtls/server.c
Andy Green 00ffebfd24 tls: add x509 query api
This adds a single api on lws that allows querying elements from the
peer certificate on a connection.

The api works the same regardless of the TLS backend.
2017-11-26 19:16:17 +08:00

326 lines
6.5 KiB
C

/*
* libwebsockets - mbedTLS-specific server functions
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int
lws_tls_server_client_cert_verify_config(struct lws_context_creation_info *info,
struct lws_vhost *vh)
{
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;
}
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 err;
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
lwsl_err("problem creating ssl context\n");
return 1;
}
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.
*/
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;
}
int
lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
{
errno = 0;
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
if (wsi->ssl == NULL) {
lwsl_err("SSL_new failed: errno %d\n", errno);
lws_ssl_elaborate_error();
return 1;
}
SSL_set_fd(wsi->ssl, accept_fd);
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
return 0;
}
int
lws_tls_server_abort_connection(struct lws *wsi)
{
lws_tls_shutdown(wsi);
SSL_free(wsi->ssl);
return 0;
}
enum lws_ssl_capable_status
lws_tls_server_accept(struct lws *wsi)
{
union lws_tls_cert_info_results ir;
int m, n = SSL_accept(wsi->ssl);
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: couldn't get client cert CN\n", __func__);
return LWS_SSL_CAPABLE_DONE;
}
m = SSL_get_error(wsi->ssl, n);
// mbedtls wrapper only
if (m == SSL_ERROR_SYSCALL && errno == 11)
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
return LWS_SSL_CAPABLE_ERROR;
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->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\n");
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->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;
}