mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-30 00:00:16 +01:00

This converts several of the selftests to return a status in their exit code about whether they 'worked'. A small bash script framework is added, with a selftest.sh in the mininmal example dirs that support it, and a ./minimal-examples/selftests.sh script that can be run from the build dir with no args that discovers and runs all the selftest.sh scripts underneath. That is also integrated into travis and the enabled tests must pass now for travis to pass. Travis does not have a modern libuv so it can't run a couple of tests which are nulled out if it sees it's running in travis env.
678 lines
16 KiB
C
678 lines
16 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010-2018 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"
|
|
#include <errno.h>
|
|
|
|
int openssl_websocket_private_data_index,
|
|
openssl_SSL_CTX_private_data_index;
|
|
|
|
int lws_ssl_get_error(struct lws *wsi, int n)
|
|
{
|
|
int m;
|
|
|
|
if (!wsi->ssl)
|
|
return 99;
|
|
|
|
m = SSL_get_error(wsi->ssl, n);
|
|
lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->ssl, n, m, errno);
|
|
|
|
return m;
|
|
}
|
|
|
|
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_ssl_elaborate_error(void)
|
|
{
|
|
char buf[256];
|
|
u_long err;
|
|
|
|
while ((err = ERR_get_error()) != 0) {
|
|
ERR_error_string_n(err, buf, sizeof(buf));
|
|
lwsl_info("*** %s\n", buf);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
|
|
{
|
|
if (!info->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, lws_context_init_ssl_pem_passwd_cb);
|
|
}
|
|
|
|
int
|
|
lws_context_init_ssl_library(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");
|
|
|
|
SSL_library_init();
|
|
OpenSSL_add_all_algorithms();
|
|
SSL_load_error_strings();
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_VISIBLE 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->ssl_ctx)
|
|
SSL_CTX_free(vhost->ssl_ctx);
|
|
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
|
SSL_CTX_free(vhost->ssl_client_ctx);
|
|
|
|
// 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_VISIBLE 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->ssl)
|
|
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
|
|
|
|
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
|
|
|
errno = 0;
|
|
n = SSL_read(wsi->ssl, buf, len);
|
|
#if defined(LWS_WITH_ESP32)
|
|
if (!n && errno == ENOTCONN) {
|
|
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
}
|
|
#endif
|
|
#if defined(LWS_WITH_STATS)
|
|
if (!wsi->seen_rx) {
|
|
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
|
|
time_in_microseconds() - wsi->accept_start_us);
|
|
lws_stats_atomic_bump(wsi->context, 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 */
|
|
if (!n || (n == -1 && errno == ENOTCONN)) {
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
}
|
|
|
|
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 ||
|
|
m == SSL_ERROR_SYSCALL)
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
|
|
if (SSL_want_read(wsi->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->ssl)) {
|
|
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
|
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
|
}
|
|
wsi->socket_is_permanently_unusable = 1;
|
|
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
}
|
|
|
|
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
|
|
|
if (wsi->vhost)
|
|
wsi->vhost->conn_stats.rx += n;
|
|
|
|
lws_restart_ws_ping_pong_timer(wsi);
|
|
|
|
// lwsl_hexdump_err(buf, n);
|
|
|
|
/*
|
|
* 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->ssl)
|
|
goto bail;
|
|
|
|
if (!SSL_pending(wsi->ssl))
|
|
goto bail;
|
|
|
|
if (wsi->pending_read_list_next)
|
|
return n;
|
|
if (wsi->pending_read_list_prev)
|
|
return n;
|
|
if (pt->pending_read_list == wsi)
|
|
return n;
|
|
|
|
/* add us to the linked list of guys with pending ssl */
|
|
if (pt->pending_read_list)
|
|
pt->pending_read_list->pending_read_list_prev = wsi;
|
|
|
|
wsi->pending_read_list_next = pt->pending_read_list;
|
|
wsi->pending_read_list_prev = NULL;
|
|
pt->pending_read_list = wsi;
|
|
|
|
return n;
|
|
bail:
|
|
lws_ssl_remove_wsi_from_buffered_list(wsi);
|
|
|
|
return n;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_ssl_pending(struct lws *wsi)
|
|
{
|
|
if (!wsi->ssl)
|
|
return 0;
|
|
|
|
return SSL_pending(wsi->ssl);
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
|
{
|
|
int n, m;
|
|
|
|
if (!wsi->ssl)
|
|
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
|
|
|
|
n = SSL_write(wsi->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->ssl)) {
|
|
lwsl_notice("%s: want read\n", __func__);
|
|
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
|
}
|
|
|
|
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
|
lws_set_blocking_send(wsi);
|
|
|
|
lwsl_notice("%s: want write\n", __func__);
|
|
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
|
}
|
|
}
|
|
|
|
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
|
|
lws_ssl_elaborate_error();
|
|
|
|
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;
|
|
|
|
context = (struct lws_context *)SSL_CTX_get_ex_data(
|
|
SSL_get_SSL_CTX(ssl),
|
|
openssl_SSL_CTX_private_data_index);
|
|
if (!context)
|
|
return;
|
|
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
|
|
if (!wsi)
|
|
return;
|
|
|
|
if (!(where & wsi->vhost->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);
|
|
}
|
|
|
|
|
|
LWS_VISIBLE int
|
|
lws_ssl_close(struct lws *wsi)
|
|
{
|
|
lws_sockfd_type n;
|
|
|
|
if (!wsi->ssl)
|
|
return 0; /* not handled */
|
|
|
|
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
|
/* kill ssl callbacks, becausse we will remove the fd from the
|
|
* table linking it to the wsi
|
|
*/
|
|
if (wsi->vhost->ssl_info_event_mask)
|
|
SSL_set_info_callback(wsi->ssl, NULL);
|
|
#endif
|
|
|
|
n = SSL_get_fd(wsi->ssl);
|
|
if (!wsi->socket_is_permanently_unusable)
|
|
SSL_shutdown(wsi->ssl);
|
|
compatible_close(n);
|
|
SSL_free(wsi->ssl);
|
|
wsi->ssl = NULL;
|
|
|
|
if (wsi->context->simultaneous_ssl_restriction &&
|
|
wsi->context->simultaneous_ssl-- ==
|
|
wsi->context->simultaneous_ssl_restriction)
|
|
/* we made space and can do an accept */
|
|
lws_gate_accepts(wsi->context, 1);
|
|
|
|
// lwsl_notice("%s: ssl restr %d, simul %d\n", __func__,
|
|
// wsi->context->simultaneous_ssl_restriction,
|
|
// wsi->context->simultaneous_ssl);
|
|
|
|
#if defined(LWS_WITH_STATS)
|
|
wsi->context->updated = 1;
|
|
#endif
|
|
|
|
return 1; /* handled */
|
|
}
|
|
|
|
void
|
|
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
|
|
{
|
|
if (vhost->ssl_ctx)
|
|
SSL_CTX_free(vhost->ssl_ctx);
|
|
|
|
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
|
SSL_CTX_free(vhost->ssl_client_ctx);
|
|
#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->ssl)
|
|
return NULL;
|
|
|
|
return SSL_get_SSL_CTX(wsi->ssl);
|
|
}
|
|
|
|
enum lws_ssl_capable_status
|
|
__lws_tls_shutdown(struct lws *wsi)
|
|
{
|
|
int n;
|
|
|
|
n = SSL_shutdown(wsi->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->ssl, n);
|
|
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
|
|
if (SSL_want_read(wsi->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->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;
|
|
}
|
|
}
|
|
#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;
|
|
|
|
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:
|
|
{
|
|
size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL);
|
|
uint8_t *tmp, *ptmp;
|
|
|
|
if (klen <= 0 || 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);
|
|
|
|
return 0;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_VISIBLE LWS_EXTERN 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->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
|
|
}
|
|
|
|
LWS_VISIBLE 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->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->ssl) == X509_V_OK;
|
|
break;
|
|
default:
|
|
rc = lws_tls_openssl_cert_info(x509, type, buf, len);
|
|
}
|
|
|
|
X509_free(x509);
|
|
|
|
return rc;
|
|
}
|