diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 0ec908963..2539d7826 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -920,6 +920,9 @@ lws_plat_ntpclient_config(struct lws_context *context); int lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len); +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost); + int lws_check_byte_utf8(unsigned char state, unsigned char c); int LWS_WARN_UNUSED_RESULT diff --git a/lib/plat/freertos/freertos-sockets.c b/lib/plat/freertos/freertos-sockets.c index 33e731989..c6bfec35d 100644 --- a/lib/plat/freertos/freertos-sockets.c +++ b/lib/plat/freertos/freertos-sockets.c @@ -324,6 +324,12 @@ lws_plat_ifconfig(int fd, lws_dhcpc_ifstate_t *is) return -1; } +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost) +{ + return 0; +} + #if defined(LWS_WITH_MBEDTLS) int lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len) diff --git a/lib/plat/optee/network.c b/lib/plat/optee/network.c index 918b8827b..34f152f02 100644 --- a/lib/plat/optee/network.c +++ b/lib/plat/optee/network.c @@ -262,6 +262,12 @@ lws_plat_set_socket_options_ip(int fd, uint8_t pri, unsigned int lws_flags) return 0; } +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost) +{ + return 0; +} + #if defined(LWS_WITH_MBEDTLS) int lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len) diff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c index d2fb118b8..f8b5db922 100644 --- a/lib/plat/unix/unix-sockets.c +++ b/lib/plat/unix/unix-sockets.c @@ -543,6 +543,12 @@ lws_plat_ifconfig(int fd, lws_dhcpc_ifstate_t *is) #endif } +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost) +{ + return 0; +} + #if defined(LWS_WITH_MBEDTLS) int lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len) diff --git a/lib/plat/windows/private-lib-plat-windows.h b/lib/plat/windows/private-lib-plat-windows.h index 8db7ec3f3..beb5f544d 100644 --- a/lib/plat/windows/private-lib-plat-windows.h +++ b/lib/plat/windows/private-lib-plat-windows.h @@ -68,6 +68,10 @@ #include #endif +#if defined(LWS_WITH_TLS) +#include +#endif + #if defined(LWS_HAVE_PTHREAD_H) #define lws_mutex_t pthread_mutex_t #define lws_mutex_init(x) pthread_mutex_init(&(x), NULL) diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index 2db5441c7..8fa4b4580 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2019 Andy Green + * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -140,7 +140,6 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, lwsl_warn("setsockopt TCP_NODELAY 1 failed with error %d\n", error); } - return lws_plat_set_nonblocking(fd); } @@ -242,6 +241,163 @@ lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, return 0; } +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost) +{ +#if !defined(LWS_WITH_MBEDTLS) && defined(LWS_SSL_CLIENT_USE_OS_CA_CERTS) + PCCERT_CONTEXT pcc = NULL; + CERT_ENHKEY_USAGE* ceu = NULL; + DWORD ceu_alloc = 0; + X509_STORE* store; + HCERTSTORE hStore; + int imps = 0; + + if (lws_check_opt(vhost->options, + LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + return 0; + + /* + * Windows Trust Store code adapted from curl (MIT) openssl.c + * https://github.com/warmcat/libwebsockets/pull/2233 + */ + + store = SSL_CTX_get_cert_store(vhost->tls.ssl_client_ctx); + hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL, TEXT("ROOT")); + + if (!hStore) { + lwsl_notice("%s: no store\n", __func__); + return 1; + } + + do { + const unsigned char* ecert; + char cert_name[256]; + DWORD req_size = 0; + BYTE key_usage[2]; + FILETIME ft; + X509* x509; + + pcc = CertEnumCertificatesInStore(hStore, pcc); + if (!pcc) + break; + + if (!CertGetNameStringA(pcc, CERT_NAME_SIMPLE_DISPLAY_TYPE, + 0, NULL, cert_name, sizeof(cert_name))) + strcpy(cert_name, "Unknown"); + + lwsl_debug("%s: Checking cert \"%s\"\n", __func__, cert_name); + + ecert = (const unsigned char*)pcc->pbCertEncoded; + if (!ecert) + continue; + + GetSystemTimeAsFileTime(&ft); + if (CompareFileTime(&pcc->pCertInfo->NotBefore, &ft) > 0 || + CompareFileTime(&ft, &pcc->pCertInfo->NotAfter) > 0) + continue; + + /* If key usage exists check for signing attribute */ + if (CertGetIntendedKeyUsage(pcc->dwCertEncodingType, + pcc->pCertInfo, + key_usage, sizeof(key_usage))) { + if (!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; + } else + if (GetLastError()) + continue; + + /* + * If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also + * have EKU extended properties that specify valid uses for the + * certificate." + * The call below checks both, and behavior varies depending on + * what is found. For more details see CertGetEnhancedKeyUsage + * doc. + */ + if (!CertGetEnhancedKeyUsage(pcc, 0, NULL, &req_size)) + continue; + + if (req_size && req_size > ceu_alloc) { + void* tmp = lws_realloc(ceu, req_size, __func__); + + if (!tmp) { + lwsl_err("%s: OOM", __func__); + break; + } + + ceu = (CERT_ENHKEY_USAGE*)tmp; + ceu_alloc = req_size; + } + + if (!CertGetEnhancedKeyUsage(pcc, 0, ceu, &req_size)) + continue; + + if (!ceu || (ceu && !ceu->cUsageIdentifier)) { + /* + * "If GetLastError returns CRYPT_E_NOT_FOUND, the + * certificate is good for all uses. If it returns + * zero, the certificate has no valid uses." + */ + if ((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; + + /* ... allow it... */ + + } else + if (ceu) { + BOOL found = FALSE; + DWORD i; + + /* + * If there is a CEU, check that it specifies + * we can use the cert for server validation + */ + + for (i = 0; i < ceu->cUsageIdentifier; i++) { + if (strcmp("1.3.6.1.5.5.7.3.1" + /* OID server auth */, + ceu->rgpszUsageIdentifier[i])) + continue; + + found = TRUE; + break; + } + + if (!found) + /* Don't use cert if no usage match */ + continue; + } + + x509 = d2i_X509(NULL, &ecert, pcc->cbCertEncoded); + if (!x509) + /* We can't parse it as am X.509, skip it */ + continue; + + if (X509_STORE_add_cert(store, x509) == 1) { + lwsl_debug("%s: Imported cert \"%s\"\n", __func__, + cert_name); + imps++; + } + + /* + * Treat failure as nonfatal, eg, may be dupe + */ + + X509_free(x509); + } while (1); + + lws_free(ceu); + CertFreeCertificateContext(pcc); + CertCloseStore(hStore, 0); + + lwsl_notice("%s: Imported %d certs from plat store\n", __func__, imps); +#endif + + return 0; +} + const char * lws_plat_inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c index 590ed86b9..10280dbcb 100644 --- a/lib/tls/openssl/openssl-client.c +++ b/lib/tls/openssl/openssl-client.c @@ -756,6 +756,8 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh, return 1; } + lws_plat_vhost_tls_client_ctx_init(vh); + tcr = lws_zalloc(sizeof(*tcr), "client ctx tcr"); if (!tcr) { SSL_CTX_free(vh->tls.ssl_client_ctx);