| /* |
| * 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 "core/private.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->tls.ssl) |
| return 99; |
| |
| m = SSL_get_error(wsi->tls.ssl, n); |
| lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->tls.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, |
| const 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(const 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"); |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L |
| SSL_library_init(); |
| OpenSSL_add_all_algorithms(); |
| SSL_load_error_strings(); |
| #else |
| OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); |
| #endif |
| |
| 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->tls.ssl_ctx) |
| SSL_CTX_free(vhost->tls.ssl_ctx); |
| if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx) |
| SSL_CTX_free(vhost->tls.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->tls.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->tls.ssl, buf, len); |
| #if defined(LWS_WITH_ESP32) |
| if (!n && errno == LWS_ENOTCONN) { |
| lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); |
| return LWS_SSL_CAPABLE_ERROR; |
| } |
| #endif |
| #if defined(LWS_WITH_STATS) |
| if (!wsi->seen_rx && wsi->accept_start_us) { |
| 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 |
| * |
| * 2018-09-10: https://github.com/openssl/openssl/issues/1903 |
| * |
| * So, in summary, if you get a 0 or -1 return from SSL_read() / |
| * SSL_write(), you should call SSL_get_error(): |
| * |
| * - If you get back SSL_ERROR_RETURN_ZERO then you know the connection |
| * has been cleanly shutdown by the peer. To fully close the |
| * connection you may choose to call SSL_shutdown() to send a |
| * close_notify back. |
| * |
| * - If you get back SSL_ERROR_SSL then some kind of internal or |
| * protocol error has occurred. More details will be on the SSL error |
| * queue. You can also call SSL_get_shutdown(). If this indicates a |
| * state of SSL_RECEIVED_SHUTDOWN then you know a fatal alert has |
| * been received from the peer (if it had been a close_notify then |
| * SSL_get_error() would have returned SSL_ERROR_RETURN_ZERO). |
| * SSL_ERROR_SSL is considered fatal - you should not call |
| * SSL_shutdown() in this case. |
| * |
| * - If you get back SSL_ERROR_SYSCALL then some kind of fatal (i.e. |
| * non-retryable) error has occurred in a system call. |
| */ |
| if (n <= 0) { |
| m = lws_ssl_get_error(wsi, n); |
| lwsl_notice("%p: ssl err %d errno %d\n", wsi, m, errno); |
| if (m == SSL_ERROR_ZERO_RETURN) /* cleanly shut down */ |
| return LWS_SSL_CAPABLE_ERROR; |
| |
| /* hm not retryable.. could be 0 size pkt or error */ |
| |
| if (m == SSL_ERROR_SSL || m == SSL_ERROR_SYSCALL || |
| errno == LWS_ENOTCONN) { |
| |
| /* unclean, eg closed conn */ |
| |
| wsi->socket_is_permanently_unusable = 1; |
| |
| return LWS_SSL_CAPABLE_ERROR; |
| } |
| |
| /* retryable? */ |
| |
| if (SSL_want_read(wsi->tls.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->tls.ssl)) { |
| lwsl_debug("%s: WANT_WRITE\n", __func__); |
| lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); |
| return LWS_SSL_CAPABLE_MORE_SERVICE; |
| } |
| |
| /* keep on trucking it seems */ |
| } |
| |
| lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); |
| |
| if (wsi->vhost) |
| wsi->vhost->conn_stats.rx += n; |
| |
| // 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->tls.ssl) |
| goto bail; |
| |
| if (!SSL_pending(wsi->tls.ssl)) |
| goto bail; |
| |
| if (wsi->tls.pending_read_list_next) |
| return n; |
| if (wsi->tls.pending_read_list_prev) |
| return n; |
| if (pt->tls.pending_read_list == wsi) |
| return n; |
| |
| /* add us to the linked list of guys with pending ssl */ |
| if (pt->tls.pending_read_list) |
| pt->tls.pending_read_list->tls.pending_read_list_prev = wsi; |
| |
| wsi->tls.pending_read_list_next = pt->tls.pending_read_list; |
| wsi->tls.pending_read_list_prev = NULL; |
| pt->tls.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->tls.ssl) |
| return 0; |
| |
| return SSL_pending(wsi->tls.ssl); |
| } |
| |
| LWS_VISIBLE int |
| lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) |
| { |
| int n, m; |
| |
| if (!wsi->tls.ssl) |
| return lws_ssl_capable_write_no_ssl(wsi, buf, len); |
| |
| n = SSL_write(wsi->tls.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->tls.ssl)) { |
| lwsl_notice("%s: want read\n", __func__); |
| |
| return LWS_SSL_CAPABLE_MORE_SERVICE; |
| } |
| |
| if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { |
| lws_set_blocking_send(wsi); |
| |
| lwsl_debug("%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; |
| |
| #ifndef USE_WOLFSSL |
| context = (struct lws_context *)SSL_CTX_get_ex_data( |
| SSL_get_SSL_CTX(ssl), |
| openssl_SSL_CTX_private_data_index); |
| #else |
| context = (struct lws_context *)SSL_CTX_get_ex_data( |
| SSL_get_SSL_CTX((SSL*) ssl), |
| openssl_SSL_CTX_private_data_index); |
| #endif |
| if (!context) |
| return; |
| wsi = wsi_from_fd(context, SSL_get_fd(ssl)); |
| if (!wsi) |
| return; |
| |
| if (!(where & wsi->vhost->tls.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->tls.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->tls.ssl_info_event_mask) |
| SSL_set_info_callback(wsi->tls.ssl, NULL); |
| #endif |
| |
| n = SSL_get_fd(wsi->tls.ssl); |
| if (!wsi->socket_is_permanently_unusable) |
| SSL_shutdown(wsi->tls.ssl); |
| compatible_close(n); |
| SSL_free(wsi->tls.ssl); |
| wsi->tls.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->tls.ssl_ctx) |
| SSL_CTX_free(vhost->tls.ssl_ctx); |
| |
| if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx) |
| SSL_CTX_free(vhost->tls.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->tls.ssl) |
| return NULL; |
| |
| return SSL_get_SSL_CTX(wsi->tls.ssl); |
| } |
| |
| enum lws_ssl_capable_status |
| __lws_tls_shutdown(struct lws *wsi) |
| { |
| int n; |
| |
| n = SSL_shutdown(wsi->tls.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->tls.ssl, n); |
| if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { |
| if (SSL_want_read(wsi->tls.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->tls.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; |
| |
| #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 = 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; |
| } |
| |
| 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->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 |
| } |
| |
| 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->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; |
| } |
| |
| static int |
| tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt) |
| { |
| return lws_tls_fake_POLLIN_for_buffered(pt); |
| } |
| |
| static int |
| tops_periodic_housekeeping_openssl(struct lws_context *context, time_t now) |
| { |
| int n; |
| |
| n = lws_compare_time_t(context, now, context->tls.last_cert_check_s); |
| if ((!context->tls.last_cert_check_s || n > (24 * 60 * 60)) && |
| !lws_tls_check_all_cert_lifetimes(context)) |
| context->tls.last_cert_check_s = now; |
| |
| return 0; |
| } |
| |
| const struct lws_tls_ops tls_ops_openssl = { |
| /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_openssl, |
| /* periodic_housekeeping */ tops_periodic_housekeeping_openssl, |
| |
| }; |