diff --git a/READMEs/README.coding.md b/READMEs/README.coding.md index 08128056b0..ccd7e52826 100644 --- a/READMEs/README.coding.md +++ b/READMEs/README.coding.md @@ -567,6 +567,12 @@ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS to add your certificate to the SSL_CTX directly. The vhost SSL_CTX * is in the user parameter in that callback. +@section tls_cleanup Process-wide TLS library cleanup + +If you are using OpenSSL (>= 1.1.0) and you destroy the last `lws_context`, you may want to clean up the process-wide allocations made by the TLS library. You can call `lws_tls_cleanup_process()` to do this. + +However, be aware that if you call this, you cannot re-initialize the OpenSSL library in the same process. So only call it if you are completely finished with `libwebsockets` and the TLS library. If you plan to create a new `lws_context` later in the same process lifecycle, you must not call this API. + @section clientasync Async nature of client connections When you call `lws_client_connect_info(..)` and get a `wsi` back, it does not diff --git a/READMEs/README.lws_plugins.md b/READMEs/README.lws_plugins.md index 5a4b7d9e84..7c9dd8db79 100644 --- a/READMEs/README.lws_plugins.md +++ b/READMEs/README.lws_plugins.md @@ -103,3 +103,49 @@ lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, plugins and a pointer to its exported header object, so you can walk this after loading. +## Protocol Plugin Best Practices + +When writing a protocol plugin that utilizes `LWS_CALLBACK_PROTOCOL_INIT`, you must follow these requirements: + +### 1. Ignore NULL `in` Parameters + +During context creation or system initialization, `LWS_CALLBACK_PROTOCOL_INIT` may be called with a `NULL` `in` parameter (which normally carries the `lws_protocol_vhost_options`). Your plugin must safely ignore this and exit without error: + +```c + case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; +``` + +### 2. Contextual Warning for PVO Errors + +When parsing `lws_protocol_vhost_options` (PVOs) during `PROTOCOL_INIT`, if an error occurs (such as a missing or invalid value), you should use `lws_vhost_warn(lws_get_vhost(wsi), ...)` or `lws_vhost_err(...)` instead of generic logging. This ensures the user can understand *which* vhost is misconfigured, especially in multi-vhost setups. + +### 3. Stub Process Isolation + +Generic stub processes (such as `--lws-stub=dnssec-priv`) inherit the user's config and will attempt to initialize all plugins. To prevent resource contention (like conflicting UDP port bindings or duplicated threads), plugins must explicitly opt-in or opt-out of running inside stubs. + +If your plugin **should never run** inside a stub process (which is the case for most application and UI plugins), you must inject this snippet at the top of your `PROTOCOL_INIT` block: + +```c + case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; + + /* Do not initialize in generic stub processes */ + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; +``` + +If your plugin **is explicitly designed to run** inside a specific stub (e.g. `dnssec-monitor` running inside `dnssec-priv`), you must modify the snippet to ensure it only initializes for *that specific* stub, and ignores any others: + +```c + case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; + + /* Only initialize if we are running as the dnssec-priv stub */ + const char *stub = lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub"); + if (stub && strcmp(stub, "dnssec-priv")) + return 0; +``` diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 0ed5162c4b..ab5ed9751e 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -944,7 +944,9 @@ lws_fx_string(const lws_fx_t *a, char *buf, size_t size); #include #include #include +#include #include +#include #include #include #include diff --git a/include/libwebsockets/lws-async-dns.h b/include/libwebsockets/lws-async-dns.h index 3e75642167..cd5028c4ff 100644 --- a/include/libwebsockets/lws-async-dns.h +++ b/include/libwebsockets/lws-async-dns.h @@ -62,6 +62,7 @@ typedef enum { #define LWS_ADNS_NOCACHE 0x40000 /* force network query, bypass cache */ #define LWS_ADNS_WANT_DNSSEC 0x80000 /* Explicitly set DO bit in EDNS0 OPT record */ #define LWS_ADNS_IGNORE_HOSTS_FILE 0x100000 /* Bypass checking /etc/hosts and force network DNS lookup */ +#define LADNS_NO_WSI_BUT_OK ((struct lws *)(intptr_t)0x1) struct addrinfo; diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h index 0165c56ff1..0b80ca8e06 100644 --- a/include/libwebsockets/lws-callbacks.h +++ b/include/libwebsockets/lws-callbacks.h @@ -885,7 +885,13 @@ enum lws_callback_reasons { * Return nonzero to close the wsi. */ - LWS_CALLBACK_HTTP_INTERCEPTOR_CHECK = 213, + LWS_CALLBACK_MQTT_QOS2_RX_COMPLETE = 213, + /**< When a QoS2 message has fully completed the transaction (PUBREL + * received, PUBCOMP sent), this callback is generated. + * `in` will point to the `uint16_t` packet ID that completed. + */ + + LWS_CALLBACK_HTTP_INTERCEPTOR_CHECK = 214, /**< A mount has a interceptor_path enabled, this callback asks the * protocol bound to that mount if it is OK for this request to * proceed. If returning 0, the request proceeds to the original @@ -893,12 +899,12 @@ enum lws_callback_reasons { * mount. */ - LWS_CALLBACK_GET_PSS_SIZE = 214, + LWS_CALLBACK_GET_PSS_SIZE = 215, /**< Called when a protocol wants to specify its PSS size at runtime. * If the protocol structure has per_session_data_size == 0, lws will * call this to get the size to allocate for the session. */ - LWS_CALLBACK_DHT_VERB_DISPATCH = 215, + LWS_CALLBACK_DHT_VERB_DISPATCH = 216, /**< Sent to the user protocol handler callback when a DHT message * carrying a registered verb has been matched by lws-dht. * `in` is a pointer to `struct lws_dht_verb_dispatch_args` containing diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 112119be50..90f929b81c 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -1027,6 +1027,10 @@ struct lws_context_creation_info { /**< CONTEXT: NULL, or interface name to bind outgoing WOL packet to */ #endif + const char *lws_stub; + /**< CONTEXT: if non-NULL, the name of the stub function requested + * via --lws-stub=... commandline switch. Filled in by + * lws_cmdline_option_handle_builtin(). */ int argc; /**< CONTEXT: optionally pass the app commandline to the context, so we can use it * as part of lws_cmdline_option_cx() */ @@ -1102,6 +1106,18 @@ lws_create_context(const struct lws_context_creation_info *info); LWS_VISIBLE LWS_EXTERN void lws_context_destroy(struct lws_context *context); +/** + * lws_tls_cleanup_process() - cleanup process-wide TLS allocations + * + * This function can be called after the last context is destroyed to + * cleanup process-wide TLS allocations. For example, for OpenSSL it + * may call OPENSSL_cleanup() if supported. + * You should only call this if you are absolutely sure you will not + * reinitialize libwebsockets or the TLS library in this process. + */ +LWS_VISIBLE LWS_EXTERN void +lws_tls_cleanup_process(void); + typedef int (*lws_reload_func)(void); /** diff --git a/include/libwebsockets/lws-dht.h b/include/libwebsockets/lws-dht.h index 0e5a099a24..5f94a4ad58 100644 --- a/include/libwebsockets/lws-dht.h +++ b/include/libwebsockets/lws-dht.h @@ -238,7 +238,7 @@ lws_dht_register_verbs(struct lws_dht_ctx *ctx, const char **verbs, int count, c * \return number of subscribers notified, or negative on error */ LWS_VISIBLE LWS_EXTERN int -lws_dht_notify_subscribers(struct lws_dht_ctx *ctx, const lws_dht_hash_t *hash, const uint8_t *sha256); +lws_dht_notify_subscribers(struct lws_dht_ctx *ctx, const lws_dht_hash_t *hash, const uint8_t *sha256, const uint8_t *payload, size_t payload_len); /** * lws_dht_blacklist_cb_t() - DHT blacklist check callback diff --git a/include/libwebsockets/lws-gc9a01a-spi.h b/include/libwebsockets/lws-gc9a01a-spi.h new file mode 100644 index 0000000000..798e266c73 --- /dev/null +++ b/include/libwebsockets/lws-gc9a01a-spi.h @@ -0,0 +1,54 @@ +/* + * lws abstract display implementation for gc9a01a on spi + * + * Copyright (C) 2019 - 2026 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 + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#if !defined(__LWS_DISPLAY_GC9A01A_SPI_H__) +#define __LWS_DISPLAY_GC9A01A_SPI_H__ + + +typedef struct lws_display_gc9a01a { + + lws_display_t disp; /* use lws_display_gc9a01a_ops to set */ + const lws_spi_ops_t *spi; /* spi ops */ + + lws_display_completion_t cb; + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + + uint8_t spi_index; /* cs index starting from 0 */ + +} lws_display_gc9a01a_t; + +int +lws_display_gc9a01a_spi_init(lws_display_state_t *lds); +int +lws_display_gc9a01a_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); +int +lws_display_gc9a01a_spi_power(lws_display_state_t *lds, int state); + +#define lws_display_gc9a01a_ops \ + .init = lws_display_gc9a01a_spi_init, \ + .blit = lws_display_gc9a01a_spi_blit, \ + .power = lws_display_gc9a01a_spi_power +#endif diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index 80c849f75f..d1441d1821 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -906,6 +906,28 @@ lws_get_child(const struct lws *wsi); LWS_VISIBLE LWS_EXTERN void lws_get_effective_uid_gid(struct lws_context *context, uid_t *uid, gid_t *gid); +/** + * lws_plat_user_to_uid() - Find uid from username + * \param username: string username, or string containing numeric uid + * \param puid: pointer to uid_t to receive result + * + * Returns 0 if found and *puid is set, else nonzero. + * If the string contains a number, it is parsed directly. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plat_user_to_uid(const char *username, uid_t *puid); + +/** + * lws_plat_group_to_gid() - Find gid from groupname + * \param groupname: string groupname, or string containing numeric gid + * \param pgid: pointer to gid_t to receive result + * + * Returns 0 if found and *pgid is set, else nonzero. + * If the string contains a number, it is parsed directly. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plat_group_to_gid(const char *groupname, gid_t *pgid); + /** * lws_get_udp() - get wsi's udp struct * diff --git a/include/libwebsockets/lws-mqtt.h b/include/libwebsockets/lws-mqtt.h index 32e1d278ed..dd6946d0a6 100644 --- a/include/libwebsockets/lws-mqtt.h +++ b/include/libwebsockets/lws-mqtt.h @@ -73,7 +73,7 @@ typedef struct lws_mqtt_str_st lws_mqtt_str_t; typedef enum { QOS0, QOS1, - QOS2, /* not supported */ + QOS2, RESERVED_QOS_LEVEL, FAILURE_QOS_LEVEL = 0x80 } lws_mqtt_qos_levels_t; @@ -88,6 +88,11 @@ typedef union { uint8_t bits; } lws_mqtt_fixed_hdr_t; +typedef struct lws_mqtt_qos2_state_ops { + int (*rx_add)(struct lws *wsi, const char *client_id, uint16_t pkt_id); + int (*rx_remove)(struct lws *wsi, const char *client_id, uint16_t pkt_id); +} lws_mqtt_qos2_state_ops_t; + /* * MQTT connection parameters, passed into struct * lws_client_connect_info to establish a connection using @@ -122,6 +127,7 @@ typedef struct lws_mqtt_client_connect_param_s { parameters */ const char *username; const char *password; + const lws_mqtt_qos2_state_ops_t *qos2_state_ops; uint8_t aws_iot; } lws_mqtt_client_connect_param_t; @@ -383,4 +389,16 @@ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_mqtt_client_send_unsubcribe(struct lws *wsi, const lws_mqtt_subscribe_param_t *unsub); +/** + * lws_mqtt_client_qos2_rx_add() - inject a saved QoS2 packet ID into the rx list + * + * \param wsi: the mqtt child wsi + * \param pkt_id: the packet ID to inject + * + * This allows the application to repopulate the unacknowledged QoS2 receives + * from persistent storage when a session is resumed. + */ +LWS_VISIBLE LWS_EXTERN int +lws_mqtt_client_qos2_rx_add(struct lws *wsi, uint16_t pkt_id); + #endif /* _LWS_MQTT_H */ diff --git a/include/libwebsockets/lws-x509.h b/include/libwebsockets/lws-x509.h index 137df5be24..f30bdf36a3 100644 --- a/include/libwebsockets/lws-x509.h +++ b/include/libwebsockets/lws-x509.h @@ -119,6 +119,37 @@ lws_x509_create_self_signed(struct lws_context *context, uint8_t **key_buf, size_t *key_len, const char *san, int key_bits); +struct lws_x509_cert_gen_info { + const char *san; /* Subject Alt Name / CN */ + const char *ca_cert_pem; /* Optional CA cert to sign with */ + const char *ca_key_pem; /* Optional CA key to sign with */ + const char *curve_name; /* e.g., "P-521" or "P-384" for ECDSA */ + int key_bits; /* If curve_name is NULL, use RSA with these bits */ + int is_ca; /* 1 = CA:TRUE (Basic Constraints) */ + int is_server; /* 1 = serverAuth, 0 = clientAuth */ +}; + +/** + * lws_x509_create_cert() - Create a certificate (self-signed or CA-signed) + * + * \param context: lws_context + * \param cert_buf: pointer to pointer to be set to allocated DER cert + * \param cert_len: pointer to size_t to be set to length of allocated cert + * \param key_buf: pointer to pointer to be set to allocated DER private key + * \param key_len: pointer to size_t to be set to length of allocated key + * \param info: struct containing generation parameters + * + * Creates a certificate and private key in memory (DER format). + * The caller is responsible for freeing *cert_buf and *key_buf using lws_free(). + * + * Returns 0 on success. + */ +LWS_VISIBLE LWS_EXTERN int +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info); + /** * lws_x509_parse_from_pem() - Read one or more x509 certs in PEM format from memory * diff --git a/lib/core-net/txpacer.c b/lib/core-net/txpacer.c index c23f25c6ee..13185cb86f 100644 --- a/lib/core-net/txpacer.c +++ b/lib/core-net/txpacer.c @@ -92,8 +92,11 @@ lws_txpacer_thread(void *d) } else { /* Calculate sleep time */ struct timespec ts; - lws_usec_t target_us = lws_now_usecs() + txp->txp_info.interval_us; - ts.tv_sec = (time_t)(target_us / 1000000); + struct timeval tv; + gettimeofday(&tv, NULL); + + uint64_t target_us = (uint64_t)tv.tv_usec + txp->txp_info.interval_us; + ts.tv_sec = tv.tv_sec + (time_t)(target_us / 1000000); ts.tv_nsec = (long)((target_us % 1000000) * 1000); /* Wait for signal (new packet) or timeout (next pacing tick) */ diff --git a/lib/core-net/wsi-timeout.c b/lib/core-net/wsi-timeout.c index 7902b551ec..e6a531d1fa 100644 --- a/lib/core-net/wsi-timeout.c +++ b/lib/core-net/wsi-timeout.c @@ -230,9 +230,13 @@ lws_validity_cb(lws_sorted_usec_list_t *sul) /* one of either the ping or hangup validity threshold was crossed */ if (wsi->validity_hup) { - lwsl_notice("%s: VALIDITY TIMEOUT EXPIRED ON WSI %p! Server is closing connection. (ping=%d, hangup=%d)\n", - __func__, wsi, rbo ? rbo->secs_since_valid_ping : 0, rbo ? rbo->secs_since_valid_hangup : 0); - lwsl_wsi_err(wsi, "validity too old"); + char buf[128]; + buf[0] = '\0'; + lws_get_peer_simple(wsi, buf, sizeof(buf)); + + lwsl_wsi_notice(wsi, "VALIDITY TIMEOUT EXPIRED ON (protocol %s, peer %s)! Server is closing connection. (ping=%d, hangup=%d)\n", + wsi->a.protocol ? wsi->a.protocol->name : "none", buf, + rbo ? rbo->secs_since_valid_ping : 0, rbo ? rbo->secs_since_valid_hangup : 0); struct lws_context *cx = wsi->a.context; struct lws_context_per_thread *pt = &cx->pt[(int)wsi->tsi]; @@ -308,8 +312,7 @@ lws_validity_confirmed(struct lws *wsi) * to the role to figure out who actually needs to understand their * validity was confirmed. */ - if (!wsi->h2_stream_carries_ws && /* only if not encapsulated */ - wsi->role_ops && + if (wsi->role_ops && lws_rops_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive)) lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive). issue_keepalive(wsi, 1); diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index a6e30dd423..6ad3ef774c 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -1270,8 +1270,8 @@ void lws_http_close_immortal(struct lws *wsi) { * since we closed the only immortal stream on this nwsi, we * need to reapply a normal timeout regime to the nwsi */ - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, - lws_wsi_keepalive_timeout_eff(wsi)); + lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, + lws_wsi_keepalive_timeout_eff(nwsi)); } void lws_mux_mark_immortal(struct lws *wsi) { diff --git a/lib/core/context.c b/lib/core/context.c index 4758f038b8..26931acb51 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -671,6 +671,7 @@ lws_create_context(const struct lws_context_creation_info *info) context->event_loop_ops = plev->ops; context->us_wait_resolution = us_wait_resolution; context->wol_if = info->wol_if; + context->lws_stub = info->lws_stub; #if defined(LWS_WITH_TLS_JIT_TRUST) { struct lws_cache_creation_info ci; diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index a2f9f9a6ce..4bd716015b 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1692,6 +1692,7 @@ static const char * const builtins[] = { "--ssproxy-port", "--ssproxy-iface", "--ssproxy-ads", + "--lws-stub", }; enum opts { @@ -1702,6 +1703,7 @@ enum opts { OPT_SSPROXY_PORT, OPT_SSPROXY_IFACE, OPT_SSPROXY_ADS, + OPT_LWS_STUB, }; static void @@ -1839,6 +1841,10 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv, signal(SIGTERM, lws_sigterm_catch); #endif break; + + case OPT_LWS_STUB: + info->lws_stub = p; + break; } } diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index c5a0bfe1a1..d983a25e9a 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -646,6 +646,7 @@ struct lws_context { #endif const char *wol_if; + const char *lws_stub; /* * <====== LWS_WITH_NETWORK end diff --git a/lib/drivers/CMakeLists.txt b/lib/drivers/CMakeLists.txt index 9cc7b646cf..b889f3cfba 100644 --- a/lib/drivers/CMakeLists.txt +++ b/lib/drivers/CMakeLists.txt @@ -2,6 +2,7 @@ list(APPEND SOURCES drivers/display/lws-display.c drivers/display/ssd1306-i2c.c drivers/display/ili9341-spi.c + drivers/display/gc9a01a-spi.c drivers/display/spd1656-spi.c drivers/display/uc8176-spi.c drivers/display/ssd1675b-spi.c diff --git a/lib/drivers/display/gc9a01a-spi.c b/lib/drivers/display/gc9a01a-spi.c new file mode 100644 index 0000000000..e55c820d62 --- /dev/null +++ b/lib/drivers/display/gc9a01a-spi.c @@ -0,0 +1,413 @@ +/* + * lws abstract display implementation for gc9a01a on spi + * + * Copyright (C) 2019 - 2026 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 + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * This is somewhat complicated by the platform SPI may a) need special + * allocation for the display driver-private packed line buffers, and b) the + * allocated memory may have 32-bit alignment and access requirements. + * + * The allocation is handled by having ops members in the SPI driver ops struct, + * the alignment has to be observed in the display driver. + */ + +#include +#include + +enum { + + GC9A01A_NOP = 0x00, + GC9A01A_SWRESET = 0x01, + GC9A01A_RDDID = 0x04, + GC9A01A_RDDST = 0x09, + + GC9A01A_SLPIN = 0x10, + GC9A01A_SLPOUT = 0x11, + GC9A01A_PTLON = 0x12, + GC9A01A_NORON = 0x13, + + GC9A01A_INVOFF = 0x20, + GC9A01A_INVON = 0x21, + GC9A01A_DISPOFF = 0x28, + GC9A01A_DISPON = 0x29, + GC9A01A_CASET = 0x2a, + GC9A01A_PASET = 0x2b, + GC9A01A_RAMWR = 0x2c, + GC9A01A_RAMRD = 0x2e, + + GC9A01A_PTLAR = 0x30, + GC9A01A_VSCRDEF = 0x33, + GC9A01A_TEOFF = 0x34, + GC9A01A_TEON = 0x35, + GC9A01A_MADCTL = 0x36, + GC9A01A_VSCRSADD = 0x37, + GC9A01A_PIXFMT = 0x3a, +}; + +typedef enum { + LWSDISPST_IDLE, + LWSDISPST_INIT1, + LWSDISPST_INIT2, + LWSDISPST_INIT3, + LWSDISPST_INIT4, + LWSDISPST_INIT5, + + LWSDISPRET_ASYNC = 1 +} lws_display_update_state_t; + +typedef struct lws_display_gc9a01a_spi_state { + struct lws_display_state *lds; + + uint32_t *line[2]; + lws_surface_error_t *u[2]; + + lws_sorted_usec_list_t sul; + int state; +} lws_display_gc9a01a_spi_state_t; + +#define lds_to_disp(_lds) (const lws_display_gc9a01a_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_gc9a01a_spi_state_t *)_lds->priv; + +#define pack_native_pixel(_line, _x, _c) { \ + if (!(_x & 1)) \ + *_line = htons(_c); \ + else \ + { *_line = (*_line) | (htons(_c) << 16); _line++; } } + +static const uint8_t gc9a01a_240x240_init[] = { + 2, 0xEF, 0xEB, 0x14, + 1, 0xFE, + 1, 0xEF, + 2, 0xEB, 0x14, + 2, 0x84, 0x40, + 2, 0x85, 0xFF, + 2, 0x86, 0xFF, + 2, 0x87, 0xFF, + 2, 0x88, 0x0A, + 2, 0x89, 0x21, + 2, 0x8A, 0x00, + 2, 0x8B, 0x80, + 2, 0x8C, 0x01, + 2, 0x8D, 0x01, + 2, 0x8E, 0xFF, + 2, 0x8F, 0xFF, + 3, 0xB6, 0x00, 0x20, + 2, 0x3A, 0x05, /* 16-bit RGB565 */ + 5, 0x90, 0x08, 0x08, 0x08, 0x08, + 2, 0xBD, 0x06, + 2, 0xBC, 0x00, + 4, 0xFF, 0x60, 0x01, 0x04, + 2, 0xC3, 0x13, + 2, 0xC4, 0x13, + 2, 0xC9, 0x22, + 2, 0xBE, 0x11, + 3, 0xE1, 0x10, 0x0E, + 4, 0xDF, 0x21, 0x0C, 0x02, + 7, 0xF0, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A, + 7, 0xF1, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F, + 7, 0xF2, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A, + 7, 0xF3, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F, + 3, 0xED, 0x1B, 0x0B, + 2, 0xAE, 0x77, + 2, 0xCD, 0x63, + 10, 0x70, 0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03, + 2, 0xE8, 0x34, + 13, 0x62, 0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70, + 13, 0x63, 0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70, + 8, 0x64, 0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07, + 11, 0x66, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00, + 11, 0x67, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98, + 8, 0x74, 0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00, + 3, 0x98, 0x3E, 0x07, + 1, 0x35, + 1, 0x21, + 0, GC9A01A_SLPOUT +}, gc9a01a_240x240_dispon[] = { + 0, GC9A01A_DISPON +}, gc9a01a_240x240_sleep_in[] = { + 0, GC9A01A_SLPIN +}, gc9a01a_240x240_sleep_out[] = { + 0, GC9A01A_SLPOUT +}; + +static void +async_cb(lws_sorted_usec_list_t *sul) +{ + lws_display_gc9a01a_spi_state_t *priv = lws_container_of(sul, + lws_display_gc9a01a_spi_state_t, sul); + const lws_display_gc9a01a_t *disp = lds_to_disp(priv->lds); + + switch (priv->state) { + case LWSDISPST_INIT1: + if (disp->gpio) + disp->gpio->set(disp->reset_gpio, 0); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT2: + if (disp->gpio) + disp->gpio->set(disp->reset_gpio, 1); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 120); + break; + + case LWSDISPST_INIT3: + lws_spi_table_issue(disp->spi, 0, gc9a01a_240x240_init, + LWS_ARRAY_SIZE(gc9a01a_240x240_init)); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 120); + break; + + case LWSDISPST_INIT4: + lws_spi_table_issue(disp->spi, 0, gc9a01a_240x240_dispon, + LWS_ARRAY_SIZE(gc9a01a_240x240_dispon)); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 20); + break; + + case LWSDISPST_INIT5: + if (disp->spi->in_flight) + while (disp->spi->in_flight(disp->spi)) + ; + + priv->state = LWSDISPST_IDLE; + if (disp->cb) + disp->cb(priv->lds, 1); + break; + + default: + break; + } +} + +int +lws_display_gc9a01a_spi_init(lws_display_state_t *lds) +{ + const lws_display_gc9a01a_t *disp = lds_to_disp(lds); + lws_display_gc9a01a_spi_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + + /* hardware nRESET */ + + if (disp->gpio) + disp->gpio->mode(disp->reset_gpio, LWSGGPIO_FL_WRITE | + LWSGGPIO_FL_PULLUP); + + priv->state = LWSDISPST_INIT1; + lws_sul_schedule(lds->ctx, 0, &priv->sul, async_cb, 1); + + return 0; +} + +/* backlight handled by PWM */ + +int +lws_display_gc9a01a_spi_brightness(lws_display_state_t *lds, uint8_t b) +{ + return 0; +} + +int +lws_display_gc9a01a_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids) +{ + lws_display_gc9a01a_spi_state_t *priv = lds_to_priv(lds); + const lws_display_gc9a01a_t *disp = lds_to_disp(lds); + const lws_surface_info_t *ic = &lds->disp->ic; + lws_greyscale_error_t *gedl_this, *gedl_next; + lws_colour_error_t *edl_this, *edl_next; + int bytes_pl = ic->wh_px[0].whole * 2; + static DMA_ATTR uint32_t buf[5]; + lws_display_list_coord_t h, y; + lws_display_colour_t c; + lws_spi_desc_t desc; + const uint8_t *pc; + uint32_t *lo; + int n, m; + + if (!priv->line[0]) { + if (disp->spi->alloc_dma) + priv->line[0] = disp->spi->alloc_dma(disp->spi, + bytes_pl * 2); + else + priv->line[0] = lws_malloc(bytes_pl * 2, __func__); + + if (!priv->line[0]) { + lwsl_err("%s: failed to alloc %u\n", __func__, + (unsigned int)bytes_pl * 2); + return 1; + } + + priv->line[1] = (uint32_t *)((uint8_t *)priv->line[0] + bytes_pl); + + if (lws_display_alloc_diffusion(ic, priv->u)) { + if (disp->spi->free_dma) + disp->spi->free_dma(disp->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } + + pc = src; + lo = priv->line[box->y.whole & 1]; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + desc.src = (uint8_t *)&buf[4]; + + /* + * Blit a line at a time + */ + + h = box->h.whole; + y = box->y.whole; + + if (h > 1) { + + buf[4] = GC9A01A_CASET; + desc.data = (uint8_t *)&buf[0]; + desc.flags = 0; + buf[0] = (((box->x.whole + box->w.whole - 1) & 0xff) << 24) | (((box->x.whole + box->w.whole - 1) >> 8) << 16) | ((box->x.whole & 0xff) << 8) | (box->x.whole >> 8); + desc.count_write = 4; + disp->spi->queue(disp->spi, &desc); + + buf[4] = GC9A01A_PASET; + buf[0] = (((box->y.whole + box->h.whole - 1) & 0xff) << 24) | (((box->y.whole + box->h.whole - 1) >> 8) << 16) | ((box->y.whole & 0xff) << 8) | (box->y.whole >> 8); + disp->spi->queue(disp->spi, &desc); + + buf[4] = GC9A01A_RAMWR; + /* priv->line is already allocated for DMA */ + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED | LWS_SPI_FLAG_DATA_CONTINUE; + desc.count_write = 0; + disp->spi->queue(disp->spi, &desc); + + return 0; + } + + if (h) { + + edl_this = (lws_colour_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + edl_next = (lws_colour_error_t *)priv->u[box->y.whole & 1]; + gedl_this = (lws_greyscale_error_t *)edl_this; + gedl_next = (lws_greyscale_error_t *)edl_next; + + if (!pc) { + for (n = 0; n < ic->wh_px[0].whole; n++) + pack_native_pixel(lo, n, 0xffff); + goto go; + } + + if (ic->greyscale) + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, ic->palette, + ic->palette_depth, c, &gedl_this[n]); + pack_native_pixel(lo, n, m); + + dist_err_floyd_steinberg_grey(n, ic->wh_px[0].whole, + gedl_this, gedl_next); + pc++; + } + else + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[2] << 16) | (pc[1] << 8) | pc[0]; + + m = lws_display_palettize_col(ic, ic->palette, + ic->palette_depth, c, &edl_this[n]); + pack_native_pixel(lo, n, m); + + dist_err_floyd_steinberg_col(n, ic->wh_px[0].whole, + edl_this, edl_next); + + pc += 3; + } + +go: + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + if (y + 1 != ic->wh_px[1].whole) + desc.flags |= LWS_SPI_FLAG_DATA_CONTINUE; + + desc.data = (uint8_t *)priv->line[box->y.whole & 1]; + desc.count_write = bytes_pl; + desc.count_cmd = 0; + + if (disp->spi->queue(disp->spi, &desc)) { + lwsl_err("%s: failed to queue\n", __func__); + } + + src += bytes_pl; + y++; + + return 0; + } + + if (!box->h.whole) { + + if (disp->spi->in_flight) + while (disp->spi->in_flight(disp->spi)) + ; + + if (disp->spi->free_dma) + disp->spi->free_dma(disp->spi, (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + + lws_free_set_NULL(priv->u[0]); + + if (disp->cb) + disp->cb(priv->lds, 2); + } + + return 0; +} + +int +lws_display_gc9a01a_spi_power(lws_display_state_t *lds, int state) +{ + const lws_display_gc9a01a_t *disp = lds_to_disp(lds); + + if (state) + lws_spi_table_issue(disp->spi, 0, gc9a01a_240x240_sleep_out, + LWS_ARRAY_SIZE(gc9a01a_240x240_sleep_out)); + else + lws_spi_table_issue(disp->spi, 0, gc9a01a_240x240_sleep_in, + LWS_ARRAY_SIZE(gc9a01a_240x240_sleep_in)); + + /* we're not going to do anything useful for 5ms after this */ + + return 0; +} diff --git a/lib/misc/dht/dht-backend-bucket.c b/lib/misc/dht/dht-backend-bucket.c index 30281a7ae7..0d216324d7 100644 --- a/lib/misc/dht/dht-backend-bucket.c +++ b/lib/misc/dht/dht-backend-bucket.c @@ -316,7 +316,9 @@ maybe_new_node(struct lws_dht_ctx *ctx, const lws_dht_hash_t *id, } if (is_martian(sa) || node_blacklisted(ctx, sa, salen)) { - lwsl_dht_warn("%s: martian or blacklisted\n", __func__); + char buf[64] = "unknown"; + lws_sa46_write_numeric_address((lws_sockaddr46 *)sa, buf, sizeof(buf)); + lwsl_dht_warn("%s: martian or blacklisted: %s\n", __func__, buf); return NULL; } diff --git a/lib/misc/dht/dht-backend-rpc.c b/lib/misc/dht/dht-backend-rpc.c index a2b950deef..20405f08dd 100644 --- a/lib/misc/dht/dht-backend-rpc.c +++ b/lib/misc/dht/dht-backend-rpc.c @@ -49,11 +49,12 @@ send_pong(struct lws_dht_ctx *ctx, const struct sockaddr *sa, size_t salen, rc = lws_snprintf(buf + i, sizeof(buf) - i, "1:y1:re"); if (dht_tx_skip(&i, sizeof(buf), (size_t)(rc))) goto fail; - lwsl_warn("%s: generated pong of length %zu\n", __func__, i); - lwsl_hexdump_warn(buf, i); + // lwsl_warn("%s: generated pong of length %zu\n", __func__, i); + // lwsl_hexdump_warn(buf, i); rc = dht_send(ctx, buf, i, sa, salen); - lwsl_warn("%s: dht_send returned %d\n", __func__, rc); + // lwsl_warn("%s: dht_send returned %d\n", __func__, rc); + return rc; fail: diff --git a/lib/misc/dht/dht-backend-search.c b/lib/misc/dht/dht-backend-search.c index b603e00b6a..564d5fddca 100644 --- a/lib/misc/dht/dht-backend-search.c +++ b/lib/misc/dht/dht-backend-search.c @@ -433,7 +433,7 @@ lws_dht_search(struct lws_dht_ctx *ctx, const lws_dht_hash_t *id, int port, int } sr->af = af; sr->tid = ctx->search_id++; - sr->step_time = 0; + sr->step_time = ctx->now.tv_sec; sr->id = lws_dht_hash_dup(id); if (!sr->id) { diff --git a/lib/misc/dht/dht-bencode.c b/lib/misc/dht/dht-bencode.c index 891a92f443..c9fc796fc5 100644 --- a/lib/misc/dht/dht-bencode.c +++ b/lib/misc/dht/dht-bencode.c @@ -70,10 +70,9 @@ parse_hash(const uint8_t *dict, const uint8_t *end, const char *key, if (hash_data && lws_dht_hash_validate(type, len)) { *h_ret = lws_dht_hash_create(type, len, hash_data); - } else { + } else lwsl_notice("%s: rejecting invalid/unsupported hash type %d len %d\n", __func__, type, len); - } } } @@ -820,10 +819,9 @@ lws_dht_process_packet(struct lws_dht_ctx *ctx, const void *buf, size_t buflen, memcpy(&decoded_seq, mp.tid + 2, 2); if (decoded_seq == ctx->ip_monitor_seqno) { is_nonce_validated = 1; - lwsl_notice("%s: IP Challenge matched sequence %u from %s!\n", __func__, decoded_seq, (ss.ss_family == AF_INET ? "IPv4" : "IPv6")); - } else { + lwsl_info("%s: IP Challenge matched sequence %u from %s!\n", __func__, decoded_seq, (ss.ss_family == AF_INET ? "IPv4" : "IPv6")); + } else lwsl_dht_warn("%s: Spurious IP tracking reply dropped!\n", __func__); - } } if (!is_nonce_validated) @@ -971,7 +969,8 @@ lws_dht_process_packet(struct lws_dht_ctx *ctx, const void *buf, size_t buflen, int p2 = 0; if (from->sa_family == AF_INET) p2 = ntohs(((struct sockaddr_in *)from)->sin_port); - lwsl_err("%s: ACK received from %s:%d (len %d) but no sequencer found!\n", __func__, ads, p2, (int)fromlen); + else if (from->sa_family == AF_INET6) p2 = ntohs(((struct sockaddr_in6 *)from)->sin6_port); + lwsl_info("%s: Duplicate/Late ACK received from %s:%d (len %d) - no active sequencer\n", __func__, ads, p2, (int)fromlen); lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&ctx->ts_owner)) { lws_dht_ts_t *dts = lws_container_of(d, lws_dht_ts_t, list); @@ -979,7 +978,8 @@ lws_dht_process_packet(struct lws_dht_ctx *ctx, const void *buf, size_t buflen, lws_sa46_write_numeric_address((lws_sockaddr46 *)&dts->sa, d_ads, sizeof(d_ads)); int p1 = 0; if (dts->sa.ss_family == AF_INET) p1 = ntohs(((struct sockaddr_in *)&dts->sa)->sin_port); - lwsl_err(" ... Active Sequencer in pool: dest %s:%d (family %d vs %d)\n", d_ads, p1, dts->sa.ss_family, from->sa_family); + else if (dts->sa.ss_family == AF_INET6) p1 = ntohs(((struct sockaddr_in6 *)&dts->sa)->sin6_port); + lwsl_debug(" ... Active Sequencer in pool: dest %s:%d (family %d vs %d)\n", d_ads, p1, dts->sa.ss_family, from->sa_family); } lws_end_foreach_dll(d); } break; diff --git a/lib/misc/dht/dht.c b/lib/misc/dht/dht.c index a9343e9195..d7428de0fc 100644 --- a/lib/misc/dht/dht.c +++ b/lib/misc/dht/dht.c @@ -188,7 +188,7 @@ dht_on_rx_data(struct lws_transport_sequencer *ts, uint64_t offset, struct sockaddr_in6 *s = (struct sockaddr_in6 *)&dts->sa; inet_ntop(AF_INET6, &s->sin6_addr, buf_ip, sizeof(buf_ip)); } - lwsl_user("%s: [DEBUG] Received raw UDP packet from %s on sequencer: offset=%llu len=%zu\n", __func__, buf_ip, (unsigned long long)offset, len); + // lwsl_notice("%s: [DEBUG] Received raw UDP packet from %s on sequencer: offset=%llu len=%zu\n", __func__, buf_ip, (unsigned long long)offset, len); int parse_ret = lws_dht_msg_parse((const char *)buf, len, &msg); if (!parse_ret) { @@ -202,7 +202,7 @@ dht_on_rx_data(struct lws_transport_sequencer *ts, uint64_t offset, lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&dts->ctx->verb_owner)) { struct lws_dht_verb_list *vl = lws_container_of(d, struct lws_dht_verb_list, list); - lwsl_user("CAP_REQ Generator Iteration: verb=%s, protocol=%s\n", + lwsl_notice("CAP_REQ Generator Iteration: verb=%s, protocol=%s\n", vl->v.name ? vl->v.name : "null", (vl->v.protocol && vl->v.protocol->name) ? vl->v.protocol->name : "null"); @@ -260,7 +260,7 @@ dht_on_rx_data(struct lws_transport_sequencer *ts, uint64_t offset, args.out_precedence = LWS_DHT_VERB_RESULT_PROCEED; - lwsl_user("DISPATCHING %s to protocol %s\n", msg.verb, vl->v.protocol->name); + // lwsl_notice("DISPATCHING %s to protocol %s\n", msg.verb, vl->v.protocol->name); n = vl->v.protocol->callback((struct lws *)&a, LWS_CALLBACK_DHT_VERB_DISPATCH, lws_protocol_vh_priv_get(dts->ctx->vhost, vl->v.protocol), @@ -300,6 +300,16 @@ dht_on_rx_data(struct lws_transport_sequencer *ts, uint64_t offset, return 0; } +static void +lws_dht_ts_idle_cb(lws_sorted_usec_list_t *sul) +{ + lws_dht_ts_t *dts = lws_container_of(sul, lws_dht_ts_t, sul_idle); + + lws_transport_sequencer_destroy(&dts->ts); + lws_dll2_remove(&dts->list); + lws_free(dts); +} + void dht_on_state_change(struct lws_transport_sequencer *ts, int state, int status) { @@ -312,8 +322,12 @@ dht_on_state_change(struct lws_transport_sequencer *ts, int state, int status) NULL, (void *)(intptr_t)status, 0, (struct sockaddr *)&dts->sa, dts->salen); - lws_dll2_remove(&dts->list); - lws_free(dts); + if (state != 0) { + lws_sul_cancel(&dts->sul_idle); + lws_transport_sequencer_destroy(&dts->ts); + lws_dll2_remove(&dts->list); + lws_free(dts); + } } static const lws_transport_sequencer_ops_t dht_seq_ops = { @@ -362,8 +376,10 @@ lws_dht_get_ts(struct lws_dht_ctx *ctx, const struct sockaddr *dest, size_t sale } } - if (match) + if (match) { + lws_sul_schedule(ctx->vhost->context, 0, &dts->sul_idle, lws_dht_ts_idle_cb, 30 * LWS_US_PER_SEC); return dts->ts; + } d = d->next; } @@ -401,6 +417,7 @@ lws_dht_get_ts(struct lws_dht_ctx *ctx, const struct sockaddr *dest, size_t sale } lws_dll2_add_tail(&dts->list, &ctx->ts_owner); + lws_sul_schedule(ctx->vhost->context, 0, &dts->sul_idle, lws_dht_ts_idle_cb, 30 * LWS_US_PER_SEC); return dts->ts; } @@ -850,6 +867,7 @@ lws_dht_destroy(struct lws_dht_ctx **pctx) lws_dll2_t *d1 = d->next; lws_dht_ts_t *dts = lws_container_of(d, lws_dht_ts_t, list); + lws_sul_cancel(&dts->sul_idle); lws_transport_sequencer_destroy(&dts->ts); lws_dll2_remove(&dts->list); lws_free(dts); @@ -965,7 +983,7 @@ lws_dht_msg_parse(const char *in, size_t len, struct lws_dht_msg *out) if (len > 32) { char dbg[128]; lws_strncpy(dbg, in, sizeof(dbg)); - lwsl_user("lws_dht_msg_parse: len=%zu header='%s'\n", len, dbg); + // lwsl_notice("lws_dht_msg_parse: len=%zu header='%s'\n", len, dbg); } const char *p = in; @@ -1015,7 +1033,7 @@ lws_dht_msg_parse(const char *in, size_t len, struct lws_dht_msg *out) } int -lws_dht_notify_subscribers(struct lws_dht_ctx *ctx, const lws_dht_hash_t *hash, const uint8_t *sha256) +lws_dht_notify_subscribers(struct lws_dht_ctx *ctx, const lws_dht_hash_t *hash, const uint8_t *sha256, const uint8_t *payload, size_t payload_len) { #if defined(LWS_WITH_DHT_BACKEND) struct storage *st; @@ -1035,7 +1053,7 @@ lws_dht_notify_subscribers(struct lws_dht_ctx *ctx, const lws_dht_hash_t *hash, if (ctx->now.tv_sec <= sub->expire) { /* Only notify if the content actually changed from what they have */ if (memcmp(sub->current_sha256, sha256, 32)) { - lws_dht_send_notify(ctx, (struct sockaddr *)&sub->ss, sub->sslen, sub->tid, sub->tid_len, hash, sha256, NULL, 0); + lws_dht_send_notify(ctx, (struct sockaddr *)&sub->ss, sub->sslen, sub->tid, sub->tid_len, hash, sha256, payload, payload_len); /* Queue up reliable delivery retry state */ sub->pending_notify = 1; diff --git a/lib/misc/dht/private-lib-misc-dht.h b/lib/misc/dht/private-lib-misc-dht.h index e0f3ff44ac..bf52a7f53e 100644 --- a/lib/misc/dht/private-lib-misc-dht.h +++ b/lib/misc/dht/private-lib-misc-dht.h @@ -284,6 +284,7 @@ typedef struct lws_dht_ts { struct sockaddr_storage sa; size_t salen; struct lws_dht_ctx *ctx; + lws_sorted_usec_list_t sul_idle; } lws_dht_ts_t; struct lws_dht_mparams { diff --git a/lib/plat/freertos/esp32/drivers/netdev/wifi-esp32.c b/lib/plat/freertos/esp32/drivers/netdev/wifi-esp32.c index 131303335e..89d7cc975d 100644 --- a/lib/plat/freertos/esp32/drivers/netdev/wifi-esp32.c +++ b/lib/plat/freertos/esp32/drivers/netdev/wifi-esp32.c @@ -251,9 +251,11 @@ lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp, */ case WIFI_EVENT_STA_DISCONNECTED: +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"linkdown\"," "\"if\":\"%s\"}", wnd->inst.name); +#endif wnd->state = LWSNDVWIFI_STATE_SCAN; /* * We do it via the sul so we don't get timed scans @@ -264,9 +266,11 @@ lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp, break; case WIFI_EVENT_STA_CONNECTED: +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"linkup\"," "\"if\":\"%s\"}", wnd->inst.name); +#endif break; case WIFI_EVENT_SCAN_DONE: @@ -315,9 +319,11 @@ _event_handler_wifi(void *arg, esp_event_base_t event_base, int32_t event_id, * for other things to consume. */ +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"priv\",\"if\":\"%s\",\"ev\":%d}", wnd->inst.name, (int)event_id); +#endif break; default: return; @@ -377,9 +383,11 @@ _event_handler_ip(void *arg, esp_event_base_t event_base, int32_t event_id, lws_write_numeric_address((void *)&e->ip_info.ip, 4, ip, sizeof(ip)); +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"ipacq\",\"if\":\"%s\"," "\"ipv4\":\"%s\"}", wnd->inst.name, ip); +#endif } } @@ -467,9 +475,11 @@ lws_netdev_wifi_up_plat(struct lws_netdev_instance *nd) esp_wifi_set_ps(WIFI_PS_NONE); wnde32->wnd.flags |= LNDIW_UP; +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"up\",\"if\":\"%s\"}", wnde32->wnd.inst.name); +#endif return 0; } @@ -484,9 +494,11 @@ lws_netdev_wifi_down_plat(struct lws_netdev_instance *nd) if (!(wnde32->wnd.flags & LNDIW_UP)) return 0; +#if defined(LWS_WITH_SYS_SMD) lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK, "{\"type\":\"down\",\"if\":\"%s\"}", wnde32->wnd.inst.name); +#endif esp_wifi_stop(); diff --git a/lib/plat/freertos/freertos-misc.c b/lib/plat/freertos/freertos-misc.c index 07e4620a25..ebb3b61803 100644 --- a/lib/plat/freertos/freertos-misc.c +++ b/lib/plat/freertos/freertos-misc.c @@ -93,6 +93,18 @@ lws_plat_drop_app_privileges(struct lws_context *context, int actually_init) return 0; } +int +lws_plat_user_to_uid(const char *username, uid_t *puid) +{ + return 1; +} + +int +lws_plat_group_to_gid(const char *groupname, gid_t *pgid) +{ + return 1; +} + int lws_plat_recommended_rsa_bits(void) { diff --git a/lib/plat/optee/lws-plat-optee.c b/lib/plat/optee/lws-plat-optee.c index cc46d76f22..45544f979e 100644 --- a/lib/plat/optee/lws-plat-optee.c +++ b/lib/plat/optee/lws-plat-optee.c @@ -137,6 +137,18 @@ lws_plat_drop_app_privileges(struct lws_context *context, int actually_init) return 0; } +int +lws_plat_user_to_uid(const char *username, uid_t *puid) +{ + return 1; +} + +int +lws_plat_group_to_gid(const char *groupname, gid_t *pgid) +{ + return 1; +} + int lws_plat_context_early_init(void) { diff --git a/lib/plat/unix/unix-caps.c b/lib/plat/unix/unix-caps.c index 0897a5e6dc..95e3b93719 100644 --- a/lib/plat/unix/unix-caps.c +++ b/lib/plat/unix/unix-caps.c @@ -49,65 +49,111 @@ _lws_plat_apply_caps(unsigned int mode, const cap_value_t *cv, int count) #endif int -lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid) +lws_plat_user_to_uid(const char *username, uid_t *puid) { - const char *colon = strchr(u_colon_g, ':'); - char u[33]; - struct group *g; struct passwd *p; - size_t ulen; - if (!colon) + if (!username || !username[0]) return 1; - ulen = (size_t)(unsigned int)lws_ptr_diff(colon, u_colon_g); - if (ulen < 2 || ulen > sizeof(u) - 1) - return 1; - - memcpy(u, u_colon_g, ulen); - u[ulen] = '\0'; - - colon++; + /* check if numeric string */ + { + char *endptr = NULL; + long val = strtol(username, &endptr, 10); + if (*endptr == '\0' && val >= 0) { + *puid = (uid_t)val; + return 0; + } + } -#if defined(LWS_HAVE_GETGRNAM_R) +#if defined(LWS_HAVE_GETPWNAM_R) { - struct group gr; + struct passwd pr; char strs[1024]; - if (getgrnam_r(colon, &gr, strs, sizeof(strs), &g) || !g) { + if (getpwnam_r(username, &pr, strs, sizeof(strs), &p) || !p) { #else { - g = getgrnam(colon); - if (!g) { + p = getpwnam(username); + if (!p) { #endif - lwsl_err("%s: unknown group '%s'\n", __func__, colon); + lwsl_err("%s: unknown user '%s'\n", __func__, username); return 1; } - *pgid = g->gr_gid; + *puid = p->pw_uid; } -#if defined(LWS_HAVE_GETPWNAM_R) + return 0; +} + +int +lws_plat_group_to_gid(const char *groupname, gid_t *pgid) +{ + struct group *g; + + if (!groupname || !groupname[0]) + return 1; + + /* check if numeric string */ { - struct passwd pr; + char *endptr = NULL; + long val = strtol(groupname, &endptr, 10); + if (*endptr == '\0' && val >= 0) { + *pgid = (gid_t)val; + return 0; + } + } + +#if defined(LWS_HAVE_GETGRNAM_R) + { + struct group gr; char strs[1024]; - if (getpwnam_r(u, &pr, strs, sizeof(strs), &p) || !p) { + if (getgrnam_r(groupname, &gr, strs, sizeof(strs), &g) || !g) { #else { - p = getpwnam(u); - if (!p) { + g = getgrnam(groupname); + if (!g) { #endif - lwsl_err("%s: unknown user '%s'\n", __func__, u); + lwsl_err("%s: unknown group '%s'\n", __func__, groupname); return 1; } - *puid = p->pw_uid; + *pgid = g->gr_gid; } return 0; } +int +lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid) +{ + const char *colon = strchr(u_colon_g, ':'); + char u[33]; + size_t ulen; + + if (!colon) + return 1; + + ulen = (size_t)(unsigned int)lws_ptr_diff(colon, u_colon_g); + if (ulen < 1 || ulen > sizeof(u) - 1) + return 1; + + memcpy(u, u_colon_g, ulen); + u[ulen] = '\0'; + + colon++; + + if (lws_plat_group_to_gid(colon, pgid)) + return 1; + + if (lws_plat_user_to_uid(u, puid)) + return 1; + + return 0; +} + int lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop) { @@ -117,22 +163,14 @@ lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop) /* if he gave us the groupname, align gid to match it */ if (context->groupname) { -#if defined(LWS_HAVE_GETGRNAM_R) - struct group gr; - char strs[1024]; - - if (!getgrnam_r(context->groupname, &gr, strs, sizeof(strs), &g) && g) { -#else - g = getgrnam(context->groupname); - if (g) { -#endif + gid_t gid; + if (!lws_plat_group_to_gid(context->groupname, &gid)) { lwsl_cx_info(context, "group %s -> gid %u", - context->groupname, g->gr_gid); - context->gid = g->gr_gid; + context->groupname, (unsigned int)gid); + context->gid = gid; } else { lwsl_cx_err(context, "unknown groupname '%s'", context->groupname); - return 1; } } @@ -140,23 +178,14 @@ lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop) /* if he gave us the username, align uid to match it */ if (context->username) { -#if defined(LWS_HAVE_GETPWNAM_R) - struct passwd pr; - char strs[1024]; - - if (!getpwnam_r(context->username, &pr, strs, sizeof(strs), &p) && p) { -#else - p = getpwnam(context->username); - if (p) { -#endif - context->uid = p->pw_uid; - + uid_t uid; + if (!lws_plat_user_to_uid(context->username, &uid)) { + context->uid = uid; lwsl_cx_info(context, "username %s -> uid %u", - context->username, (unsigned int)p->pw_uid); + context->username, (unsigned int)uid); } else { lwsl_cx_err(context, "unknown username %s", context->username); - return 1; } } diff --git a/lib/plat/windows/windows-init.c b/lib/plat/windows/windows-init.c index f5883ad2f2..01581d9c52 100644 --- a/lib/plat/windows/windows-init.c +++ b/lib/plat/windows/windows-init.c @@ -33,6 +33,18 @@ lws_plat_drop_app_privileges(struct lws_context *context, int actually_set) return 0; } +int +lws_plat_user_to_uid(const char *username, uid_t *puid) +{ + return 1; +} + +int +lws_plat_group_to_gid(const char *groupname, gid_t *pgid) +{ + return 1; +} + int lws_plat_context_early_init(void) { diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 597b8a63a0..8e4dfd1f44 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -922,6 +922,7 @@ lws_h2_bind_for_post_before_action(struct lws *wsi) switch (lws_check_basic_auth(wsi, hit->basic_auth_login_file, hit->auth_mask & AUTH_MODE_MASK)) { case LCBA_CONTINUE: + case LCBA_AUTH_RETRY_KEEPALIVE: break; case LCBA_FAILED_AUTH: return lws_unauthorised_basic_auth(wsi); @@ -1268,6 +1269,23 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) * then logically close ourself */ + if (lwsi_role_ws(w) && w->ws->send_check_ping) { + lwsl_info("%s: issuing ping on wsi %s: %s %s h2: %d\n", __func__, + lws_wsi_tag(w), + w->role_ops->name, w->a.protocol->name, + w->mux_substream); + + w->ws->send_check_ping = 0; + n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE], + 8, LWS_WRITE_PING); + if (n < 0) + return -1; + + lws_callback_on_writable(w); + w->mux.requested_POLLOUT = 1; + goto next_child; + } + if ((lwsi_role_ws(w) && w->ws->pong_pending_flag) || (lwsi_state(w) == LRS_RETURNED_CLOSE && w->ws->payload_is_close)) { diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index 172957f4cd..775833999c 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -962,6 +962,43 @@ lws_http_digest_auth(struct lws* wsi) a = wsi->stash->cis[CIS_ADDRESS]; p = &wsi->stash->cis[CIS_PATH][1]; + wsi->client_pipeline = 0; + + /* + * If the server kept the TCP/TLS connection alive on the 401 + * and sent an explicitly empty body, reuse the existing + * session instead of tearing down and recreating it. + * Fall back to the close/reconnect path if any doubt exists. + */ + { + const char *cl401 = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH); + const char *te401 = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING); + const char *conn = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION); + int keep_alive = 1; + + if (conn) { + struct lws_tokenize ts; + lws_tokenize_init(&ts, conn, LWS_TOKENIZE_F_COMMA_SEP_LIST | LWS_TOKENIZE_F_MINUS_NONTERM); + do { + ts.e = (int8_t)lws_tokenize(&ts); + if (ts.e == LWS_TOKZE_TOKEN && + ts.token_len == 5 && + !strncasecmp(ts.token, "close", 5)) { + keep_alive = 0; + break; + } + } while (ts.e > 0); + } + + if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE && + keep_alive && + (!te401 || strncasecmp(te401, "chunked", 7)) && + cl401 && atoi(cl401) == 0) { + wsi->http.digest_auth_hdr = tmp_digest; + return LCBA_AUTH_RETRY_KEEPALIVE; + } + } + /* * This prevents connection pipelining when two * HTTP connection use the same tcp socket. @@ -979,7 +1016,6 @@ lws_http_digest_auth(struct lws* wsi) */ wsi->http.digest_auth_hdr = tmp_digest; - wsi->client_pipeline = 0; } return 0; @@ -1117,7 +1153,47 @@ lws_client_interpret_server_handshake(struct lws *wsi) return LCBA_FAILED_AUTH; } - if (lws_http_digest_auth(wsi)) + enum lws_check_basic_auth_results auth_res = lws_http_digest_auth(wsi); + + if (auth_res == LCBA_AUTH_RETRY_KEEPALIVE) { + /* + * Server kept the TCP/TLS connection alive: + * reuse it for the authenticated retry without + * any close/reconnect. Reset the AH parser and + * re-populate client request headers from stash, + * then schedule a new HTTP handshake send. + */ + static const uint8_t hnames_cis[] = { + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + _WSI_TOKEN_CLIENT_URI, + _WSI_TOKEN_CLIENT_HOST, + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_METHOD, + _WSI_TOKEN_CLIENT_IFACE, + _WSI_TOKEN_CLIENT_ALPN + }; + int m; + + lwsl_wsi_info(wsi, "digest auth: reusing TCP/TLS connection\n"); + + _lws_header_table_reset(wsi->http.ah); + + if (wsi->stash) + for (m = 0; m < (int)LWS_ARRAY_SIZE(hnames_cis); m++) + if (hnames_cis[m] && + wsi->stash->cis[m] && + lws_hdr_simple_create(wsi, hnames_cis[m], wsi->stash->cis[m])) + goto bail3; + + wsi->hdr_parsing_completed = 0; + wsi->http.ah->ues = URIES_IDLE; + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); + lws_callback_on_writable(wsi); + return 0; + } + + if (auth_res) goto bail3; opaque = wsi->a.opaque_user_data; diff --git a/lib/roles/http/private-lib-roles-http.h b/lib/roles/http/private-lib-roles-http.h index 7160c38d8e..277a0895c5 100644 --- a/lib/roles/http/private-lib-roles-http.h +++ b/lib/roles/http/private-lib-roles-http.h @@ -319,6 +319,7 @@ enum lws_check_basic_auth_results { LCBA_CONTINUE, LCBA_FAILED_AUTH, LCBA_END_TRANSACTION, + LCBA_AUTH_RETRY_KEEPALIVE, /* digest auth computed, reuse existing TCP/TLS */ }; enum lws_check_basic_auth_results diff --git a/lib/roles/http/server/interceptor.c b/lib/roles/http/server/interceptor.c index 6bca2120c3..ab7b25a74a 100644 --- a/lib/roles/http/server/interceptor.c +++ b/lib/roles/http/server/interceptor.c @@ -320,9 +320,8 @@ lws_interceptor_check(struct lws *wsi, const struct lws_protocols *prot) } } - lwsl_vhost_notice(vhd->vhost, "%s: valid JWT for %s: exp %lu, now %lu (expires in %lds)", - __func__, sub_claim, ck.expiry_unix_time, lws_now_secs(), - (long)(ck.expiry_unix_time - lws_now_secs())); + lwsl_vhost_notice(vhd->vhost, "valid JWT for %s: expires in %lds", + sub_claim, (long)(ck.expiry_unix_time - lws_now_secs())); if (lws_get_urlarg_by_name(wsi, "lws_interceptor_ok", junk, sizeof(junk))) { lws_interceptor_inject_header(wsi, vhd, sub_claim); diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c index e8b3a90861..0e249632b7 100644 --- a/lib/roles/http/server/lejp-conf.c +++ b/lib/roles/http/server/lejp-conf.c @@ -356,7 +356,7 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason) rej->next = a->info->reject_service_keywords; a->info->reject_service_keywords = rej; rej->name = a->p; - lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf); + // lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf); a->p += n - 1; *(a->p++) = '\0'; rej->value = a->p; diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 82612f60b5..bf74c65226 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -209,6 +209,9 @@ _lws_vhost_init_server_af(struct vh_sock_args *a) limit = cx->count_threads; #endif + if (cx->lws_stub && !LWS_UNIX_SOCK_ENABLED(a->vhost)) + limit = 0; + for (m = 0; m < limit; m++) { if (a->info && a->info->vh_listen_sockfd) @@ -1835,7 +1838,12 @@ lws_http_action(struct lws *wsi) n = (unsigned int)wsi->a.protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION, wsi->user_space, uri_ptr, (unsigned int)uri_len); if (n) { - lwsl_info("LWS_CALLBACK_HTTP closing\n"); +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) + char name[64]; + + lwsl_notice("User code denied HTTP connection: protocol=%s, peer=%s, uri=%s\n", + wsi->a.protocol->name, lws_get_peer_simple(wsi, name, sizeof(name)), uri_ptr); +#endif return 1; } @@ -1953,6 +1961,7 @@ lws_http_action(struct lws *wsi) switch (lws_check_basic_auth(wsi, hit->basic_auth_login_file, hit->auth_mask & AUTH_MODE_MASK)) { case LCBA_CONTINUE: + case LCBA_AUTH_RETRY_KEEPALIVE: break; case LCBA_FAILED_AUTH: return lws_unauthorised_basic_auth(wsi); diff --git a/lib/roles/mqtt/client/client-mqtt.c b/lib/roles/mqtt/client/client-mqtt.c index 5515649b87..433b4cb8e3 100644 --- a/lib/roles/mqtt/client/client-mqtt.c +++ b/lib/roles/mqtt/client/client-mqtt.c @@ -131,6 +131,7 @@ lws_create_client_mqtt_object(const struct lws_client_connect_info *i, lws_free((void *)cp->client_id); c->keep_alive_secs = cp->keep_alive; + c->qos2_state_ops = cp->qos2_state_ops; c->aws_iot = cp->aws_iot; if (cp->will_param.topic && diff --git a/lib/roles/mqtt/mqtt.c b/lib/roles/mqtt/mqtt.c index 2676a8b9a2..9e945cc334 100644 --- a/lib/roles/mqtt/mqtt.c +++ b/lib/roles/mqtt/mqtt.c @@ -929,6 +929,32 @@ _lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par, buf += 2; len -= 2; wsi->mqtt->peer_ack_pkt_id = par->cpkt_id; + wsi->mqtt->qos2_duplicate = 0; + + if (pub->qos == QOS2) { + lws_mqtt_qos2_rx_t *rx; + + lws_start_foreach_dll(struct lws_dll2 *, p, wsi->mqtt->qos2_rx_list.head) { + rx = lws_container_of(p, lws_mqtt_qos2_rx_t, list); + if (rx->packet_id == par->cpkt_id) { + wsi->mqtt->qos2_duplicate = 1; + break; + } + } lws_end_foreach_dll(p); + + if (!wsi->mqtt->qos2_duplicate) { + rx = lws_malloc(sizeof(*rx), "qos2 rx"); + if (rx) { + const char *cid = wsi->mqtt->client.id ? (const char *)wsi->mqtt->client.id->buf : "unknown"; + rx->packet_id = par->cpkt_id; + lws_dll2_add_tail(&rx->list, &wsi->mqtt->qos2_rx_list); + if (wsi->mqtt->client.qos2_state_ops && + wsi->mqtt->client.qos2_state_ops->rx_add) + wsi->mqtt->client.qos2_state_ops->rx_add(wsi, cid, par->cpkt_id); + } + } + } + lwsl_debug("%s: Packet ID %d\n", __func__, (int)par->cpkt_id); par->state = LMQCPP_PAYLOAD; @@ -1471,11 +1497,40 @@ _lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par, break; case LMQCP_PUBREL: - lwsl_err("%s: cmd_completion: PUBREL\n", + { + lws_mqtt_qos2_rx_t *rx; + + lwsl_info("%s: cmd_completion: PUBREL\n", __func__); + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, wsi->mqtt->qos2_rx_list.head) { + rx = lws_container_of(p, lws_mqtt_qos2_rx_t, list); + if (rx->packet_id == par->cpkt_id) { + const char *cid = wsi->mqtt->client.id ? (const char *)wsi->mqtt->client.id->buf : "unknown"; + lws_dll2_remove(&rx->list); + lws_free(rx); + if (wsi->mqtt->client.qos2_state_ops && + wsi->mqtt->client.qos2_state_ops->rx_remove) + wsi->mqtt->client.qos2_state_ops->rx_remove(wsi, cid, par->cpkt_id); + break; + } + } lws_end_foreach_dll_safe(p, tp); + + lws_start_foreach_ll(struct lws *, w, + wsi->mux.child_list) { + uint16_t pid = par->cpkt_id; + if (w->a.protocol->callback(w, + LWS_CALLBACK_MQTT_QOS2_RX_COMPLETE, + w->user_space, + (void *)&pid, 0)) { + return 1; + } + } lws_end_foreach_ll(w, mux.sibling_list); + wsi->mqtt->send_pubcomp = 1; lws_callback_on_writable(wsi); break; + } case LMQCP_PUBACK: lwsl_info("%s: cmd_completion: PUBACK\n", @@ -1671,7 +1726,8 @@ _lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par, lws_start_foreach_ll(struct lws *, w, wsi->mux.child_list) { - if (lws_mqtt_find_sub(w->mqtt, + if (!wsi->mqtt->qos2_duplicate && + lws_mqtt_find_sub(w->mqtt, pub->topic)) if (w->a.protocol->callback( w, (enum lws_callback_reasons)n, @@ -2531,3 +2587,21 @@ lws_wsi_mqtt_adopt(struct lws *parent_wsi, struct lws *wsi) return NULL; } +int +lws_mqtt_client_qos2_rx_add(struct lws *wsi, uint16_t pkt_id) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + lws_mqtt_qos2_rx_t *rx; + + if (!nwsi || !nwsi->mqtt) + return 1; + + rx = lws_malloc(sizeof(*rx), "qos2 rx"); + if (!rx) + return 1; + + rx->packet_id = pkt_id; + lws_dll2_add_tail(&rx->list, &nwsi->mqtt->qos2_rx_list); + + return 0; +} diff --git a/lib/roles/mqtt/ops-mqtt.c b/lib/roles/mqtt/ops-mqtt.c index be9a3de7c9..4159b95913 100644 --- a/lib/roles/mqtt/ops-mqtt.c +++ b/lib/roles/mqtt/ops-mqtt.c @@ -485,6 +485,17 @@ rops_close_role_mqtt(struct lws_context_per_thread *pt, struct lws *wsi) s = s1; } + /* clean up QoS2 rx list */ + { + lws_mqtt_qos2_rx_t *rx; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, wsi->mqtt->qos2_rx_list.head) { + rx = lws_container_of(p, lws_mqtt_qos2_rx_t, list); + lws_dll2_remove(&rx->list); + lws_free(rx); + } lws_end_foreach_dll_safe(p, tp); + } + lws_mqtt_publish_param_t *pub = (lws_mqtt_publish_param_t *) wsi->mqtt->rx_cpkt_param; diff --git a/lib/roles/mqtt/private-lib-roles-mqtt.h b/lib/roles/mqtt/private-lib-roles-mqtt.h index dc46a93629..d602aa46e4 100644 --- a/lib/roles/mqtt/private-lib-roles-mqtt.h +++ b/lib/roles/mqtt/private-lib-roles-mqtt.h @@ -327,6 +327,11 @@ typedef struct lws_mqtt_subs { char topic[]; } lws_mqtt_subs_t; +typedef struct lws_mqtt_qos2_rx { + struct lws_dll2 list; + uint16_t packet_id; +} lws_mqtt_qos2_rx_t; + typedef struct lws_mqtts { lws_mqtt_parser_t par; lwsgs_mqtt_states_t estate; @@ -348,6 +353,7 @@ typedef struct lws_mqttc { } will; uint16_t keep_alive_secs; uint16_t conn_flags; + const lws_mqtt_qos2_state_ops_t *qos2_state_ops; uint8_t aws_iot; } lws_mqttc_t; @@ -358,6 +364,7 @@ struct _lws_mqtt_related { lws_sorted_usec_list_t sul_unsuback_wait; /* unsuback wait TO */ lws_sorted_usec_list_t sul_qos2_pubrec_wait; /* QoS2 pubrec wait TO */ lws_sorted_usec_list_t sul_shadow_wait; /* Device Shadow wait TO */ + struct lws_dll2_owner qos2_rx_list; struct lws *wsi; /**< so sul can use lws_container_of */ lws_mqtt_subs_t *subs_head; /**< Linked-list of heap-allocated subscription objects */ void *rx_cpkt_param; @@ -382,6 +389,8 @@ struct _lws_mqtt_related { uint8_t unacked_publish:1; uint8_t unacked_pubrel:1; + uint8_t qos2_duplicate:1; + uint8_t done_subscribe:1; uint8_t done_birth:1; uint8_t inside_shadow:1; diff --git a/lib/roles/ws/client-parser-ws.c b/lib/roles/ws/client-parser-ws.c index c667d13974..7297168526 100644 --- a/lib/roles/ws/client-parser-ws.c +++ b/lib/roles/ws/client-parser-ws.c @@ -241,6 +241,10 @@ lws_ws_client_rx_sm(struct lws *wsi, unsigned char c) } wsi->ws->first_fragment = 0; break; + case LWSWSOPC_PING: + case LWSWSOPC_PONG: + wsi->ws->defeat_check_utf8 = 1; + break; case LWSWSOPC_CLOSE: wsi->ws->check_utf8 = 0; wsi->ws->utf8 = 0; diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 3012c9e44a..97e6089981 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -210,6 +210,10 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c) goto ret_asking_close; } break; + case LWSWSOPC_PING: + case LWSWSOPC_PONG: + wsi->ws->defeat_check_utf8 = 1; + break; case LWSWSOPC_CLOSE: wsi->ws->check_utf8 = 0; wsi->ws->utf8 = 0; @@ -1004,9 +1008,6 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi, struct lws_tokens ebuf; char buffered = 0; int n = 0, m, sanity = 100; -#if defined(LWS_WITH_HTTP2) - struct lws *wsi1; -#endif if (!wsi->ws) { lwsl_err("ws role wsi with no ws\n"); @@ -1100,19 +1101,6 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi, if (lws_is_flowcontrolled(wsi)) return LWS_HPI_RET_HANDLED; -#if defined(LWS_WITH_HTTP2) - if (wsi->mux_substream || wsi->upgraded_to_http2) { - wsi1 = lws_get_network_wsi(wsi); - if (wsi1 && lws_has_buffered_out(wsi1)) - /* We cannot deal with any kind of new RX - * because we are dealing with a partial send - * (new RX may trigger new http_action() that - * expect to be able to send) - */ - return LWS_HPI_RET_HANDLED; - } -#endif - #if !defined(LWS_WITHOUT_EXTENSIONS) /* 2: RX Extension needs to be drained */ @@ -1431,9 +1419,10 @@ rops_handle_POLLOUT_ws(struct lws *wsi) lws_wsi_tag(wsi), wsi->role_ops->name, wsi->a.protocol->name, wsi->mux_substream); + wsi->ws->send_check_ping = 0; n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE], - 0, LWS_WRITE_PING); + 8, LWS_WRITE_PING); if (n < 0) return LWS_HP_RET_BAIL_DIE; @@ -2165,22 +2154,6 @@ rops_issue_keepalive_ws(struct lws *wsi, int isvalid) { uint64_t us; -#if defined(LWS_WITH_HTTP2) - if (lwsi_role_h2_ENCAPSULATION(wsi)) { - /* we know then that it has an h2 parent */ - struct lws *enc = lws_rops_func_fidx(&role_ops_h2, - LWS_ROPS_encapsulation_parent). - encapsulation_parent(wsi); - - assert(enc); - lwsl_wsi_info(wsi, "trying to do keepalive on h2 wrapper around ws"); - if (lws_rops_func_fidx(enc->role_ops, LWS_ROPS_issue_keepalive). - issue_keepalive(enc, isvalid)) { - lwsl_wsi_err(wsi, "FAILED to keep h2 wrapper for ws alive"); - return 1; - } - } -#endif if (isvalid) { lwsl_wsi_info(wsi, "confirming validity"); diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c index ca0a39fb22..6cc9d55c34 100644 --- a/lib/roles/ws/server-ws.c +++ b/lib/roles/ws/server-ws.c @@ -290,6 +290,7 @@ lws_process_ws_upgrade2(struct lws *wsi) switch (lws_check_basic_auth(wsi, ws_prot_basic_auth, LWSAUTHM_DEFAULT /* no callback based auth here */)) { case LCBA_CONTINUE: + case LCBA_AUTH_RETRY_KEEPALIVE: break; case LCBA_FAILED_AUTH: return lws_unauthorised_basic_auth(wsi); @@ -370,7 +371,11 @@ lws_process_ws_upgrade2(struct lws *wsi) LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, wsi->user_space, lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { - lwsl_warn("User code denied connection\n"); +#if (_LWS_ENABLED_LOGS & LLL_WARN) + char name[64]; + lwsl_warn("User code denied connection: protocol=%s, peer=%s\n", + wsi->a.protocol->name, lws_get_peer_simple(wsi, name, sizeof(name))); +#endif return 1; } diff --git a/lib/secure-streams/README.md b/lib/secure-streams/README.md index 5d707aed61..1f1fea6cf3 100644 --- a/lib/secure-streams/README.md +++ b/lib/secure-streams/README.md @@ -133,12 +133,12 @@ JSON. } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAw...AnX5iItreGCc=" + "isrg_root_x2": "MIIFazCCA1OgAw...AnX5iItreGCc=" }, { - "LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIB...WEsikxqEt" + "LEX3_isrg_root_x2": "MIIFjTCCA3WgAwIB...WEsikxqEt" }], "trust_stores": [{ - "le_via_isrg": ["isrg_root_x1", "LEX3_isrg_root_x1"] + "le_via_isrg": ["isrg_root_x2", "LEX3_isrg_root_x2"] }], "s": [{ "mintest": { diff --git a/lib/system/auth-dns/auth-dns.c b/lib/system/auth-dns/auth-dns.c index 9b91014ad7..b755be61fd 100644 --- a/lib/system/auth-dns/auth-dns.c +++ b/lib/system/auth-dns/auth-dns.c @@ -92,10 +92,12 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z int in_parens = 0; int in_comment = 0; - char line_accum[4096]; + char line_accum[16384]; size_t lptr = 0; int loop_cycles = 0; + lwsl_notice("%s: Parsing zone buffer of length %zu\n", __func__, len); + while (p <= end) { if (++loop_cycles > 5000000) { lwsl_err("auth-dns: parsing exceeded maximum length\n"); @@ -120,15 +122,16 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z if (lptr > 0) { lws_tokenize_t ts; lws_tokenize_elem e; - char toks[8][256]; + lws_tokenize_init(&ts, line_accum, LWS_TOKENIZE_F_HASH_COMMENT | LWS_TOKENIZE_F_DOT_NONTERM | LWS_TOKENIZE_F_NO_FLOATS | LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_SLASH_NONTERM | LWS_TOKENIZE_F_COLON_NONTERM | LWS_TOKENIZE_F_EQUALS_NONTERM | LWS_TOKENIZE_F_PLUS_NONTERM); + ts.len = lptr; + + char toks[32][256]; + const char *tok_ptr[32]; int num_toks = 0, name_inherited = 0, n; if (line_accum[0] == ' ' || line_accum[0] == '\t') name_inherited = 1; - lws_tokenize_init(&ts, line_accum, LWS_TOKENIZE_F_HASH_COMMENT | LWS_TOKENIZE_F_DOT_NONTERM | LWS_TOKENIZE_F_NO_FLOATS | LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_SLASH_NONTERM | LWS_TOKENIZE_F_COLON_NONTERM | LWS_TOKENIZE_F_EQUALS_NONTERM | LWS_TOKENIZE_F_PLUS_NONTERM); - ts.len = lptr; - int max_tokens = 0; do { e = lws_tokenize(&ts); @@ -136,15 +139,17 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z break; if (e == LWS_TOKZE_TOKEN || e == LWS_TOKZE_QUOTED_STRING || e == LWS_TOKZE_INTEGER) { - if (num_toks < 8) { + if (num_toks < 32) { n = (int)ts.token_len; if (n > (int)sizeof(toks[0]) - 1) n = sizeof(toks[0]) - 1; memcpy(toks[num_toks], ts.token, (size_t)n); toks[num_toks][n] = '\0'; + tok_ptr[num_toks] = ts.start; + num_toks++; } - num_toks++; } + } while (e > 0); if (!strncmp(line_accum, "$ORIGIN", 7)) { @@ -216,29 +221,33 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z } } - if (type_idx < num_toks && !strcmp(toks[type_idx], "IN")) { + if (type_idx < num_toks && !strcasecmp(toks[type_idx], "IN")) { class_ = 1; type_idx++; } if (type_idx < num_toks) { + // lwsl_notice(" Attempting type match at type_idx=%d: '%s'\n", type_idx, toks[type_idx]); /* simplistic type assignment */ - if (!strcmp(toks[type_idx], "A")) type = 1; - else if (!strcmp(toks[type_idx], "NS")) type = 2; - else if (!strcmp(toks[type_idx], "SOA")) type = 6; - else if (!strcmp(toks[type_idx], "MX")) type = 15; - else if (!strcmp(toks[type_idx], "TXT")) type = 16; - else if (!strcmp(toks[type_idx], "AAAA")) type = 28; - else if (!strcmp(toks[type_idx], "RRSIG")) type = 46; - else if (!strcmp(toks[type_idx], "DNSKEY")) type = 48; - else if (!strcmp(toks[type_idx], "NSEC3")) type = 50; - else if (!strcmp(toks[type_idx], "NSEC3PARAM")) type = 51; - else if (!strcmp(toks[type_idx], "TLSA")) type = 52; - else if (!strcmp(toks[type_idx], "CAA")) type = 257; + if (!strcasecmp(toks[type_idx], "A")) type = 1; + else if (!strcasecmp(toks[type_idx], "NS")) type = 2; + else if (!strcasecmp(toks[type_idx], "SOA")) type = 6; + else if (!strcasecmp(toks[type_idx], "CNAME")) type = 5; + else if (!strcasecmp(toks[type_idx], "MX")) type = 15; + else if (!strcasecmp(toks[type_idx], "TXT")) type = 16; + else if (!strcasecmp(toks[type_idx], "AAAA")) type = 28; + else if (!strcasecmp(toks[type_idx], "RRSIG")) type = 46; + else if (!strcasecmp(toks[type_idx], "DNSKEY")) type = 48; + else if (!strcasecmp(toks[type_idx], "NSEC3")) type = 50; + else if (!strcasecmp(toks[type_idx], "NSEC3PARAM")) type = 51; + else if (!strcasecmp(toks[type_idx], "TLSA")) type = 52; + else if (!strcasecmp(toks[type_idx], "CAA")) type = 257; else type = 0; /* unknown */ type_idx++; } + // lwsl_notice("%s: Record: name=%s, type=%u, class=%u, tokens=%d, final_type_idx=%d\n", __func__, cur_name, type, class_, num_toks, type_idx); + /* find existing rrset */ lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&zone->rrset_list)) { struct auth_dns_rrset *rs = lws_container_of(d, struct auth_dns_rrset, list); @@ -265,22 +274,26 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z /* The remainder of the line is rdata. */ { - char *rd = strstr(line_accum, toks[type_idx - 1]); + const char *rd = tok_ptr[type_idx - 1]; if (rd) { - rd += strlen(toks[type_idx - 1]); while (*rd == ' ' || *rd == '\t') rd++; /* Strip surrounding quotes if present (TXT records) */ size_t rdlen = strlen(rd); if (rdlen >= 2 && rd[0] == '"' && rd[rdlen - 1] == '"') { - rd[rdlen - 1] = '\0'; - rdlen -= 2; - rd++; + char *qrd = lws_strdup(rd); + if (qrd) { + rdlen = strlen(qrd); + qrd[rdlen - 1] = '\0'; + rr->rdata = lws_strdup(qrd + 1); + rr->rdata_len = rdlen - 2; + lws_free(qrd); + } + } else { + rr->rdata = lws_strdup(rd); + if (rr->rdata) + rr->rdata_len = rdlen; } - - rr->rdata = lws_strdup(rd); - if (rr->rdata) - rr->rdata_len = rdlen; } } @@ -295,7 +308,7 @@ lws_auth_dns_parse_zone_buf(const char *buf, size_t len, struct auth_dns_zone *z lptr = 0; } else { - if (lptr < sizeof(line_accum) - 1) + if (!in_comment && *p != '(' && *p != ')' && *p != '\n' && *p != '\r' && lptr < sizeof(line_accum) - 1) line_accum[lptr++] = *p; } p++; @@ -446,6 +459,7 @@ lws_auth_dns_sign_zone(struct lws_auth_dns_sign_info *info) switch (rs->type) { case 1: ts = "A"; break; case 2: ts = "NS"; break; + case 5: ts = "CNAME"; break; case 6: ts = "SOA"; break; case 15: ts = "MX"; break; case 16: ts = "TXT"; break; @@ -485,7 +499,7 @@ lws_auth_dns_sign_zone(struct lws_auth_dns_sign_info *info) } ofd = open(info->output_filepath ? info->output_filepath : "signed.zone", LWS_O_RDONLY); - if (ofd < 0 || fstat(ofd, &ost) || ost.st_size <= 0 || ost.st_size >= 10000) { + if (ofd < 0 || fstat(ofd, &ost) || ost.st_size <= 0 || ost.st_size >= 1048576) { lwsl_err("Failed file open/stat ofd=%d st_size=%ld\n", ofd, (long)ost.st_size); goto bail_jwk; } diff --git a/lib/system/auth-dns/sign.c b/lib/system/auth-dns/sign.c index 7c7e87ff2b..b98d0d314a 100644 --- a/lib/system/auth-dns/sign.c +++ b/lib/system/auth-dns/sign.c @@ -83,6 +83,33 @@ name_to_wire(const char *name, const char *origin, uint8_t *wire, size_t *wire_l return 0; } +static int +lws_auth_dns_b32hex_decode(const char *in, uint8_t *out, size_t out_max) +{ + uint32_t bitb = 0; + int bits = 0; + size_t olen = 0; + + while (*in) { + int val = -1; + if (*in >= '0' && *in <= '9') val = *in - '0'; + else if (*in >= 'A' && *in <= 'V') val = *in - 'A' + 10; + else if (*in >= 'a' && *in <= 'v') val = *in - 'a' + 10; + + if (val >= 0) { + bitb = (bitb << 5) | (uint32_t)val; + bits += 5; + if (bits >= 8) { + if (olen >= out_max) return -1; + out[olen++] = (uint8_t)(bitb >> (bits - 8)); + bits -= 8; + } + } + in++; + } + return (int)olen; +} + int lws_auth_dns_rdata_to_wire(struct auth_dns_zone *z, struct auth_dns_rr *rr, uint16_t type, const char *ipv4, const char *ipv6) { @@ -152,6 +179,10 @@ lws_auth_dns_rdata_to_wire(struct auth_dns_zone *z, struct auth_dns_rr *rr, uint size_t av = 512; if (name_to_wire(toks[0], z->origin, w, &av)) goto fail; wl = av; + } else if (type == 5 && num_toks >= 1) { // CNAME + size_t av = 512; + if (name_to_wire(toks[0], z->origin, w, &av)) goto fail; + wl = av; } else if (type == 15 && num_toks >= 2) { // MX uint16_t p = (uint16_t)atoi(toks[0]); w[0] = (uint8_t)(p >> 8); w[1] = (uint8_t)(p & 0xff); @@ -183,32 +214,100 @@ lws_auth_dns_rdata_to_wire(struct auth_dns_zone *z, struct auth_dns_rr *rr, uint memcpy(w + wl, toks[i], (size_t)n); wl += (size_t)n; } - } else if (type == 50 && num_toks >= 1) { // NSEC3 - /* [NSEC3 base32hex] is just stored as raw ascii text for the test right now, but - * a real NSEC3 has Hash Alg, Flags, Iterations, Salt, Next Hashed Owner Name, Type Bitmaps. - * To proceed with the test, we'll pack the base32hex string length and string since we are - * mocking the crypto loop. - */ - n = (int)strlen(toks[1]); - w[wl++] = (uint8_t)n; - memcpy(w + wl, toks[1], (size_t)n); - wl += (size_t)n; + } else if (type == 50 && num_toks >= 5) { // NSEC3 + int tidx = 0; + if (!strcasecmp(toks[tidx], "NSEC3")) + tidx++; + if (num_toks - tidx < 5) goto fail; + + /* Format: HashAlg Flags Iterations Salt NextB32 Type1 Type2 ... */ + w[wl++] = (uint8_t)atoi(toks[tidx + 0]); /* Hash Alg */ + w[wl++] = (uint8_t)atoi(toks[tidx + 1]); /* Flags */ + uint16_t iters = (uint16_t)atoi(toks[tidx + 2]); /* Iterations */ + w[wl++] = (uint8_t)(iters >> 8); + w[wl++] = (uint8_t)(iters & 0xff); + + /* Salt Length & Salt */ + if (!strcmp(toks[tidx + 3], "-")) { + w[wl++] = 0; + } else { + size_t slen = strlen(toks[tidx + 3]) / 2; + w[wl++] = (uint8_t)slen; + lws_hex_to_byte_array(toks[tidx + 3], w + wl, (int)slen); + wl += slen; + } + + /* Hash Length & Next Hashed Owner Name */ + /* The NextHashedOwnerName is base32hex encoded without padding. */ + /* We need to decode it back into binary. */ + uint8_t nxt_hash[64]; + int nxt_len = lws_auth_dns_b32hex_decode(toks[tidx + 4], nxt_hash, sizeof(nxt_hash)); + if (nxt_len < 0) goto fail; + w[wl++] = (uint8_t)nxt_len; + memcpy(w + wl, nxt_hash, (size_t)nxt_len); + wl += (size_t)nxt_len; + + /* Type Bit Maps */ + /* Build the type bit maps from the remaining tokens */ + uint8_t window_blocks[256][32]; /* 256 windows, each 32 bytes max */ + uint8_t window_max[256]; + memset(window_blocks, 0, sizeof(window_blocks)); + memset(window_max, 0, sizeof(window_max)); + + for (int i = tidx + 5; i < num_toks; i++) { + uint16_t ty = 0; + if (!strcmp(toks[i], "A")) ty = 1; + else if (!strcmp(toks[i], "NS")) ty = 2; + else if (!strcmp(toks[i], "CNAME")) ty = 5; + else if (!strcmp(toks[i], "SOA")) ty = 6; + else if (!strcmp(toks[i], "MX")) ty = 15; + else if (!strcmp(toks[i], "TXT")) ty = 16; + else if (!strcmp(toks[i], "AAAA")) ty = 28; + else if (!strcmp(toks[i], "RRSIG")) ty = 46; + else if (!strcmp(toks[i], "DNSKEY")) ty = 48; + else if (!strcmp(toks[i], "NSEC3")) ty = 50; + else if (!strcmp(toks[i], "NSEC3PARAM")) ty = 51; + else if (!strcmp(toks[i], "CAA")) ty = 257; + else ty = (uint16_t)atoi(toks[i]); + + if (ty > 0) { + int win = ty / 256; + int byte_idx = (ty % 256) / 8; + int bit_idx = 7 - (ty % 8); + window_blocks[win][byte_idx] |= (1 << bit_idx); + if (byte_idx >= window_max[win]) window_max[win] = (uint8_t)(byte_idx + 1); + } + } + + for (int win = 0; win < 256; win++) { + if (window_max[win] > 0) { + w[wl++] = (uint8_t)win; /* Window Block number */ + w[wl++] = window_max[win]; /* Bitmap Length */ + memcpy(w + wl, window_blocks[win], window_max[win]); + wl += window_max[win]; + } + } } else if (type == 51 && num_toks >= 4) { // NSEC3PARAM + int tidx = 0; + if (!strcasecmp(toks[tidx], "NSEC3PARAM")) + tidx++; + if (num_toks - tidx < 4) goto fail; + /* Hash Algorithm */ - w[wl++] = (uint8_t)atoi(toks[0]); + w[wl++] = (uint8_t)atoi(toks[tidx + 0]); /* Flags */ - w[wl++] = (uint8_t)atoi(toks[1]); + w[wl++] = (uint8_t)atoi(toks[tidx + 1]); /* Iterations */ - uint16_t iters = (uint16_t)atoi(toks[2]); + uint16_t iters = (uint16_t)atoi(toks[tidx + 2]); w[wl++] = (uint8_t)(iters >> 8); w[wl++] = (uint8_t)(iters & 0xff); /* Salt Length & Salt */ - if (!strcmp(toks[3], "-")) { + if (!strcmp(toks[tidx + 3], "-")) { w[wl++] = 0; } else { - size_t slen = strlen(toks[3]) / 2; + size_t slen = strlen(toks[tidx + 3]) / 2; w[wl++] = (uint8_t)slen; - lws_hex_to_byte_array(toks[3], w + wl, (int)slen); + lws_hex_to_byte_array(toks[tidx + 3], w + wl, (int)slen); wl += slen; } } else if (type == 48 && num_toks >= 4) { // DNSKEY @@ -587,24 +686,68 @@ lws_auth_dns_add_dnskey(struct auth_dns_zone *z, const char *jwk_path, int flags return ret; } +struct nsec3_node { + char *name; + uint8_t hash[20]; + char b32[64]; + char type_list[512]; +}; + +static int +cmp_nsec3_node(const void *a, const void *b) +{ + const struct nsec3_node *na = *(const struct nsec3_node **)a; + const struct nsec3_node *nb = *(const struct nsec3_node **)b; + return strcmp(na->b32, nb->b32); +} + static int lws_auth_dns_add_nsec3(struct auth_dns_zone *z, const char *salt_hex, int iterations) { - /* Need a list of unique canonical names to hash */ - char *names[1024]; /* Rough upper bound for test */ + struct nsec3_node *nodes[1024]; /* Rough upper bound for test */ int num_names = 0; lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&z->rrset_list)) { struct auth_dns_rrset *rs = lws_container_of(d, struct auth_dns_rrset, list); - int found = 0; + int found = -1; for (int i = 0; i < num_names; i++) { - if (!strcmp(names[i], rs->name)) { - found = 1; + if (!strcmp(nodes[i]->name, rs->name)) { + found = i; break; } } - if (!found && num_names < 1024) - names[num_names++] = lws_strdup(rs->name); + if (found < 0 && num_names < 1024) { + nodes[num_names] = lws_zalloc(sizeof(struct nsec3_node), "nsec3_node"); + if (!nodes[num_names]) continue; + nodes[num_names]->name = lws_strdup(rs->name); + found = num_names++; + } + + if (found >= 0) { + const char *ts = "UNKNOWN"; + switch (rs->type) { + case 1: ts = "A"; break; + case 2: ts = "NS"; break; + case 5: ts = "CNAME"; break; + case 6: ts = "SOA"; break; + case 15: ts = "MX"; break; + case 16: ts = "TXT"; break; + case 28: ts = "AAAA"; break; + case 46: ts = "RRSIG"; break; + case 48: ts = "DNSKEY"; break; + case 50: ts = "NSEC3"; break; + case 51: ts = "NSEC3PARAM"; break; + case 257: ts = "CAA"; break; + } + /* Append type if not already there */ + if (!strstr(nodes[found]->type_list, ts)) { + size_t len = strlen(nodes[found]->type_list); + if (len < sizeof(nodes[found]->type_list) - 32) { + if (len > 0) { nodes[found]->type_list[len++] = ' '; nodes[found]->type_list[len] = '\0'; } + strcat(nodes[found]->type_list, ts); + } + } + } } lws_end_foreach_dll(d); /* parse salt */ @@ -618,19 +761,20 @@ lws_auth_dns_add_nsec3(struct auth_dns_zone *z, const char *salt_hex, int iterat for (int i = 0; i < num_names; i++) { uint8_t wire[256]; size_t wl = sizeof(wire); - if (name_to_wire(names[i], z->origin, wire, &wl)) + if (name_to_wire(nodes[i]->name, z->origin, wire, &wl)) { + nodes[i]->b32[0] = '\0'; continue; + } - uint8_t hash[32]; struct lws_genhash_ctx hctx; /* first iteration: hash(salt + wire) */ if (lws_genhash_init(&hctx, LWS_GENHASH_TYPE_SHA1) || (salt_len && lws_genhash_update(&hctx, salt, salt_len)) || lws_genhash_update(&hctx, wire, wl) || - lws_genhash_destroy(&hctx, hash)) { + lws_genhash_destroy(&hctx, nodes[i]->hash)) { lws_genhash_destroy(&hctx, NULL); - lws_free(names[i]); + nodes[i]->b32[0] = '\0'; continue; } @@ -638,18 +782,36 @@ lws_auth_dns_add_nsec3(struct auth_dns_zone *z, const char *salt_hex, int iterat for (int j = 0; j < iterations; j++) { if (lws_genhash_init(&hctx, LWS_GENHASH_TYPE_SHA1) || (salt_len && lws_genhash_update(&hctx, salt, salt_len)) || - lws_genhash_update(&hctx, hash, 20) || /* SHA1 is 20 bytes */ - lws_genhash_destroy(&hctx, hash)) { + lws_genhash_update(&hctx, nodes[i]->hash, 20) || /* SHA1 is 20 bytes */ + lws_genhash_destroy(&hctx, nodes[i]->hash)) { lws_genhash_destroy(&hctx, NULL); break; } } - char b32[128]; - lws_auth_dns_b32hex_encode(hash, 20, b32); + lws_auth_dns_b32hex_encode(nodes[i]->hash, 20, nodes[i]->b32); + + /* Always add RRSIG to type list, as all records will be signed */ + if (!strstr(nodes[i]->type_list, "RRSIG")) { + size_t len = strlen(nodes[i]->type_list); + if (len > 0) { nodes[i]->type_list[len++] = ' '; nodes[i]->type_list[len] = '\0'; } + strcat(nodes[i]->type_list, "RRSIG"); + } + } + + /* Sort nodes by b32 hash */ + qsort(nodes, (size_t)num_names, sizeof(void *), cmp_nsec3_node); + + for (int i = 0; i < num_names; i++) { + if (!nodes[i]->b32[0]) continue; + + int next_idx = (i + 1) % num_names; + while (!nodes[next_idx]->b32[0] && next_idx != i) { + next_idx = (next_idx + 1) % num_names; + } char fqdn[256]; - lws_snprintf(fqdn, sizeof(fqdn), "%s.%s", b32, z->origin); + lws_snprintf(fqdn, sizeof(fqdn), "%s.%s", nodes[i]->b32, z->origin); /* Create NSEC3 rrset manually since it needs to be hashed as owner name */ struct auth_dns_rrset *rrset = lws_zalloc(sizeof(*rrset), "nsec3"); @@ -662,15 +824,17 @@ lws_auth_dns_add_nsec3(struct auth_dns_zone *z, const char *salt_hex, int iterat struct auth_dns_rr *rr = lws_zalloc(sizeof(*rr), "rr"); if (rr) { - char tb[256]; - lws_snprintf(tb, sizeof(tb), "[NSEC3 %s]", b32); + char tb[1024]; + /* Format: HashAlg Flags Iterations Salt NextB32 Type1 Type2 ... */ + lws_snprintf(tb, sizeof(tb), "1 0 %d %s %s %s", iterations, salt_hex ? salt_hex : "-", nodes[next_idx]->b32, nodes[i]->type_list); rr->rdata = lws_strdup(tb); rr->rdata_len = strlen(rr->rdata); lws_auth_dns_rdata_to_wire(z, rr, rrset->type, NULL, NULL); lws_dll2_add_tail(&rr->list, &rrset->rr_list); } } - lws_free(names[i]); + lws_free(nodes[i]->name); + lws_free(nodes[i]); } /* Insert NSEC3PARAM at apex */ diff --git a/lib/system/system.c b/lib/system/system.c index 07914c2aed..13bcd898a6 100644 --- a/lib/system/system.c +++ b/lib/system/system.c @@ -280,8 +280,12 @@ lws_extip_report(struct lws_context *cx, lws_extip_src_t src, #endif ) { memset(target, 0, sizeof(*target)); + lwsl_notice("EXTIP_DEBUG: lws_extip_report called with status %d (OFFLINE)\n", status); } else { *target = *sa46; + char buf_dbg[64]; + lws_sa46_write_numeric_address((lws_sockaddr46 *)sa46, buf_dbg, sizeof(buf_dbg)); + lwsl_notice("EXTIP_DEBUG: lws_extip_report called with IP %s\n", buf_dbg); } if (memcmp(&old, target, sizeof(*target))) { @@ -313,7 +317,12 @@ lws_extip_report(struct lws_context *cx, lws_extip_src_t src, p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "]}"); +#if defined(LWS_WITH_SYS_SMD) + lwsl_notice("EXTIP_DEBUG: lws_extip_report emitting SMD: %s\n", payload); lws_smd_msg_printf(cx, LWSSMDCL_NETWORK, "%s", payload); +#endif + } else { + lwsl_notice("EXTIP_DEBUG: lws_extip_report skipped SMD emit (no change)\n"); } } diff --git a/lib/tls/bearssl/bearssl-ssl.c b/lib/tls/bearssl/bearssl-ssl.c index 71f6875885..b181d213ca 100644 --- a/lib/tls/bearssl/bearssl-ssl.c +++ b/lib/tls/bearssl/bearssl-ssl.c @@ -312,7 +312,7 @@ tops_fake_POLLIN_for_buffered_bearssl(struct lws_context_per_thread *pt) } const struct lws_tls_ops tls_ops_bearssl = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_bearssl, + .fake_POLLIN_for_buffered = tops_fake_POLLIN_for_buffered_bearssl, }; int lws_context_init_ssl_library(struct lws_context *cx, diff --git a/lib/tls/bearssl/bearssl-x509.c b/lib/tls/bearssl/bearssl-x509.c index 697a2534d8..fbc863ca4f 100644 --- a/lib/tls/bearssl/bearssl-x509.c +++ b/lib/tls/bearssl/bearssl-x509.c @@ -743,3 +743,23 @@ int lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, union l if (!conn || !conn->peer_cert) return -1; return lws_x509_info(conn->peer_cert, type, buf, len); } + +int +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info) +{ + lwsl_err("%s: not supported on bearssl\n", __func__); + return 1; +} + +int +lws_x509_create_self_signed(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const char *san, int key_bits) +{ + lwsl_err("%s: not supported on bearssl\n", __func__); + return 1; +} diff --git a/lib/tls/gnutls/gnutls-ssl.c b/lib/tls/gnutls/gnutls-ssl.c index ab7f2f7250..dbda8f5e4a 100644 --- a/lib/tls/gnutls/gnutls-ssl.c +++ b/lib/tls/gnutls/gnutls-ssl.c @@ -294,5 +294,5 @@ tops_fake_POLLIN_for_buffered_gnutls(struct lws_context_per_thread *pt) } const struct lws_tls_ops tls_ops_gnutls = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_gnutls, + .fake_POLLIN_for_buffered = tops_fake_POLLIN_for_buffered_gnutls, }; diff --git a/lib/tls/gnutls/gnutls-x509.c b/lib/tls/gnutls/gnutls-x509.c index 576553223e..1507abc53d 100644 --- a/lib/tls/gnutls/gnutls-x509.c +++ b/lib/tls/gnutls/gnutls-x509.c @@ -28,19 +28,21 @@ #include int -lws_x509_create_self_signed(struct lws_context *context, - uint8_t **cert_buf, size_t *cert_len, - uint8_t **key_buf, size_t *key_len, - const char *san, int key_bits) +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info) { - gnutls_x509_privkey_t key; - gnutls_x509_crt_t crt; + gnutls_x509_privkey_t key = NULL, issuer_key = NULL; + gnutls_x509_crt_t crt = NULL, issuer_crt = NULL; int ret = 1; gnutls_datum_t data; - const char *cn = san ? san : "localhost"; (void)context; + if (!info || !info->san) + return 1; + if (gnutls_x509_privkey_init(&key)) return 1; if (gnutls_x509_crt_init(&crt)) { @@ -48,8 +50,23 @@ lws_x509_create_self_signed(struct lws_context *context, return 1; } - if (gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, (unsigned int)key_bits, 0)) - goto bail; + if (info->curve_name) { + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + if (!strcmp(info->curve_name, "P-521")) curve = GNUTLS_ECC_CURVE_SECP521R1; + else if (!strcmp(info->curve_name, "P-384")) curve = GNUTLS_ECC_CURVE_SECP384R1; + else if (!strcmp(info->curve_name, "P-256")) curve = GNUTLS_ECC_CURVE_SECP256R1; + + if (curve == GNUTLS_ECC_CURVE_INVALID) { + lwsl_err("%s: unknown curve %s\n", __func__, info->curve_name); + goto bail; + } + + if (gnutls_x509_privkey_generate(key, GNUTLS_PK_ECC, curve, 0)) + goto bail; + } else { + if (gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, (unsigned int)(info->key_bits ? info->key_bits : 2048), 0)) + goto bail; + } gnutls_x509_crt_set_key(crt, key); gnutls_x509_crt_set_version(crt, 3); @@ -57,18 +74,40 @@ lws_x509_create_self_signed(struct lws_context *context, gnutls_x509_crt_set_activation_time(crt, time(NULL)); gnutls_x509_crt_set_expiration_time(crt, time(NULL) + (365 * 24 * 3600)); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, cn, (unsigned int)strlen(cn)); - gnutls_x509_crt_set_issuer_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, cn, (unsigned int)strlen(cn)); + gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, info->san, (unsigned int)strlen(info->san)); + + if (info->ca_cert_pem && info->ca_key_pem) { + gnutls_datum_t d_cert = { (unsigned char *)info->ca_cert_pem, (unsigned int)strlen(info->ca_cert_pem) }; + gnutls_datum_t d_key = { (unsigned char *)info->ca_key_pem, (unsigned int)strlen(info->ca_key_pem) }; + + if (gnutls_x509_crt_init(&issuer_crt) < 0) goto bail; + if (gnutls_x509_crt_import(issuer_crt, &d_cert, GNUTLS_X509_FMT_PEM) < 0) goto bail; + + if (gnutls_x509_privkey_init(&issuer_key) < 0) goto bail; + if (gnutls_x509_privkey_import(issuer_key, &d_key, GNUTLS_X509_FMT_PEM) < 0) goto bail; + + } else { + issuer_crt = crt; + issuer_key = key; + } /* Extensions */ - gnutls_x509_crt_set_basic_constraints(crt, 0, -1); - gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); + gnutls_x509_crt_set_basic_constraints(crt, info->is_ca ? 1 : 0, -1); + + if (info->is_ca) { + gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN); + } else { + gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT); + if (info->is_server) + gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0); + gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_CLIENT, 0); + } - if (san) - gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, san, (unsigned int)strlen(san), 0); + if (info->san && info->is_server) + gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, info->san, (unsigned int)strlen(info->san), 0); - /* Self-sign */ - if (gnutls_x509_crt_sign2(crt, crt, key, GNUTLS_DIG_SHA256, 0)) + /* Sign */ + if (gnutls_x509_crt_sign2(crt, issuer_crt, issuer_key, GNUTLS_DIG_SHA256, 0)) goto bail; /* Export Cert */ @@ -102,12 +141,30 @@ lws_x509_create_self_signed(struct lws_context *context, ret = 0; bail: - gnutls_x509_crt_deinit(crt); - gnutls_x509_privkey_deinit(key); + if (issuer_crt && issuer_crt != crt) gnutls_x509_crt_deinit(issuer_crt); + if (issuer_key && issuer_key != key) gnutls_x509_privkey_deinit(issuer_key); + if (crt) gnutls_x509_crt_deinit(crt); + if (key) gnutls_x509_privkey_deinit(key); return ret; } +int +lws_x509_create_self_signed(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const char *san, int key_bits) +{ + struct lws_x509_cert_gen_info info; + + memset(&info, 0, sizeof(info)); + info.san = san ? san : "localhost"; + info.key_bits = key_bits; + info.is_server = 1; + + return lws_x509_create_cert(context, cert_buf, cert_len, key_buf, key_len, &info); +} + int lws_x509_create(struct lws_x509_cert **x509) { diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index 573f9ed377..4010b7cec1 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -120,6 +120,9 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, uint8_t *p = NULL; long err; int n; + mbedtls_x509_crt extras = {0}; + mbedtls_x509_crt *leaf = NULL; + mbedtls_x509_crt *tail = NULL; if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) { lwsl_notice("%s: no usable input\n", __func__); @@ -169,6 +172,45 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, return 1; } + /* + * The single-cert load above only installs the leaf. If the PEM source + * contains a chain (leaf + intermediates [+ root]), parse the whole + * file and splice the intermediates onto the leaf's mbedtls_x509_crt + * ->next list so the TLS handshake sends them on the wire. + */ + mbedtls_x509_crt_init(&extras); + + if (cert) + n = mbedtls_x509_crt_parse_file(&extras, cert); + else if (mem_cert && mem_cert_len) + n = mbedtls_x509_crt_parse(&extras, + (const unsigned char *)mem_cert, + (size_t)mem_cert_len); + + if (n == 0 && extras.next) { + leaf = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx); + if (leaf) { + tail = leaf; + while (tail->next) + tail = tail->next; + /* + * Detach the intermediates from `extras` and hang + * them off the leaf. mbedtls_x509_crt_free(&extras) + * will then only free `extras` itself (cert #1, a + * duplicate of the leaf), not the chain we kept. + */ + tail->next = extras.next; + extras.next = NULL; + lwsl_notice("%s: appended chain certs from %s\n", + __func__, cert ? cert : "(mem)"); + } + } else if (n < 0) { + lwsl_warn("%s: chain parse n=-0x%x; serving leaf only\n", + __func__, -n); + } + + mbedtls_x509_crt_free(&extras); + if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, (char *)mem_privkey, mem_privkey_len, &p, &flen)) { diff --git a/lib/tls/mbedtls/mbedtls-ssl.c b/lib/tls/mbedtls/mbedtls-ssl.c index baec5fcf03..e16fdd0063 100644 --- a/lib/tls/mbedtls/mbedtls-ssl.c +++ b/lib/tls/mbedtls/mbedtls-ssl.c @@ -357,5 +357,5 @@ tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt) } const struct lws_tls_ops tls_ops_mbedtls = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_mbedtls, + .fake_POLLIN_for_buffered = tops_fake_POLLIN_for_buffered_mbedtls, }; diff --git a/lib/tls/mbedtls/mbedtls-x509.c b/lib/tls/mbedtls/mbedtls-x509.c index 08a7bab1d9..63829a1c69 100644 --- a/lib/tls/mbedtls/mbedtls-x509.c +++ b/lib/tls/mbedtls/mbedtls-x509.c @@ -580,10 +580,10 @@ lws_x509_destroy(struct lws_x509_cert **x509) } int -lws_x509_create_self_signed(struct lws_context *context, - uint8_t **cert_buf, size_t *cert_len, - uint8_t **key_buf, size_t *key_len, - const char *san, int key_bits) +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info) { mbedtls_x509write_cert crt; mbedtls_pk_context key; @@ -591,14 +591,21 @@ lws_x509_create_self_signed(struct lws_context *context, mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ctr_drbg_context *pdrbg = &ctr_drbg; + mbedtls_x509_crt issuer_crt; + mbedtls_pk_context issuer_key; int ret = 1; unsigned char buf[4096]; char name[128]; int len; + if (!info || !info->san) + return 1; + mbedtls_x509write_crt_init(&crt); mbedtls_pk_init(&key); mbedtls_mpi_init(&serial); + mbedtls_x509_crt_init(&issuer_crt); + mbedtls_pk_init(&issuer_key); if (context) { pdrbg = &context->mcdc; @@ -606,44 +613,84 @@ lws_x509_create_self_signed(struct lws_context *context, mbedtls_ctr_drbg_init(&ctr_drbg); mbedtls_entropy_init(&entropy); if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - (const unsigned char *)"lws_self_signed", 15)) + (const unsigned char *)"lws_cert_gen", 12)) goto bail; } - ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); - if (ret) { - lwsl_err("%s: pk_setup failed %d\n", __func__, ret); - goto bail; - } + if (info->curve_name) { + mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE; + if (!strcmp(info->curve_name, "P-521")) grp_id = MBEDTLS_ECP_DP_SECP521R1; + else if (!strcmp(info->curve_name, "P-384")) grp_id = MBEDTLS_ECP_DP_SECP384R1; + else if (!strcmp(info->curve_name, "P-256")) grp_id = MBEDTLS_ECP_DP_SECP256R1; - ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, pdrbg, - (unsigned int)key_bits, 65537); - if (ret) { - lwsl_err("%s: rsa_gen_key failed %d\n", __func__, ret); - goto bail; + if (grp_id == MBEDTLS_ECP_DP_NONE) { + lwsl_err("%s: unknown curve %s\n", __func__, info->curve_name); + goto bail; + } + + ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + if (ret) goto bail; + + ret = mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(key), mbedtls_ctr_drbg_random, pdrbg); + if (ret) goto bail; + } else { + ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); + if (ret) goto bail; + + ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, pdrbg, + (unsigned int)(info->key_bits ? info->key_bits : 2048), 65537); + if (ret) goto bail; } mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3); mbedtls_x509write_crt_set_subject_key(&crt, &key); - mbedtls_x509write_crt_set_issuer_key(&crt, &key); #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 { - uint8_t serial_val[] = { 1 }; + uint8_t serial_val[8]; + mbedtls_ctr_drbg_random(pdrbg, serial_val, sizeof(serial_val)); + serial_val[0] &= 0x7f; /* Positive */ if (mbedtls_x509write_crt_set_serial_raw(&crt, serial_val, sizeof(serial_val))) goto bail; } #else - if (mbedtls_mpi_read_string(&serial, 10, "1")) - goto bail; - mbedtls_x509write_crt_set_serial(&crt, &serial); + { + unsigned char rnd[8]; + mbedtls_ctr_drbg_random(pdrbg, rnd, sizeof(rnd)); + rnd[0] &= 0x7f; /* Positive */ + if (mbedtls_mpi_read_binary(&serial, rnd, sizeof(rnd))) + goto bail; + mbedtls_x509write_crt_set_serial(&crt, &serial); + } #endif - lws_snprintf(name, sizeof(name), "CN=%s", san ? san : "localhost"); + lws_snprintf(name, sizeof(name), "CN=%s", info->san); if (mbedtls_x509write_crt_set_subject_name(&crt, name)) goto bail; - if (mbedtls_x509write_crt_set_issuer_name(&crt, name)) - goto bail; + + if (info->ca_cert_pem && info->ca_key_pem) { + char issuer_name[256]; + ret = mbedtls_x509_crt_parse(&issuer_crt, (const unsigned char *)info->ca_cert_pem, strlen(info->ca_cert_pem) + 1); + if (ret) goto bail; + + ret = mbedtls_pk_parse_key(&issuer_key, (const unsigned char *)info->ca_key_pem, strlen(info->ca_key_pem) + 1, NULL, 0 +#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 + , mbedtls_ctr_drbg_random, pdrbg +#endif + ); + if (ret) goto bail; + + mbedtls_x509write_crt_set_issuer_key(&crt, &issuer_key); + + ret = mbedtls_x509_dn_gets(issuer_name, sizeof(issuer_name), &issuer_crt.MBEDTLS_PRIVATE_V30_ONLY(subject)); + if (ret < 0) goto bail; + if (mbedtls_x509write_crt_set_issuer_name(&crt, issuer_name)) + goto bail; + } else { + mbedtls_x509write_crt_set_issuer_key(&crt, &key); + if (mbedtls_x509write_crt_set_issuer_name(&crt, name)) + goto bail; + } if (mbedtls_x509write_crt_set_validity(&crt, "20240101000000", "20340101000000")) goto bail; @@ -651,34 +698,29 @@ lws_x509_create_self_signed(struct lws_context *context, mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); /* Extensions */ - mbedtls_x509write_crt_set_basic_constraints(&crt, 0, -1); - mbedtls_x509write_crt_set_key_usage(&crt, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | - MBEDTLS_X509_KU_KEY_ENCIPHERMENT); + mbedtls_x509write_crt_set_basic_constraints(&crt, info->is_ca ? 1 : 0, -1); + + if (info->is_ca) { + mbedtls_x509write_crt_set_key_usage(&crt, MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN); + } else { + mbedtls_x509write_crt_set_key_usage(&crt, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | + MBEDTLS_X509_KU_KEY_ENCIPHERMENT); + } - /* Extended Key Usage - OIDs for serverAuth and clientAuth */ +#if defined(MBEDTLS_OID_SERVER_AUTH) && defined(MBEDTLS_OID_CLIENT_AUTH) { const char *serverAuth = MBEDTLS_OID_SERVER_AUTH; const char *clientAuth = MBEDTLS_OID_CLIENT_AUTH; mbedtls_asn1_named_data *ext_key_usage = NULL; - if (mbedtls_asn1_store_named_data(&ext_key_usage, serverAuth, - strlen(serverAuth), NULL, 0) == NULL) - goto bail; - if (mbedtls_asn1_store_named_data(&ext_key_usage, clientAuth, - strlen(clientAuth), NULL, 0) == NULL) + if (info->is_server) { + if (mbedtls_asn1_store_named_data(&ext_key_usage, serverAuth, strlen(serverAuth), NULL, 0) == NULL) + goto bail; + } + if (mbedtls_asn1_store_named_data(&ext_key_usage, clientAuth, strlen(clientAuth), NULL, 0) == NULL) goto bail; - - /* Unfortunately mbedtls doesn't have a simple wrapper for EKU in some versions, - but it DOES have mbedtls_x509write_crt_set_extension */ - /* Actually serverAuth/clientAuth are very common, let's see if we can just use - mbedtls_x509write_crt_set_ext_key_usage if it exists, or just skip it if complex. - In WebRTC it's quite important. */ - } - - if (san) { - /* DNS name */ - /* MbedTLS uses a sequence for SAN */ } +#endif /* Cert Output */ len = mbedtls_x509write_crt_der(&crt, buf, sizeof(buf), mbedtls_ctr_drbg_random, pdrbg); @@ -714,6 +756,8 @@ lws_x509_create_self_signed(struct lws_context *context, mbedtls_x509write_crt_free(&crt); mbedtls_pk_free(&key); mbedtls_mpi_free(&serial); + mbedtls_x509_crt_free(&issuer_crt); + mbedtls_pk_free(&issuer_key); if (!context) { mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); @@ -721,3 +765,19 @@ lws_x509_create_self_signed(struct lws_context *context, return ret; } + +int +lws_x509_create_self_signed(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const char *san, int key_bits) +{ + struct lws_x509_cert_gen_info info; + + memset(&info, 0, sizeof(info)); + info.san = san ? san : "localhost"; + info.key_bits = key_bits; + info.is_server = 1; + + return lws_x509_create_cert(context, cert_buf, cert_len, key_buf, key_len, &info); +} diff --git a/lib/tls/openssl/openssl-ssl.c b/lib/tls/openssl/openssl-ssl.c index db05f5a0a2..52118d511c 100644 --- a/lib/tls/openssl/openssl-ssl.c +++ b/lib/tls/openssl/openssl-ssl.c @@ -624,6 +624,15 @@ tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt) return lws_tls_fake_POLLIN_for_buffered(pt); } +static void +tops_process_cleanup_openssl(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + OPENSSL_cleanup(); +#endif +} + const struct lws_tls_ops tls_ops_openssl = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_openssl, + .fake_POLLIN_for_buffered = tops_fake_POLLIN_for_buffered_openssl, + .process_cleanup = tops_process_cleanup_openssl, }; diff --git a/lib/tls/openssl/openssl-tls.c b/lib/tls/openssl/openssl-tls.c index d47ee86ac0..e914666fe6 100644 --- a/lib/tls/openssl/openssl-tls.c +++ b/lib/tls/openssl/openssl-tls.c @@ -177,10 +177,5 @@ lws_context_deinit_ssl_library(struct lws_context *context) } #endif - if (!--openssl_contexts_using_global_init) { - lwsl_cx_info(context, "Doing SSL library cleanup"); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - OPENSSL_cleanup(); -#endif - } + --openssl_contexts_using_global_init; } diff --git a/lib/tls/openssl/openssl-x509.c b/lib/tls/openssl/openssl-x509.c index 97c67a9d7f..9db6e121a5 100644 --- a/lib/tls/openssl/openssl-x509.c +++ b/lib/tls/openssl/openssl-x509.c @@ -914,10 +914,10 @@ X509_extension_helper(X509 *x, X509V3_CTX *ctx, int nid, const char *value) #endif int -lws_x509_create_self_signed(struct lws_context *context, - uint8_t **cert_buf, size_t *cert_len, - uint8_t **key_buf, size_t *key_len, - const char *san, int key_bits) +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info) { #if defined(USE_WOLFSSL) lwsl_err("%s: not supported on wolfssl\n", __func__); @@ -931,30 +931,79 @@ lws_x509_create_self_signed(struct lws_context *context, unsigned char *p; int n; size_t len; + X509 *issuer_x509 = NULL; + EVP_PKEY *issuer_pkey = NULL; + BIO *bio; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); - if (!pctx) return 1; - if (EVP_PKEY_keygen_init(pctx) <= 0 || - EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1) <= 0 || - EVP_PKEY_keygen(pctx, &pkey) <= 0) { - EVP_PKEY_CTX_free(pctx); + if (!info || !info->san) return 1; - } - EVP_PKEY_CTX_free(pctx); + + /* 1. Generate or load the subject key */ + if (info->curve_name) { + int nid = OBJ_sn2nid(info->curve_name); + if (nid == NID_undef) + nid = OBJ_ln2nid(info->curve_name); + if (nid == NID_undef) { + /* Common fallback map if OBJ_sn2nid fails for standard names */ + if (!strcmp(info->curve_name, "P-521")) nid = NID_secp521r1; + else if (!strcmp(info->curve_name, "P-384")) nid = NID_secp384r1; + else if (!strcmp(info->curve_name, "P-256")) nid = NID_X9_62_prime256v1; + } + + if (nid == NID_undef) { + lwsl_err("%s: unknown curve %s\n", __func__, info->curve_name); + goto bail; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + { + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!pctx) goto bail; + if (EVP_PKEY_keygen_init(pctx) <= 0 || + EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 || + EVP_PKEY_keygen(pctx, &pkey) <= 0) { + EVP_PKEY_CTX_free(pctx); + goto bail; + } + EVP_PKEY_CTX_free(pctx); + } #else - /* Legacy OpenSSL 1.0.2 fallback */ - EC_KEY *ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (!ec) return 1; - EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_generate_key(ec) <= 0) { - EC_KEY_free(ec); - return 1; - } - EVP_PKEY_assign_EC_KEY(pkey, ec); + /* Legacy OpenSSL 1.0.2 fallback */ + { + EC_KEY *ec = EC_KEY_new_by_curve_name(nid); + if (!ec) goto bail; + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(ec) <= 0) { + EC_KEY_free(ec); + goto bail; + } + EVP_PKEY_assign_EC_KEY(pkey, ec); + } +#endif + } else { + /* RSA */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + { + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!pctx) goto bail; + if (EVP_PKEY_keygen_init(pctx) <= 0 || + EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, info->key_bits ? info->key_bits : 2048) <= 0 || + EVP_PKEY_keygen(pctx, &pkey) <= 0) { + EVP_PKEY_CTX_free(pctx); + goto bail; + } + EVP_PKEY_CTX_free(pctx); + } +#else + { + RSA *rsa = RSA_generate_key(info->key_bits ? info->key_bits : 2048, RSA_F4, NULL, NULL); + if (!rsa) goto bail; + EVP_PKEY_assign_RSA(pkey, rsa); + } #endif + } - /* Create Cert */ + /* 2. Create Cert */ x509 = X509_new(); if (!x509) goto bail; @@ -976,39 +1025,73 @@ lws_x509_create_self_signed(struct lws_context *context, name = X509_get_subject_name(x509); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, - (unsigned char *)(san ? san : "localhost"), -1, -1, 0); - X509_set_issuer_name(x509, name); + (unsigned char *)info->san, -1, -1, 0); + + /* 3. Load Issuer if provided, else self-sign */ + if (info->ca_cert_pem && info->ca_key_pem) { + bio = BIO_new_mem_buf(info->ca_cert_pem, -1); + issuer_x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!issuer_x509) goto bail; + + bio = BIO_new_mem_buf(info->ca_key_pem, -1); + issuer_pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!issuer_pkey) goto bail; - /* Add Extensions */ + X509_set_issuer_name(x509, X509_get_subject_name(issuer_x509)); + } else { + X509_set_issuer_name(x509, name); + issuer_x509 = x509; + issuer_pkey = pkey; + } + + /* 4. Add Extensions */ +#if !defined(USE_WOLFSSL) { X509V3_CTX ctx; - X509V3_set_ctx(&ctx, x509, x509, NULL, NULL, 0); + X509V3_set_ctx(&ctx, issuer_x509, x509, NULL, NULL, 0); + + if (info->is_ca) { + X509_extension_helper(x509, &ctx, NID_basic_constraints, "critical,CA:TRUE"); + X509_extension_helper(x509, &ctx, NID_key_usage, "critical,keyCertSign,cRLSign"); + } else { + X509_extension_helper(x509, &ctx, NID_basic_constraints, "critical,CA:FALSE"); + X509_extension_helper(x509, &ctx, NID_key_usage, "critical,digitalSignature,keyEncipherment"); + if (info->is_server) + X509_extension_helper(x509, &ctx, NID_ext_key_usage, "serverAuth,clientAuth"); + else + X509_extension_helper(x509, &ctx, NID_ext_key_usage, "clientAuth"); + } - X509_extension_helper(x509, &ctx, NID_basic_constraints, "critical,CA:FALSE"); - X509_extension_helper(x509, &ctx, NID_key_usage, "critical,digitalSignature"); - X509_extension_helper(x509, &ctx, NID_ext_key_usage, "serverAuth,clientAuth"); X509_extension_helper(x509, &ctx, NID_subject_key_identifier, "hash"); - X509_extension_helper(x509, &ctx, NID_authority_key_identifier, "keyid:always"); - if (san) { + if (issuer_x509 != x509) + X509_extension_helper(x509, &ctx, NID_authority_key_identifier, "keyid:always,issuer:always"); + else + X509_extension_helper(x509, &ctx, NID_authority_key_identifier, "keyid:always"); + + if (info->san && info->is_server) { char alt[256]; - int is_ip = !!strchr(san, '.'); - lws_snprintf(alt, sizeof(alt), "%s:%s", is_ip ? "IP" : "DNS", san); + int is_ip = (strspn(info->san, "0123456789.") == strlen(info->san)) || + (strspn(info->san, "0123456789abcdefABCDEF:") == strlen(info->san) && strchr(info->san, ':')); + lws_snprintf(alt, sizeof(alt), "%s:%s", is_ip ? "IP" : "DNS", info->san); X509_extension_helper(x509, &ctx, NID_subject_alt_name, alt); } } +#endif - /* Sign */ - if (!X509_sign(x509, pkey, EVP_sha256())) + /* 5. Sign */ + if (!X509_sign(x509, issuer_pkey, EVP_sha256())) goto bail; - /* Export to DER buffers */ + /* 6. Export to DER buffers */ /* Cert */ n = i2d_X509(x509, NULL); if (n < 0) goto bail; len = (size_t)n; - *cert_buf = malloc(len); /* Use standard malloc for example to free */ + *cert_buf = malloc(len); /* Use standard malloc for caller to free */ if (!*cert_buf) goto bail; p = *cert_buf; *cert_len = (size_t)i2d_X509(x509, &p); @@ -1031,9 +1114,27 @@ lws_x509_create_self_signed(struct lws_context *context, ret = 0; bail: + if (issuer_x509 && issuer_x509 != x509) X509_free(issuer_x509); + if (issuer_pkey && issuer_pkey != pkey) EVP_PKEY_free(issuer_pkey); if (x509) X509_free(x509); if (pkey) EVP_PKEY_free(pkey); return ret; #endif } + +int +lws_x509_create_self_signed(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const char *san, int key_bits) +{ + struct lws_x509_cert_gen_info info; + + memset(&info, 0, sizeof(info)); + info.san = san ? san : "localhost"; + info.key_bits = key_bits; + info.is_server = 1; + + return lws_x509_create_cert(context, cert_buf, cert_len, key_buf, key_len, &info); +} diff --git a/lib/tls/private-network.h b/lib/tls/private-network.h index 7aecc1688d..6f19b01706 100644 --- a/lib/tls/private-network.h +++ b/lib/tls/private-network.h @@ -27,6 +27,7 @@ struct lws_context_per_thread; struct lws_tls_ops { int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt); + void (*process_cleanup)(void); }; struct lws_context_tls { diff --git a/lib/tls/schannel/schannel-ssl.c b/lib/tls/schannel/schannel-ssl.c index 2ba29fe53d..43321a54f8 100644 --- a/lib/tls/schannel/schannel-ssl.c +++ b/lib/tls/schannel/schannel-ssl.c @@ -871,5 +871,5 @@ tops_fake_POLLIN_for_buffered_schannel(struct lws_context_per_thread *pt) } const struct lws_tls_ops tls_ops_schannel = { - /* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_schannel, + .fake_POLLIN_for_buffered = tops_fake_POLLIN_for_buffered_schannel, }; diff --git a/lib/tls/schannel/schannel-x509.c b/lib/tls/schannel/schannel-x509.c index eb67a8c338..b6004d7119 100644 --- a/lib/tls/schannel/schannel-x509.c +++ b/lib/tls/schannel/schannel-x509.c @@ -1327,3 +1327,23 @@ lws_tls_acme_sni_csr_create_ecdsa(struct lws_context *context, const char *eleme return _lws_tls_acme_sni_csr_create(context, elements, csr, csr_len, privkey_pem, privkey_len, 1); } #endif + +int +lws_x509_create_cert(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const struct lws_x509_cert_gen_info *info) +{ + lwsl_err("%s: not supported on schannel\n", __func__); + return 1; +} + +int +lws_x509_create_self_signed(struct lws_context *context, + uint8_t **cert_buf, size_t *cert_len, + uint8_t **key_buf, size_t *key_len, + const char *san, int key_bits) +{ + lwsl_err("%s: not supported on schannel\n", __func__); + return 1; +} diff --git a/lib/tls/tls-network.c b/lib/tls/tls-network.c index e7367cc1aa..f542a5a32d 100644 --- a/lib/tls/tls-network.c +++ b/lib/tls/tls-network.c @@ -279,3 +279,24 @@ lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len) + +void +lws_tls_cleanup_process(void) +{ +#if defined(LWS_WITH_MBEDTLS) + if (tls_ops_mbedtls.process_cleanup) + tls_ops_mbedtls.process_cleanup(); +#elif defined(LWS_WITH_SCHANNEL) + if (tls_ops_schannel.process_cleanup) + tls_ops_schannel.process_cleanup(); +#elif defined(LWS_WITH_GNUTLS) + if (tls_ops_gnutls.process_cleanup) + tls_ops_gnutls.process_cleanup(); +#elif defined(LWS_WITH_BEARSSL) + if (tls_ops_bearssl.process_cleanup) + tls_ops_bearssl.process_cleanup(); +#else + if (tls_ops_openssl.process_cleanup) + tls_ops_openssl.process_cleanup(); +#endif +} diff --git a/lwsws/main.c b/lwsws/main.c index 1a542c4793..3194e52f94 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -165,14 +165,16 @@ context_creation(int argc, const char **argv) if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len)) goto init_failed; - if (lws_cmdline_option(argc, argv, "--lws-dht-dnssec-monitor-root")) { + const char *stub = lws_cmdline_option(argc, argv, "--lws-stub"); + if (lws_cmdline_option(argc, argv, "--lws-dht-dnssec-monitor-root") || stub) { const char *p; if ((p = lws_cmdline_option(argc, argv, "--uid"))) info.uid = (unsigned int)atoi(p); if ((p = lws_cmdline_option(argc, argv, "--gid"))) info.gid = (unsigned int)atoi(p); - /* Root monitor makes outbound TLS probes but skips user vhosts, force global TLS init */ + info.lws_stub = stub; + /* Root monitor / stubs make outbound TLS probes but skip user vhosts, force global TLS init */ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_VH_SKIP_PRIV_DROP; } @@ -294,14 +296,15 @@ int main(int argc, char **argv) */ { - int is_monitor = 0; + int is_stub = 0; for (n = 1; n < argc; n++) - if (!strcmp(argv[n], "--lws-dht-dnssec-monitor-root")) { - is_monitor = 1; + if (!strcmp(argv[n], "--lws-dht-dnssec-monitor-root") || + !strncmp(argv[n], "--lws-stub", 10)) { + is_stub = 1; break; } - if (!is_monitor) { + if (!is_stub) { signal(SIGPIPE, SIG_IGN); signal(SIGHUP, reload_handler); signal(SIGINT, reload_handler); diff --git a/minimal-examples-lowlevel/api-tests/api-test-secure-streams/main.c b/minimal-examples-lowlevel/api-tests/api-test-secure-streams/main.c index 7b5a0d58c1..9535308084 100644 --- a/minimal-examples-lowlevel/api-tests/api-test-secure-streams/main.c +++ b/minimal-examples-lowlevel/api-tests/api-test-secure-streams/main.c @@ -68,36 +68,19 @@ static const char * const default_ss_policy = "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy" "rqXRfboQnoZsG4q5WTP468SQvvG5" "\"}," - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ @@ -110,7 +93,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/CMakeLists.txt b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/CMakeLists.txt new file mode 100644 index 0000000000..c805241de5 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/CMakeLists.txt @@ -0,0 +1,31 @@ +project(lws-minimal-mqtt-client-qos2 C) +cmake_minimum_required(VERSION 3.10) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-minimal-mqtt-client-qos2) +set(SRCS minimal-mqtt-client-qos2.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_MQTT 1 requirements) +require_lws_config(LWS_WITH_CLIENT 1 requirements) +require_lws_config(LWS_WITH_SYS_STATE 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() + + # + # requires mosquitto running locally + # + # add_test(NAME mqtt-client-qos2 COMMAND lws-minimal-mqtt-client-qos2 -f) + # set_tests_properties(mqtt-client-qos2 PROPERTIES + # WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2 + # TIMEOUT 20) +endif() diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/README.md b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/README.md new file mode 100644 index 0000000000..fa3b3b3583 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/README.md @@ -0,0 +1,46 @@ +# lws-minimal-mqtt-client-qos2 + +This is a minimal example demonstrating MQTT QoS 2 functionality with deduplication and session restoration capabilities. + +The client connects to a local Mosquitto broker, subscribes to a topic (`test/topic0`), and publishes a QoS 2 message to that same topic. It tracks the `packet_id` of unacknowledged QoS 2 receives using the operations API. + +## Build + +```bash +$ cmake . && make +``` + +## Usage + +This example requires a local `mosquitto` broker to run against. + +### 1. Standard Testing +Run the client normally to see the full QoS 2 handshake without any interruptions: + +```bash +$ ./lws-minimal-mqtt-client-qos2 +``` + +The client will successfully connect, subscribe, publish a message, receive it, and log the payload, followed by the normal completion. + +### 2. Fault Injection Mode (Session Resumption) +Run the client with the `-f` flag to simulate a dropped connection during the QoS 2 handshake: + +```bash +$ ./lws-minimal-mqtt-client-qos2 -f +``` + +**Sequence of Events:** +1. The client receives the QoS 2 `PUBLISH` from the broker. +2. The custom `my_rx_add` callback fires, saving the unacknowledged `packet_id` to the simulated state store. +3. The client immediately drops its connection (forceful simulation). +4. The built-in retry policy triggers a reconnection. +5. On connection establishment, the client injects the stashed `packet_id` back into the library using `lws_mqtt_client_qos2_rx_add`. +6. Mosquitto resends the `PUBLISH` with `DUP=1`. +7. `libwebsockets` recognizes the duplicate, drops the payload to maintain the *Exactly-Once* guarantee, and seamlessly resumes the handshake (`PUBREC` -> `PUBREL` -> `PUBCOMP`). + +## Commandline Options + +- `-d `: Set the logging level (e.g., `-d 1039`) +- `-s`: Use TLS / HTTPS +- `-f`: Simulate connection drop for testing QoS 2 restoration diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/minimal-mqtt-client-qos2.c b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/minimal-mqtt-client-qos2.c new file mode 100644 index 0000000000..ab9c545785 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/minimal-mqtt-client-qos2.c @@ -0,0 +1,419 @@ +/* + * lws-minimal-mqtt-client + * + * Written in 2010-2020 by Andy Green + * Sakthi Kannan + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include + +enum { + LWS_SW_S, + LWS_SW_F, + LWS_SW_HELP, +}; + +static const struct lws_switches switches[] = { + [LWS_SW_S] = { "-s", "Use TLS / https" }, + [LWS_SW_F] = { "-f", "Simulate connection drop for testing QoS2 restoration" }, + [LWS_SW_HELP] = { "--help", "Show this help information" }, +}; + +#include +#include +#if defined(WIN32) +#define HAVE_STRUCT_TIMESPEC +#if defined(pid_t) +#undef pid_t +#endif +#endif +#include +#include + +enum { + STATE_SUBSCRIBE, /* subscribe to the topic */ + STATE_PUBLISH_QOS0, /* Send the message in QoS0 */ + STATE_WAIT_ACK0, /* Wait for the synthetic "ack" */ + STATE_PUBLISH_QOS1, /* Send the message in QoS1 */ + STATE_WAIT_ACK1, /* Wait for the real ack (or timeout + retry) */ + STATE_PUBLISH_QOS2, /* Send the message in QoS2 */ + STATE_WAIT_ACK2, /* Wait for the real ack (PUBCOMP) */ + + STATE_TEST_FINISH +}; + +static int interrupted, bad = 1, do_ssl, do_fault_injection; +static uint16_t simulated_saved_pkt_id; + +static const lws_retry_bo_t retry = { + .secs_since_valid_ping = 20, /* if idle, PINGREQ after secs */ + .secs_since_valid_hangup = 25, /* hangup if still idle secs */ +}; + +static int my_rx_add(struct lws *wsi, const char *client_id, uint16_t pkt_id) { + lwsl_user("QoS 2: Stored packet_id %d for client %s\n", pkt_id, client_id); + simulated_saved_pkt_id = pkt_id; + return 0; +} +static int my_rx_remove(struct lws *wsi, const char *client_id, uint16_t pkt_id) { + lwsl_user("QoS 2: Removed packet_id %d for client %s\n", pkt_id, client_id); + simulated_saved_pkt_id = 0; + return 0; +} +static const lws_mqtt_qos2_state_ops_t my_qos2_ops = { + .rx_add = my_rx_add, + .rx_remove = my_rx_remove, +}; + +static const lws_mqtt_client_connect_param_t client_connect_param = { + .client_id = "lwsMqttClient", + .keep_alive = 60, + .clean_start = 1, + .client_id_nofree = 1, + .username_nofree = 1, + .password_nofree = 1, + .will_param = { + .topic = "good/bye", + .message = "sign-off", + .qos = 0, + .retain = 0, + }, + .username = "lwsUser", + .password = "mySecretPassword", + .qos2_state_ops = &my_qos2_ops, +}; + +static lws_mqtt_publish_param_t pub_param; + +static lws_mqtt_topic_elem_t topics[] = { + [0] = { .name = "test/topic0", .qos = QOS2 }, + [1] = { .name = "test/topic1", .qos = QOS2 }, +}; + +static lws_mqtt_subscribe_param_t sub_param = { + .topic = &topics[0], + .num_topics = LWS_ARRAY_SIZE(topics), +}; + +static const char * const test_string = + "No one would have believed in the last years of the nineteenth " + "century that this world was being watched keenly and closely by " + "intelligences greater than man's and yet as mortal as his own; that as " + "men busied themselves about their various concerns they were " + "scrutinised and studied, perhaps almost as narrowly as a man with a " + "microscope might scrutinise the transient creatures that swarm and " + "multiply in a drop of water. With infinite complacency men went to " + "and fro over this globe about their little affairs, serene in their " + "assurance of their empire over matter. It is possible that the " + "infusoria under the microscope do the same. No one gave a thought to " + "the older worlds of space as sources of human danger, or thought of " + "them only to dismiss the idea of life upon them as impossible or " + "improbable. It is curious to recall some of the mental habits of " + "those departed days. At most terrestrial men fancied there might be " + "other men upon Mars, perhaps inferior to themselves and ready to " + "welcome a missionary enterprise. Yet across the gulf of space, minds " + "that are to our minds as ours are to those of the beasts that perish, " + "intellects vast and cool and unsympathetic, regarded this earth with " + "envious eyes, and slowly and surely drew their plans against us. And " + "early in the twentieth century came the great disillusionment. "; + +/* this reflects the length of the string above */ +#define TEST_STRING_LEN 1337 + +struct pss { + int state; + size_t pos; + int retries; +}; + +static void +sigint_handler(int sig) +{ + interrupted = 1; +} + +static int +connect_client(struct lws_context *context) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof i); + + i.mqtt_cp = &client_connect_param; + i.address = "localhost"; + i.host = "localhost"; + i.protocol = "mqtt"; + i.context = context; + i.method = "MQTT"; + i.alpn = "mqtt"; + i.port = 1883; + + if (do_ssl) { + i.ssl_connection = LCCSCF_USE_SSL; + i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; + i.port = 8883; + } + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("%s: Client Connect Failed\n", __func__); + + return 1; + } + + return 0; +} + +static int +system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, + int current, int target) +{ + struct lws_context *context = mgr->parent; + + if (current != LWS_SYSTATE_OPERATIONAL || + target != LWS_SYSTATE_OPERATIONAL) + return 0; + + /* + * We delay trying to do the client connection until + * the protocols have been initialized for each + * vhost... this happens after we have network and + * time so we can judge tls cert validity. + */ + + if (connect_client(context)) + interrupted = 1; + + return 0; + } + + +static int +callback_mqtt(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct pss *pss = (struct pss *)user; + lws_mqtt_publish_param_t *pub; + size_t chunk; + + switch (reason) { + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__, + in ? (char *)in : "(null)"); + interrupted = 1; + break; + + case LWS_CALLBACK_MQTT_CLIENT_CLOSED: + lwsl_user("%s: CLIENT_CLOSED\n", __func__); + interrupted = 1; + break; + + case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED: + lwsl_user("%s: MQTT_CLIENT_ESTABLISHED\n", __func__); + + /* + * In a real application, you would load your saved QoS2 unacknowledged + * receives from your database here and inject them. + * + * For this example, we inject the simulated saved ID. + */ + if (simulated_saved_pkt_id) { + lwsl_user("%s: Restoring saved QoS2 packet_id %d\n", __func__, simulated_saved_pkt_id); + lws_mqtt_client_qos2_rx_add(wsi, simulated_saved_pkt_id); + } + + lws_callback_on_writable(wsi); + + return 0; + + case LWS_CALLBACK_MQTT_SUBSCRIBED: + lwsl_user("%s: MQTT_SUBSCRIBED\n", __func__); + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE: + /* + * Extra WRITEABLE may appear here other than ones we asked + * for, so we must consult our own state to decide if we want + * to make use of the opportunity + */ + + switch (pss->state) { + case STATE_SUBSCRIBE: + lwsl_user("%s: WRITEABLE: Subscribing\n", __func__); + + if (lws_mqtt_client_send_subcribe(wsi, &sub_param)) { + lwsl_notice("%s: subscribe failed\n", __func__); + + return -1; + } + pss->state++; + break; + + case STATE_PUBLISH_QOS0: + case STATE_PUBLISH_QOS1: + case STATE_PUBLISH_QOS2: + + lwsl_user("%s: WRITEABLE: Publish\n", __func__); + + pub_param.topic = "test/topic0"; + pub_param.topic_len = (uint16_t)strlen(pub_param.topic); + if (pss->state == STATE_PUBLISH_QOS0) + pub_param.qos = QOS0; + else if (pss->state == STATE_PUBLISH_QOS1) + pub_param.qos = QOS1; + else + pub_param.qos = QOS2; + + pub_param.payload_len = TEST_STRING_LEN; + + /* We send the message out 300 bytes or less at at time */ + + chunk = 300; + + if (chunk > TEST_STRING_LEN - pss->pos) + chunk = TEST_STRING_LEN - pss->pos; + + if (lws_mqtt_client_send_publish(wsi, &pub_param, + test_string + pss->pos, (uint32_t)chunk, + (pss->pos + chunk == TEST_STRING_LEN))) + return -1; + + pss->pos += chunk; + + if (pss->pos == TEST_STRING_LEN) { + pss->pos = 0; + pss->state++; + } + break; + + default: + break; + } + + return 0; + + case LWS_CALLBACK_MQTT_ACK: + lwsl_user("%s: MQTT_ACK\n", __func__); + /* + * We can forget about the message we just sent, it's done. + * + * For our test, that's the indication we can close the wsi. + */ + + pss->state++; + if (pss->state != STATE_TEST_FINISH) { + lws_callback_on_writable(wsi); + break; + } + + /* Oh we are done then */ + + bad = 0; + interrupted = 1; + lws_cancel_service(lws_get_context(wsi)); + break; + + case LWS_CALLBACK_MQTT_QOS2_RX_COMPLETE: + lwsl_user("%s: MQTT_QOS2_RX_COMPLETE packet ID %d\n", __func__, *(uint16_t *)in); + return 0; + + case LWS_CALLBACK_MQTT_RESEND: + lwsl_user("%s: MQTT_RESEND\n", __func__); + /* + * We must resend the packet ID mentioned in len + */ + if (++pss->retries == 3) { + interrupted = 1; + break; + } + pss->state--; + pss->pos = 0; + break; + + case LWS_CALLBACK_MQTT_CLIENT_RX: + lwsl_user("%s: MQTT_CLIENT_RX\n", __func__); + + pub = (lws_mqtt_publish_param_t *)in; + assert(pub); + + lwsl_hexdump_notice(pub->topic, pub->topic_len); + lwsl_hexdump_notice(pub->payload, pub->payload_len); + + if (do_fault_injection) { + lwsl_user("%s: Simulating connection drop!\n", __func__); + do_fault_injection = 0; /* only do it once */ + return -1; /* forceful drop */ + } + + return 0; + + default: + break; + } + + return 0; +} + +static const struct lws_protocols protocols[] = { + { + .name = "mqtt", + .callback = callback_mqtt, + .per_session_data_size = sizeof(struct pss) + }, + LWS_PROTOCOL_LIST_TERM +}; + +int main(int argc, const char **argv) +{ + lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, + system_notify_cb, "app" }; + lws_state_notify_link_t *na[] = { ¬ifier, NULL }; + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + signal(SIGINT, sigint_handler); + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + lws_cmdline_option_handle_builtin(argc, argv, &info); + + do_ssl = !!lws_cmdline_option(argc, argv, switches[LWS_SW_S].sw); + do_fault_injection = !!lws_cmdline_option(argc, argv, switches[LWS_SW_F].sw); + + if (do_ssl) + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + + lwsl_user("LWS minimal MQTT client %s [-d][-s][-f]\n", + do_ssl ? "tls enabled": "unencrypted"); + + info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ + info.protocols = protocols; + info.register_notifier_list = na; + info.fd_limit_per_thread = 1 + 1 + 1; + info.retry_and_idle_policy = &retry; + +#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL) + /* + * OpenSSL uses the system trust store. mbedTLS has to be told which + * CA to trust explicitly. + */ + info.client_ssl_ca_filepath = "./mosq-ca.crt"; +#endif + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* Event loop */ + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + + lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); + lws_context_destroy(context); + + return bad; +} diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-ca.crt b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-ca.crt new file mode 100644 index 0000000000..ee3d9769b5 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUAVMnfaOq8yiLnvIB/obE689mulMwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5 +MTEyMDA1NTYyNFoYDzIxMTkxMDI3MDU1NjI0WjBWMQswCQYDVQQGEwJYWDEVMBMG +A1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRk +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCyw+kBLg9lCGlBceil0lNqgh7fyguin8IFm5X60bfSJ/pV6i8dZZplVjE+ +g75iFEFBYyfn+6bOPdinfQ7Uu+l6t6y2HWbK6MkoypF/g7cdtUFy9s4cUX0467BZ +hMPJUc4UfnD+bYcXoguPJ6/OH84+Ayg6uvm5nJ32pDiXr6gMd5YljdXaJpCeeh4w +O2UBD1HffyPIklIPT59lxv2ZvKnZbE4UE1uaLLvTWiT+X+gA3i0Syxkq5RlZ61DE +3MyIYAUVSf3coNXCSdJ9wrOsGoP+X+T+aDjnFCCnqus3QX3JOHTKf4+tBoF65cNP +mnHXb5/ZQCcR9HMofacalMpjiGb7AgMBAAGjUzBRMB0GA1UdDgQWBBTl3poLE/22 +R4RXTMoXPHMlc3QRjzAfBgNVHSMEGDAWgBTl3poLE/22R4RXTMoXPHMlc3QRjzAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCwWVnNjKRH9CCBv3yT +Djah51q3NH3E+f1IcBZz2c5WbJHxEtP4QC57ou2x3hC7Cur9iOqIO57VW8vnFP2Y +bD9oHb46grsGhwuaSuA2AlFZ5EuUAe2cgEj5/3Ihd3HYsXN3rfRO1PVGN1iRG1sE +xAxENNm6nOS1Ht1Zy5YmMiSPzghcsTnpg44AqsmowbIED75EpumLwY2NbAl9/7JL +EJil3cxEZ8rl2DVWPU3hAwrOfhl/rkQTCcigyPvZvAqsJ9vYhZftrF6njUsqr5kL +KHENu5ySKPNk5gFR17WjWoqT6iEOZN25qyfFhBRzjpCX6zD1gx0sYcVryCnTH5Y4 +Drjh +-----END CERTIFICATE----- diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.crt b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.crt new file mode 100644 index 0000000000..46952b55b9 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh0CFFu5XIMrh5gPYnjTr8UrXA3UiWqHMA0GCSqGSIb3DQEBCwUAMFYx +CzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0Rl +ZmF1bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xOTExMjAw +NTU4NTdaGA8yMTE5MTAyNzA1NTg1N1owVjELMAkGA1UEBhMCWFgxFTATBgNVBAcM +DERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAG +A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +tldZ5yGrBsLR/7G4b48pwQSSG6fp4egiZdeFV7SRNfbMzpuIDlFdZM9zdcoQQrTl +24aVIGwkvfsMD33Hb/D1WW+r8UFnq4CutigwXArXUxoFX6fa0rwEEjuxwG3f7+xm +vb6p/KXomyWcdAUmAvALaDXIUDEc3tH+Hxik5z36YjIqRjH16jhhs/6T8B3xAWuR +jnDknJWv36QruMIyPUqYYkl2zl4VXUKBgWZr31Opm08kb/FrWJ6lQ7912jZC8G2L +rtwZJB/1psBrX3Oj/Quj+BWHmzkosqVae2G5zAhphZ2NMrdSVfxdctNmakH8oTwf +hRas8DE2olW3whUkfKG2DQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAKEQ7LpPdU +XbJKushJ7wmuljQn3pmW9SjzFMlL9o59KLHWAmxzTDaAm6r3SGgHeSz3ZLwqtJ8I +7pCxQxI6V1ySMkWI1mfi4KPSavxBRaST4o8+YIKJt4c5aLB1seHoghx3Q/jXEGEB +9dFyLMK6u3EhYSletQNeMVGaeK1q/nVZdHNk4LXVIHsXnKlxyMnW3v18iaV3ZhVd +doAWMpnbY91AyCXjOmQrfQaHLL6n3r1Xk2L+cRO3nSor54UIXqIJxHZtj+ZYOy3Z +C5AkQ1yyTTOtEz9WB0Bk2O4ZfNgJO+1MbQSfL0m0YKpuaFnMHD9g5ufUlJGR2aMI +nw1F/oGZoNUl +-----END CERTIFICATE----- diff --git a/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.key b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.key new file mode 100644 index 0000000000..255528d361 --- /dev/null +++ b/minimal-examples-lowlevel/mqtt-client/minimal-mqtt-client-qos2/mosq-server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtldZ5yGrBsLR/7G4b48pwQSSG6fp4egiZdeFV7SRNfbMzpuI +DlFdZM9zdcoQQrTl24aVIGwkvfsMD33Hb/D1WW+r8UFnq4CutigwXArXUxoFX6fa +0rwEEjuxwG3f7+xmvb6p/KXomyWcdAUmAvALaDXIUDEc3tH+Hxik5z36YjIqRjH1 +6jhhs/6T8B3xAWuRjnDknJWv36QruMIyPUqYYkl2zl4VXUKBgWZr31Opm08kb/Fr +WJ6lQ7912jZC8G2LrtwZJB/1psBrX3Oj/Quj+BWHmzkosqVae2G5zAhphZ2NMrdS +VfxdctNmakH8oTwfhRas8DE2olW3whUkfKG2DQIDAQABAoIBACMctwc3CIQIx/+A +7Y8t9lBg3PHOZ89EsDsEQX0eHEhT+iRe9tgq+t0KxaUNAAyYYRrg056mtHyQ90WU +Zu87a0OJqYaPnbL82KfjHUzcGZK7FAXTgOPLqM0KCbSQc+rzjuVC7eDk4eHeYD5H +L4apSskKckRe8LxHm7PJPxf4a1q1EuMEfAyJhh7Tot0oVsG/wABGFUuJVJWXnec1 +0ukPowKh9bg7UyEecwyeYGzXqNqvbjhS3J0dBkjG5vfxuVHae2yIeXk6ZNsCw6tO +K8bklmsmbWAFR5SKpsNve8X/6nlclP0taDDZsz0KSbxJEd2DuRhFcdiRWEoryZVp +7DOORFECgYEA5sdsRjQoHaU85QZuM7ff6NpNT7kMIJbjHRdiauEBakLHs8yVLNEp +Vvg5fcZY4PumqPKyGEjUD6DenlLvb4OBGqzKGGhAJaLz9cpVoWWPz8y1NRBfPjlB +FQdB4GdtBQGXwnZoD9kXPjYHlk4nwZZ/Sitm2w6RibiIxE0adnwLhP8CgYEAykTE +5NZ88OGGf0RWUt54OxTl4fChAcvK93KkdlK9nbokXHs7VIl4QpKPFu1nuMDrkVI4 +fVYwRDcZUjyxqbpBSf/M6T/kuEsMWBYYGv5c9/U87y0UWHbphN0TSdML2DJp9BTy +uy4RleQovof2kOr6sOsKP8lhBGSlhXyJDKn1iPMCgYEAnpvc7HsYPxe7vGQpBV6Q +g0bV777seNF7EhlqSK6P/GodOpOWyxCN6vn6+ViC6U3Lgz4Z7NrQ9FTJ6+JwMSIe +byjmVNQBklxmcz02kRBuQJEe0XOJIgjTlBJC0moC4Xfwx3P9nTbE5LrZiBH6/O/k +WCNwM4nVuOOdC906HMiwWh0CgYEAqn3m3ODydXQTk2i9vqIpA9vsnVLf1Ay8a3El +sVqy26VQCugQrYQmay7wD6pS2Ec9CMQeO3+PtaAf5tKkCmWlrMNCLIWfu7v+jq0o +6m/nW1ZKY2xDDwJEeaqDHKIZBMYRyxxxMVd2mTq1IUynh6WZY9DqVbPf4/0WC/tZ +5ePIxAMCgYEAwwBNT2xjG1mWD4eANvKjQgrsxKFttmaXXCiixZJR+tsQc5bff5Yb +IgvvkIwLHoNpL2Nk7sEjS4sUtAKwzCtIMwvPnhQedICnOEteZ8NPfaFmPewcovcL +gv9k+mFActZ7H8i9FXLrZHyEzOXZaM/vY/mHbrlJSWnSDZsvnVzQv+o= +-----END RSA PRIVATE KEY----- diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-blob/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-blob/minimal-secure-streams.c index 1256603230..98460c6599 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-blob/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-blob/minimal-secure-streams.c @@ -114,36 +114,19 @@ static const char * const default_ss_policy = * using that. */ #if !defined(FORCE_OS_TRUST_STORE) - "{\"isrg_root_x1\": \"" -"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" -"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" -"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" -"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" -"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" -"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" -"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" -"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" -"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" -"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" -"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" -"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" -"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" -"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" -"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" -"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" -"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" -"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" -"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" -"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" -"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" -"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" -"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" -"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" -"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" -"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" -"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" -"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" -"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" +"MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" +"CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" +"R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" +"MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" +"ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" +"EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" +"+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" +"ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" +"AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" +"zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" +"tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" +"/q4AaOeMSQ+2b1tbFfLn" "\"}" #endif "]," @@ -152,7 +135,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" #endif diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-hugeurl/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-hugeurl/minimal-secure-streams.c index 0bd7384781..da61abd3fa 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-hugeurl/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-hugeurl/minimal-secure-streams.c @@ -69,43 +69,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c index c86efbbcb3..55e987e650 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c @@ -103,43 +103,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" -"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" -"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" -"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" -"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" -"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" -"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" -"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" -"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" -"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" -"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" -"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" -"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" -"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" -"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" -"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" -"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" -"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" -"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" -"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" -"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" -"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" -"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" -"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" -"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" -"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" -"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" -"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" -"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" -"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" +"MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" +"CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" +"R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" +"MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" +"ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" +"EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" +"+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" +"ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" +"AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" +"zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" +"tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" +"/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metrics-proxy/metrics-proxy-policy.json b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metrics-proxy/metrics-proxy-policy.json index d74201454f..339f6546a2 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metrics-proxy/metrics-proxy-policy.json +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-metrics-proxy/metrics-proxy-policy.json @@ -11,12 +11,13 @@ "svalidhup":310 }}], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="}, + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn"}, {"self_localhost": "MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuWaICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXarjr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrowYNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuAxbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9PwtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjvxQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKkujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYAAOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6GgmnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIXe2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE="},{"self_localhost_key": "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCjYtuWaICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABAoICAFWe8MQZb37k2gdAV3Y6aq8fqokKQqbCNLd3giGFwYkezHXoJfg6Di7oZxNcKyw35LFEghkgtQqErQqo35VPIoH+vXUpWOjnCmM4muFA9/cX6mYMc8TmJsg0ewLdBCOZVw+wPABlaqz+0UOiSMMftpk9fz9JwGd8ERyBsT+tk3Qi6D0vPZVsC1KqxxL/cwIFd3Hf2ZBtJXe0KBn1pktWht5AKqx9mld2Ovl7NjgiC1Fx9r+fZw/iOabFFwQA4dr+R8mEMK/7bd4VXfQ1o/QGGbMTG+ulFrsiDyP+rBIAaGC0i7gDjLAIBQeDhP409ZhswIEc/GBtODU372a2CQK/u4Q/HBQvuBtKFNkGUooLgCCbFxzgNUGc83GB/6IwbEM7R5uXqsFiE71LpmroDyjKTlQ8YZkpIcLNVLw0usoGYHFm2rvCyEVlfsE3Ub8cFyTFk50SeOcF2QL2xzKmmbZEpXglxBHR0hjgon0IKJDGfor4bHO7Nt+1Ece8u2oTEKvpz5aIn44OeC5mApRGy83/0bvsesnWjDE/bGpoT8qFuy+0urDEPNId44XcJm1IRIlG56ErxC3l0s11wrIpTmXXckqwzFR9s2z7f0zjeyxqZg4NTPI7wkM3M8BXlvp2GTBIeoxrWB4V3YArwu8QF80QBgVzmgHl24nTg00UH1OjZsABAoIBAQDOxftSDbSqGytcWqPYP3SZHAWDA0O4ACEM+eCwau9ASutl0IDlNDMJ8nC2ph25BMe5hHDWp2cGQJog7pZ/3qQogQho2gUniKDifN7740QdykllTzTVROqmP8+efreIvqlzHmuqaGfGs5oTkZaWj5su+B+bT+9rIwZcwfs5YRINhQRx17qa++xh5mfE25c+M9fiIBTiNSo4lTxWMBShnK8xrGaMEmN7W0qTMbFHPgQz5FcxRjCCqwHilwNBeLDTp/ZECEB7y34khVh531mBE2mNzSVIQcGZP1I/DvXjW7UUNdgFwii/GW+6M0uUDy23UVQpbFzcV8o1C2nZc4Fb4zwBAoIBAQDKSJkFwwuRnaVJS6WxOKjX8MCu9/cKPnwBv2mmI2jgGxHTw5sr3ahmF5eTb8Zo19BowytN+tr62ZFoIBA9Ubc9esEAU8l3fggdfM82cuR9sGcfQVoCh8tMg6BP8IBLOmbSUhN3PG2m39I802u0fFNVQCJKhx1m1MFFLOu7lVcDS9JN+oYVPb6MDfBLm5jOiPuYkFZ4gH79J7gXI0/YKhaJ7yXthYVkdrSF6Eooer4RZgma62Dd1VNzSq3JBo6rYjF7Lvd+RwDCR1thHrmf/IXplxpNVkoMVxtzbrrbgnC25QmvRYc0rlS/kvM4yQhMH3eA7IycDZMpY+0xm7I7jTT7AoIBAGKzKIMDXdCxBWKhNYJ8z7hiItNl1IZZMW2TPUiY0rl6yaChBVXjM9W0r07QPnHZsUiByqb743adkbTUjmxdJzjaVtxN7ZXwZvOVrY7I7fPWYnCEfXCr4+IVpZI/ZHZWpGX6CGSgT6EOjCZ5IUufIvEpqVSmtF8MqfXO9o9uIYLokrWQx1dBl5UnuTLDqw8bChq7O5y6yfuWaOWvL7nxI8NvSsfj4y635gIa/0dFeBYZEfHIUlGdNVomwXwYEzgE/c19ruIowX7HU/NgxMWTMZhpazlxgesXybel+YNcfDQ4e3RMOMz3ZFiaMaJsGGNf4++d9TmMgk4Ns6oDs6Tb9AECggEBAJYzd+SOYo26iBu3nw3L65uEeh6xou8pXH0Tu4gQrPQTRZZ/nT3iNgOwqu1gRuxcq7TOjt41UdqIKO8vN7/AaJavCpaKoIMowy/aGCbvAvjNPpU3unU8jdl/t08EXs79S5IKPcgAx87sTTi7KDN5SYt4tr2uPEe53NTXuSatilG5QCyExIELOuzWAMKzg7CAiIlNS9foWeLyVkBgCQ6Sme/L8ta+mUDy37K6vC34jh9vK9yrwF6X44ItRoOJafCaVfGI+175q/eWcqTX4q+IG4tKls4sL4mgOJLq+ra50aYMxbcuommctPMXU6CrrYyQpPTHMNVDQy2ttFdsq9iKTncCggEBAMmt/8yvPflS+xv3kg/ZBvR9JB1In2n3rUCYYD47ReKFqJ03Vmq5C9nY56s9w7OUO8perBXlJYmKZQhO4293lvxZD2Iq4NcZbVSCMoHAUzhzY3brdgtSIxa2gGveGAezZ38qKIU26dkz7deECY4vrsRkwhpTW0LGVCpjcQoaKvymAoCmAs8V2oMrZiw1YQ9uOUoWwOqm1wZqmVcOXvPIS2gWAs3fQlWjH9hkcQTMsUaXQDOD0aqkSY3ENqOvbCV1/oUpRi3076khCoAXI1bKSn/AvR3KDP14B5toHI/F5OTSEiGhhHesgRrsfBrpEY1IATtPq1taBZZogRqI3rOkkPk=" }], "trust_stores": [ {"name": "le_via_isrg", - "stack": ["isrg_root_x1"] + "stack": ["isrg_root_x2"] } ], "s": [ { diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-perf/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-perf/minimal-secure-streams.c index dbd891edc6..631c4783e0 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-perf/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-perf/minimal-secure-streams.c @@ -114,43 +114,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c index 796d45b378..087e3fd666 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-post/minimal-secure-streams-post.c @@ -196,43 +196,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-proxy/main.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-proxy/main.c index 0a45ed4a61..32a142e6bb 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-proxy/main.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-proxy/main.c @@ -84,43 +84,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/README.md b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/README.md index 6e98f1180a..a9f8829f41 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/README.md +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/README.md @@ -32,7 +32,7 @@ Commandline option|Meaning [2020/07/27 10:51:05:1753] N: lws_adopt_descriptor_vhost2: wsi 0x53182c0, vhost system ss_handle (nil) [2020/07/27 10:51:05:2129] N: lws_ss_policy_parser_cb: server 'self_localhost' keep 52 0x5318cc0 [2020/07/27 10:51:05:2134] N: lws_ss_policy_parser_cb: server 'self_localhost_key' keep 53 0x5318cf8 -[2020/07/27 10:51:05:2192] N: lws_ss_policy_ref_trust_store: le_via_isrg trust store initial 'isrg_root_x1' +[2020/07/27 10:51:05:2192] N: lws_ss_policy_ref_trust_store: le_via_isrg trust store initial 'isrg_root_x2' [2020/07/27 10:51:05:7804] N: smd_cb: creating server stream [2020/07/27 10:51:05:7851] N: Vhost 'myserver' using TLS mode [2020/07/27 10:51:05:8660] N: SSL ECDH curve 'prime256v1' diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/main.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/main.c index 9a3f141281..db5a7b3b81 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/main.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-server/main.c @@ -56,36 +56,19 @@ static const char * const default_ss_policy = * deployed in browsers. We use the ISRG path because that * way we can skip the extra IdenTrust root cert. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}," /* * a selfsigned cert for localhost for 100 years @@ -184,7 +167,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.h b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.h index b772a6a0e5..c021fdc950 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.h +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.h @@ -17,9 +17,9 @@ } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" }, { - "LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" + "LEX3_isrg_root_x2": "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" }, { "amazon_root_ca_1": "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5" }, { @@ -33,7 +33,7 @@ }], "trust_stores": [{ "name": "le_via_isrg", - "stack": ["isrg_root_x1", "LEX3_isrg_root_x1"] + "stack": ["isrg_root_x2", "LEX3_isrg_root_x2"] }, { "name": "api_amazon_com", "stack": ["digicert_global_ca_g2", "digicert_global_root_g2"] @@ -347,7 +347,7 @@ static const uint8_t _ss_der_amazon_root_ca_1[] = { static const lws_ss_x509_t _ss_x509_amazon_root_ca_1 = { .vhost_name = "amazon_root_ca_1", .ca_der = _ss_der_amazon_root_ca_1, - .ca_der_len = 837, + .ca_der_len = 543, }; static const uint8_t _ss_der_starfield_class_2_ca[] = { /* 0x 0 */ 0x30, 0x82, 0x04, 0x0F, 0x30, 0x82, 0x02, 0xF7, @@ -485,7 +485,7 @@ static const uint8_t _ss_der_starfield_class_2_ca[] = { static const lws_ss_x509_t _ss_x509_starfield_class_2_ca = { .vhost_name = "starfield_class_2_ca", .ca_der = _ss_der_starfield_class_2_ca, - .ca_der_len = 1043, + .ca_der_len = 543, }; static const uint8_t _ss_der_starfield_services_root_ca[] = { /* 0x 0 */ 0x30, 0x82, 0x03, 0xEF, 0x30, 0x82, 0x02, 0xD7, @@ -619,7 +619,7 @@ static const uint8_t _ss_der_starfield_services_root_ca[] = { static const lws_ss_x509_t _ss_x509_starfield_services_root_ca = { .vhost_name = "starfield_services_root_ca", .ca_der = _ss_der_starfield_services_root_ca, - .ca_der_len = 1011, + .ca_der_len = 543, }; static const lws_ss_trust_store_t _ss_ts_mqtt_amz_iot = { .name = "mqtt_amz_iot", @@ -810,7 +810,82 @@ static const lws_ss_x509_t _ss_x509_isrg_root_x1 = { .ca_der = _ss_der_isrg_root_x1, .ca_der_len = 1391, }; -static const uint8_t _ss_der_LEX3_isrg_root_x1[] = { +static const uint8_t _ss_der_isrg_root_x2[] = { + /* 0x 0 */ 0x30, 0x82, 0x02, 0x1B, 0x30, 0x82, 0x01, 0xA1, + /* 0x 8 */ 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x41, + /* 0x 10 */ 0xD2, 0x9D, 0xD1, 0x72, 0xEA, 0xEE, 0xA7, 0x80, + /* 0x 18 */ 0xC1, 0x2C, 0x6C, 0xE9, 0x2F, 0x87, 0x52, 0x30, + /* 0x 20 */ 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + /* 0x 28 */ 0x04, 0x03, 0x03, 0x30, 0x4F, 0x31, 0x0B, 0x30, + /* 0x 30 */ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + /* 0x 38 */ 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, + /* 0x 40 */ 0x55, 0x04, 0x0A, 0x13, 0x20, 0x49, 0x6E, 0x74, + /* 0x 48 */ 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, 0x65, + /* 0x 50 */ 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, + /* 0x 58 */ 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, + /* 0x 60 */ 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, 0x30, + /* 0x 68 */ 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, + /* 0x 70 */ 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6F, 0x6F, + /* 0x 78 */ 0x74, 0x20, 0x58, 0x32, 0x30, 0x1E, 0x17, 0x0D, + /* 0x 80 */ 0x32, 0x30, 0x30, 0x39, 0x30, 0x34, 0x30, 0x30, + /* 0x 88 */ 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x34, + /* 0x 90 */ 0x30, 0x30, 0x39, 0x31, 0x37, 0x31, 0x36, 0x30, + /* 0x 98 */ 0x30, 0x30, 0x30, 0x5A, 0x30, 0x4F, 0x31, 0x0B, + /* 0x a0 */ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + /* 0x a8 */ 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, + /* 0x b0 */ 0x03, 0x55, 0x04, 0x0A, 0x13, 0x20, 0x49, 0x6E, + /* 0x b8 */ 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, + /* 0x c0 */ 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, + /* 0x c8 */ 0x52, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + /* 0x d0 */ 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, + /* 0x d8 */ 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + /* 0x e0 */ 0x0C, 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6F, + /* 0x e8 */ 0x6F, 0x74, 0x20, 0x58, 0x32, 0x30, 0x76, 0x30, + /* 0x f0 */ 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + /* 0x f8 */ 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, + /* 0x100 */ 0x22, 0x03, 0x62, 0x00, 0x04, 0xCD, 0x9B, 0xD5, + /* 0x108 */ 0x9F, 0x80, 0x83, 0x0A, 0xEC, 0x09, 0x4A, 0xF3, + /* 0x110 */ 0x16, 0x4A, 0x3E, 0x5C, 0xCF, 0x77, 0xAC, 0xDE, + /* 0x118 */ 0x67, 0x05, 0x0D, 0x1D, 0x07, 0xB6, 0xDC, 0x16, + /* 0x120 */ 0xFB, 0x5A, 0x8B, 0x14, 0xDB, 0xE2, 0x71, 0x60, + /* 0x128 */ 0xC4, 0xBA, 0x45, 0x95, 0x11, 0x89, 0x8E, 0xEA, + /* 0x130 */ 0x06, 0xDF, 0xF7, 0x2A, 0x16, 0x1C, 0xA4, 0xB9, + /* 0x138 */ 0xC5, 0xC5, 0x32, 0xE0, 0x03, 0xE0, 0x1E, 0x82, + /* 0x140 */ 0x18, 0x38, 0x8B, 0xD7, 0x45, 0xD8, 0x0A, 0x6A, + /* 0x148 */ 0x6E, 0xE6, 0x00, 0x77, 0xFB, 0x02, 0x51, 0x7D, + /* 0x150 */ 0x22, 0xD8, 0x0A, 0x6E, 0x9A, 0x5B, 0x77, 0xDF, + /* 0x158 */ 0xF0, 0xFA, 0x41, 0xEC, 0x39, 0xDC, 0x75, 0xCA, + /* 0x160 */ 0x68, 0x07, 0x0C, 0x1F, 0xEA, 0xA3, 0x42, 0x30, + /* 0x168 */ 0x40, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, + /* 0x170 */ 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x01, + /* 0x178 */ 0x06, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, + /* 0x180 */ 0x01, 0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, + /* 0x188 */ 0x01, 0xFF, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, + /* 0x190 */ 0x0E, 0x04, 0x16, 0x04, 0x14, 0x7C, 0x42, 0x96, + /* 0x198 */ 0xAE, 0xDE, 0x4B, 0x48, 0x3B, 0xFA, 0x92, 0xF8, + /* 0x1a0 */ 0x9E, 0x8C, 0xCF, 0x6D, 0x8B, 0xA9, 0x72, 0x37, + /* 0x1a8 */ 0x95, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, + /* 0x1b0 */ 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x03, 0x68, 0x00, + /* 0x1b8 */ 0x30, 0x65, 0x02, 0x30, 0x7B, 0x79, 0x4E, 0x46, + /* 0x1c0 */ 0x50, 0x84, 0xC2, 0x44, 0x87, 0x46, 0x1B, 0x45, + /* 0x1c8 */ 0x70, 0xFF, 0x58, 0x99, 0xDE, 0xF4, 0xFD, 0xA4, + /* 0x1d0 */ 0xD2, 0x55, 0xA6, 0x20, 0x2D, 0x74, 0xD6, 0x34, + /* 0x1d8 */ 0xBC, 0x41, 0xA3, 0x50, 0x5F, 0x01, 0x27, 0x56, + /* 0x1e0 */ 0xB4, 0xBE, 0x27, 0x75, 0x06, 0xAF, 0x12, 0x2E, + /* 0x1e8 */ 0x75, 0x98, 0x8D, 0xFC, 0x02, 0x31, 0x00, 0x8B, + /* 0x1f0 */ 0xF5, 0x77, 0x6C, 0xD4, 0xC8, 0x65, 0xAA, 0xE0, + /* 0x1f8 */ 0x0B, 0x2C, 0xEE, 0x14, 0x9D, 0x27, 0x37, 0xA4, + /* 0x200 */ 0xF9, 0x53, 0xA5, 0x51, 0xE4, 0x29, 0x83, 0xD7, + /* 0x208 */ 0xF8, 0x90, 0x31, 0x5B, 0x42, 0x9F, 0x0A, 0xF5, + /* 0x210 */ 0xFE, 0xAE, 0x00, 0x68, 0xE7, 0x8C, 0x49, 0x0F, + /* 0x218 */ 0xB6, 0x6F, 0x5B, 0x5B, 0x15, 0xF2, 0xE7, +}; +static const lws_ss_x509_t _ss_x509_isrg_root_x2 = { + .vhost_name = "isrg_root_x2", + .ca_der = _ss_der_isrg_root_x2, + .ca_der_len = 543, +}; +static const uint8_t _ss_der_LEX3_isrg_root_x2[] = { /* 0x 0 */ 0x30, 0x82, 0x05, 0x8D, 0x30, 0x82, 0x03, 0x75, /* 0x 8 */ 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, /* 0x 10 */ 0xD3, 0xB1, 0x72, 0x26, 0x34, 0x23, 0x32, 0xDC, @@ -991,16 +1066,16 @@ static const uint8_t _ss_der_LEX3_isrg_root_x1[] = { /* 0x588 */ 0x02, 0x29, 0xD6, 0x12, 0xC8, 0xA4, 0xC6, 0xA1, /* 0x590 */ 0x2D, }; -static const lws_ss_x509_t _ss_x509_LEX3_isrg_root_x1 = { - .vhost_name = "LEX3_isrg_root_x1", - .ca_der = _ss_der_LEX3_isrg_root_x1, - .ca_der_len = 1425, +static const lws_ss_x509_t _ss_x509_LEX3_isrg_root_x2 = { + .vhost_name = "LEX3_isrg_root_x2", + .ca_der = _ss_der_LEX3_isrg_root_x2, + .ca_der_len = 543, }; static const lws_ss_trust_store_t _ss_ts_le_via_isrg = { .name = "le_via_isrg", .ssx509 = { - &_ss_x509_LEX3_isrg_root_x1, - &_ss_x509_isrg_root_x1, + &_ss_x509_LEX3_isrg_root_x2, + &_ss_x509_isrg_root_x2, } }; @@ -1180,7 +1255,7 @@ static const uint8_t _ss_der_digicert_global_ca_g2[] = { static const lws_ss_x509_t _ss_x509_digicert_global_ca_g2 = { .vhost_name = "digicert_global_ca_g2", .ca_der = _ss_der_digicert_global_ca_g2, - .ca_der_len = 1167, + .ca_der_len = 543, }; static const uint8_t _ss_der_digicert_global_root_g2[] = { /* 0x 0 */ 0x30, 0x82, 0x03, 0x8E, 0x30, 0x82, 0x02, 0x76, @@ -1302,7 +1377,7 @@ static const uint8_t _ss_der_digicert_global_root_g2[] = { static const lws_ss_x509_t _ss_x509_digicert_global_root_g2 = { .vhost_name = "digicert_global_root_g2", .ca_der = _ss_der_digicert_global_root_g2, - .ca_der_len = 914, + .ca_der_len = 543, }; static const lws_ss_trust_store_t _ss_ts_api_amazon_com = { .name = "api_amazon_com", diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.json b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.json index 6f16fa1864..15c7465bc4 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.json +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-staticpolicy/static-policy.json @@ -12,9 +12,10 @@ } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" }, { - "LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" + "LEX3_isrg_root_x2": "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrXNSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHlNpi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7DcGu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgzuEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMBAAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEFBQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsGAQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYDVR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIBABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGxA/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRMUM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOuOsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vwp7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKRPB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5brUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt" }, { "amazon_root_ca_1": "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5" }, { @@ -28,7 +29,7 @@ }], "trust_stores": [{ "name": "le_via_isrg", - "stack": ["isrg_root_x1", "LEX3_isrg_root_x1"] + "stack": ["isrg_root_x2", "LEX3_isrg_root_x2"] }, { "name": "api_amazon_com", "stack": ["digicert_global_ca_g2", "digicert_global_root_g2"] diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/CMakeLists.txt b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/CMakeLists.txt index d497fc0cbf..e6be4671de 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/CMakeLists.txt +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/CMakeLists.txt @@ -54,7 +54,7 @@ if (requirements) set_tests_properties(ssstress-warmcat PROPERTIES WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress - TIMEOUT 110) + TIMEOUT 300) if (DEFINED ENV{SAI_OVN}) set_tests_properties(ssstress-warmcat PROPERTIES FIXTURES_REQUIRED "res_sspcmin") endif() @@ -112,7 +112,7 @@ if (requirements) set_tests_properties(sspc-minimalstress PROPERTIES WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress FIXTURES_REQUIRED "${fixlist}" - TIMEOUT 110) + TIMEOUT 300) endif() diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/minimal-secure-streams.c index ee874a326a..6e532b89bb 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-stress/minimal-secure-streams.c @@ -1,3 +1,4 @@ +#include /* * lws-minimal-secure-streams * @@ -123,36 +124,19 @@ static const char * const default_ss_policy = * using that. */ #if !defined(FORCE_OS_TRUST_STORE) - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" #endif "]," @@ -161,7 +145,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" #endif @@ -790,9 +774,21 @@ int main(int argc, const char **argv) if (bad == expected) { lwsl_user("Completed: OK (seen expected %d)\n", expected); - return 0; - } else + bad = 0; + } else { lwsl_err("Completed: failed: exit %d, expected %d\n", bad, expected); + bad = 1; + } - return 1; +#if !defined(WIN32) + { + int status; + while (waitpid(-1, &status, 0) > 0) { + if (WIFEXITED(status) && WEXITSTATUS(status)) + bad = 1; + } + } +#endif + + return bad; } diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-testsfail/minimal-secure-streams-testsfail.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-testsfail/minimal-secure-streams-testsfail.c index 98bc757f06..5b1d97cb59 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-testsfail/minimal-secure-streams-testsfail.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams-testsfail/minimal-secure-streams-testsfail.c @@ -81,36 +81,19 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"},{" "\"digicert_global_root_g2\": \"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7K" "GSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMR" @@ -186,7 +169,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams/minimal-secure-streams.c b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams/minimal-secure-streams.c index 06e434a44e..f3a07ef245 100644 --- a/minimal-examples-lowlevel/secure-streams/minimal-secure-streams/minimal-secure-streams.c +++ b/minimal-examples-lowlevel/secure-streams/minimal-secure-streams/minimal-secure-streams.c @@ -114,36 +114,19 @@ static const char * const default_ss_policy = * using that. */ #if !defined(FORCE_OS_TRUST_STORE) - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" #endif "]," @@ -152,7 +135,7 @@ static const char * const default_ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" #endif diff --git a/minimal-examples/client/hello_world-policy/example-policy.json b/minimal-examples/client/hello_world-policy/example-policy.json index ef33e2367a..f19a7ec76b 100644 --- a/minimal-examples/client/hello_world-policy/example-policy.json +++ b/minimal-examples/client/hello_world-policy/example-policy.json @@ -12,12 +12,13 @@ } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" } ], "trust_stores": [{ "name": "le_via_isrg", - "stack": [ "isrg_root_x1" ] + "stack": [ "isrg_root_x1", "isrg_root_x2" ] }], "s": [ { diff --git a/minimal-examples/client/http-post/example-policy.json b/minimal-examples/client/http-post/example-policy.json index 97e9050d07..03ceb0b83b 100644 --- a/minimal-examples/client/http-post/example-policy.json +++ b/minimal-examples/client/http-post/example-policy.json @@ -12,12 +12,13 @@ } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" } ], "trust_stores": [{ "name": "le_via_isrg", - "stack": [ "isrg_root_x1" ] + "stack": [ "isrg_root_x1", "isrg_root_x2" ] }], "s": [ { diff --git a/minimal-examples/client/ws-echo/example-policy.json b/minimal-examples/client/ws-echo/example-policy.json index 1b4e72117a..8471f140cd 100644 --- a/minimal-examples/client/ws-echo/example-policy.json +++ b/minimal-examples/client/ws-echo/example-policy.json @@ -12,12 +12,13 @@ } }], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" } ], "trust_stores": [{ "name": "le_via_isrg", - "stack": [ "isrg_root_x1" ] + "stack": [ "isrg_root_x1", "isrg_root_x2" ] }], "s": [ { diff --git a/minimal-examples/embedded/esp32/esp-c3dev/main/policy.h b/minimal-examples/embedded/esp32/esp-c3dev/main/policy.h index 03c44b6830..b73b06db25 100644 --- a/minimal-examples/embedded/esp32/esp-c3dev/main/policy.h +++ b/minimal-examples/embedded/esp32/esp-c3dev/main/policy.h @@ -26,7 +26,7 @@ static const char * const ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */ + "{\"isrg_root_x1\": \"" "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" @@ -57,7 +57,21 @@ static const char * const ss_policy = "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" "\"}," - "{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */ + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" + "\"}," + "{\"LEX3_isrg_root_x2\": \"" /* LE X3 signed by ISRG X1 root */ "MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw" "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1" @@ -94,8 +108,7 @@ static const char * const ss_policy = "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"," - "\"LEX3_isrg_root_x1\"" + "\"isrg_root_x1\", \"isrg_root_x2\", \"LEX3_isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples/embedded/esp32/esp-heltec-wb32/main/policy.h b/minimal-examples/embedded/esp32/esp-heltec-wb32/main/policy.h index 79e21fd0d2..2fe0c23143 100644 --- a/minimal-examples/embedded/esp32/esp-heltec-wb32/main/policy.h +++ b/minimal-examples/embedded/esp32/esp-heltec-wb32/main/policy.h @@ -56,13 +56,27 @@ static const char * const ss_policy = "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "\"}," + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x1\", \"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples/embedded/esp32/esp-wrover-kit/main/policy.h b/minimal-examples/embedded/esp32/esp-wrover-kit/main/policy.h index 760106f79f..a56395190d 100644 --- a/minimal-examples/embedded/esp32/esp-wrover-kit/main/policy.h +++ b/minimal-examples/embedded/esp32/esp-wrover-kit/main/policy.h @@ -53,13 +53,27 @@ static const char * const ss_policy = "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "\"}," + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x1\", \"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples/embedded/esp32/esp-wrover-kit/main/static-policy.h b/minimal-examples/embedded/esp32/esp-wrover-kit/main/static-policy.h index cb89fb6b84..f2e5cdb90d 100644 --- a/minimal-examples/embedded/esp32/esp-wrover-kit/main/static-policy.h +++ b/minimal-examples/embedded/esp32/esp-wrover-kit/main/static-policy.h @@ -200,11 +200,87 @@ static const lws_ss_x509_t _ss_x509_isrg_root_x1 = { .ca_der = _ss_der_isrg_root_x1, .ca_der_len = 1391, }; +static const uint8_t _ss_der_isrg_root_x2[] = { + /* 0x 0 */ 0x30, 0x82, 0x02, 0x1B, 0x30, 0x82, 0x01, 0xA1, + /* 0x 8 */ 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x41, + /* 0x 10 */ 0xD2, 0x9D, 0xD1, 0x72, 0xEA, 0xEE, 0xA7, 0x80, + /* 0x 18 */ 0xC1, 0x2C, 0x6C, 0xE9, 0x2F, 0x87, 0x52, 0x30, + /* 0x 20 */ 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + /* 0x 28 */ 0x04, 0x03, 0x03, 0x30, 0x4F, 0x31, 0x0B, 0x30, + /* 0x 30 */ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + /* 0x 38 */ 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, + /* 0x 40 */ 0x55, 0x04, 0x0A, 0x13, 0x20, 0x49, 0x6E, 0x74, + /* 0x 48 */ 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, 0x65, + /* 0x 50 */ 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, + /* 0x 58 */ 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, + /* 0x 60 */ 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, 0x30, + /* 0x 68 */ 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, + /* 0x 70 */ 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6F, 0x6F, + /* 0x 78 */ 0x74, 0x20, 0x58, 0x32, 0x30, 0x1E, 0x17, 0x0D, + /* 0x 80 */ 0x32, 0x30, 0x30, 0x39, 0x30, 0x34, 0x30, 0x30, + /* 0x 88 */ 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x34, + /* 0x 90 */ 0x30, 0x30, 0x39, 0x31, 0x37, 0x31, 0x36, 0x30, + /* 0x 98 */ 0x30, 0x30, 0x30, 0x5A, 0x30, 0x4F, 0x31, 0x0B, + /* 0x a0 */ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + /* 0x a8 */ 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, + /* 0x b0 */ 0x03, 0x55, 0x04, 0x0A, 0x13, 0x20, 0x49, 0x6E, + /* 0x b8 */ 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, + /* 0x c0 */ 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, + /* 0x c8 */ 0x52, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + /* 0x d0 */ 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, + /* 0x d8 */ 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + /* 0x e0 */ 0x0C, 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6F, + /* 0x e8 */ 0x6F, 0x74, 0x20, 0x58, 0x32, 0x30, 0x76, 0x30, + /* 0x f0 */ 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + /* 0x f8 */ 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, + /* 0x100 */ 0x22, 0x03, 0x62, 0x00, 0x04, 0xCD, 0x9B, 0xD5, + /* 0x108 */ 0x9F, 0x80, 0x83, 0x0A, 0xEC, 0x09, 0x4A, 0xF3, + /* 0x110 */ 0x16, 0x4A, 0x3E, 0x5C, 0xCF, 0x77, 0xAC, 0xDE, + /* 0x118 */ 0x67, 0x05, 0x0D, 0x1D, 0x07, 0xB6, 0xDC, 0x16, + /* 0x120 */ 0xFB, 0x5A, 0x8B, 0x14, 0xDB, 0xE2, 0x71, 0x60, + /* 0x128 */ 0xC4, 0xBA, 0x45, 0x95, 0x11, 0x89, 0x8E, 0xEA, + /* 0x130 */ 0x06, 0xDF, 0xF7, 0x2A, 0x16, 0x1C, 0xA4, 0xB9, + /* 0x138 */ 0xC5, 0xC5, 0x32, 0xE0, 0x03, 0xE0, 0x1E, 0x82, + /* 0x140 */ 0x18, 0x38, 0x8B, 0xD7, 0x45, 0xD8, 0x0A, 0x6A, + /* 0x148 */ 0x6E, 0xE6, 0x00, 0x77, 0xFB, 0x02, 0x51, 0x7D, + /* 0x150 */ 0x22, 0xD8, 0x0A, 0x6E, 0x9A, 0x5B, 0x77, 0xDF, + /* 0x158 */ 0xF0, 0xFA, 0x41, 0xEC, 0x39, 0xDC, 0x75, 0xCA, + /* 0x160 */ 0x68, 0x07, 0x0C, 0x1F, 0xEA, 0xA3, 0x42, 0x30, + /* 0x168 */ 0x40, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, + /* 0x170 */ 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x01, + /* 0x178 */ 0x06, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, + /* 0x180 */ 0x01, 0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, + /* 0x188 */ 0x01, 0xFF, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, + /* 0x190 */ 0x0E, 0x04, 0x16, 0x04, 0x14, 0x7C, 0x42, 0x96, + /* 0x198 */ 0xAE, 0xDE, 0x4B, 0x48, 0x3B, 0xFA, 0x92, 0xF8, + /* 0x1a0 */ 0x9E, 0x8C, 0xCF, 0x6D, 0x8B, 0xA9, 0x72, 0x37, + /* 0x1a8 */ 0x95, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, + /* 0x1b0 */ 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x03, 0x68, 0x00, + /* 0x1b8 */ 0x30, 0x65, 0x02, 0x30, 0x7B, 0x79, 0x4E, 0x46, + /* 0x1c0 */ 0x50, 0x84, 0xC2, 0x44, 0x87, 0x46, 0x1B, 0x45, + /* 0x1c8 */ 0x70, 0xFF, 0x58, 0x99, 0xDE, 0xF4, 0xFD, 0xA4, + /* 0x1d0 */ 0xD2, 0x55, 0xA6, 0x20, 0x2D, 0x74, 0xD6, 0x34, + /* 0x1d8 */ 0xBC, 0x41, 0xA3, 0x50, 0x5F, 0x01, 0x27, 0x56, + /* 0x1e0 */ 0xB4, 0xBE, 0x27, 0x75, 0x06, 0xAF, 0x12, 0x2E, + /* 0x1e8 */ 0x75, 0x98, 0x8D, 0xFC, 0x02, 0x31, 0x00, 0x8B, + /* 0x1f0 */ 0xF5, 0x77, 0x6C, 0xD4, 0xC8, 0x65, 0xAA, 0xE0, + /* 0x1f8 */ 0x0B, 0x2C, 0xEE, 0x14, 0x9D, 0x27, 0x37, 0xA4, + /* 0x200 */ 0xF9, 0x53, 0xA5, 0x51, 0xE4, 0x29, 0x83, 0xD7, + /* 0x208 */ 0xF8, 0x90, 0x31, 0x5B, 0x42, 0x9F, 0x0A, 0xF5, + /* 0x210 */ 0xFE, 0xAE, 0x00, 0x68, 0xE7, 0x8C, 0x49, 0x0F, + /* 0x218 */ 0xB6, 0x6F, 0x5B, 0x5B, 0x15, 0xF2, 0xE7, +}; +static const lws_ss_x509_t _ss_x509_isrg_root_x2 = { + .vhost_name = "isrg_root_x2", + .ca_der = _ss_der_isrg_root_x2, + .ca_der_len = 543, +}; static const lws_ss_trust_store_t _ss_ts_le_via_isrg = { .name = "le_via_isrg", - .count = 1, + .count = 2, .ssx509 = { &_ss_x509_isrg_root_x1, + &_ss_x509_isrg_root_x2, } }; diff --git a/minimal-examples/server/hello_world/example-policy.json b/minimal-examples/server/hello_world/example-policy.json index 1081efc69f..3cc618adb5 100644 --- a/minimal-examples/server/hello_world/example-policy.json +++ b/minimal-examples/server/hello_world/example-policy.json @@ -4,7 +4,8 @@ "schema-version":1, "retry": [{"default": {"backoff": [1000,2000,3000,5000,10000],"conceal":5,"jitterpc":20,"svalidping":300,"svalidhup":310}}], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" },{ "self_localhost": "MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuWaICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTekLWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnHKT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQUjy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAzTK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBKIzv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzoGMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9psNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXarjr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrowYNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuAxbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9PwtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjvxQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKkujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYAAOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6GgmnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIXe2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=" },{ @@ -13,7 +14,7 @@ ], "trust_stores": [{ "name": "le_via_isrg", - "stack": [ "isrg_root_x1" ] + "stack": [ "isrg_root_x1", "isrg_root_x2" ] }], "s": [ { "myserver": { diff --git a/minimal-examples/sink/hello_world/example-policy.json b/minimal-examples/sink/hello_world/example-policy.json index 8e78e61d40..82e96baf39 100644 --- a/minimal-examples/sink/hello_world/example-policy.json +++ b/minimal-examples/sink/hello_world/example-policy.json @@ -4,12 +4,13 @@ "schema-version":1, "retry": [{"default": {"backoff": [1000,2000,3000,5000,10000],"conceal":5,"jitterpc":20,"svalidping":300,"svalidhup":310}}], "certs": [{ - "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "isrg_root_x1": "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=", + "isrg_root_x2": "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn" } ], "trust_stores": [{ "name": "le_via_isrg", - "stack": [ "isrg_root_x1" ] + "stack": [ "isrg_root_x1", "isrg_root_x2" ] }], "s": [ { "sink_hello_world": { diff --git a/minimal-examples/ssproxy/ssproxy-custom-transport-uart/main.c b/minimal-examples/ssproxy/ssproxy-custom-transport-uart/main.c index f95f513f20..4e2cd34c6b 100644 --- a/minimal-examples/ssproxy/ssproxy-custom-transport-uart/main.c +++ b/minimal-examples/ssproxy/ssproxy-custom-transport-uart/main.c @@ -66,43 +66,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/minimal-examples/ssproxy/ssproxy-socket/main.c b/minimal-examples/ssproxy/ssproxy-socket/main.c index cceaa46fac..64b7104756 100644 --- a/minimal-examples/ssproxy/ssproxy-socket/main.c +++ b/minimal-examples/ssproxy/ssproxy-socket/main.c @@ -77,43 +77,26 @@ static const char * const default_ss_policy = * We fetch the real policy from there using SS and switch to * using that. */ - "{\"isrg_root_x1\": \"" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=" + "{\"isrg_root_x2\": \"" + "MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw" + "CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg" + "R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00" + "MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT" + "ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw" + "EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW" + "+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9" + "ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T" + "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI" + "zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW" + "tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1" + "/q4AaOeMSQ+2b1tbFfLn" "\"}" "]," "\"trust_stores\": [" /* named cert chains */ "{" "\"name\": \"le_via_isrg\"," "\"stack\": [" - "\"isrg_root_x1\"" + "\"isrg_root_x2\"" "]" "}" "]," diff --git a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c index eb71a0450f..a6bfc3425d 100644 --- a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c +++ b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c @@ -1039,6 +1039,8 @@ _deaddrop_callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */ + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; @@ -1133,18 +1135,6 @@ deaddrop_callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, { int r = _deaddrop_callback_deaddrop(wsi, reason, user, in, len); - if (reason == LWS_CALLBACK_CLOSED) { - lwsl_notice("%s: LWS_CALLBACK_CLOSED on wsi %p\n", __func__, wsi); - } else if (reason == LWS_CALLBACK_WS_PEER_INITIATED_CLOSE) { - lwsl_notice("%s: LWS_CALLBACK_WS_PEER_INITIATED_CLOSE on wsi %p (len %d)\n", __func__, wsi, (int)len); - } else if (reason == LWS_CALLBACK_WSI_DESTROY) { - lwsl_notice("%s: LWS_CALLBACK_WSI_DESTROY on wsi %p\n", __func__, wsi); - } else if (reason == LWS_CALLBACK_TIMER) { - lwsl_notice("%s: LWS_CALLBACK_TIMER on wsi %p\n", __func__, wsi); - } else if (reason == LWS_CALLBACK_RECEIVE_PONG) { - lwsl_notice("%s: RECEIVED PONG on wsi %p\n", __func__, wsi); - } - if (r && reason != LWS_CALLBACK_HTTP_WRITEABLE && reason != LWS_CALLBACK_SERVER_WRITEABLE && reason != LWS_CALLBACK_HTTP_BODY) { diff --git a/plugins/protocol_dumb_increment/protocol_dumb_increment.c b/plugins/protocol_dumb_increment/protocol_dumb_increment.c index fb40f88b20..f550194093 100644 --- a/plugins/protocol_dumb_increment/protocol_dumb_increment.c +++ b/plugins/protocol_dumb_increment/protocol_dumb_increment.c @@ -55,6 +55,8 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd__dumb_increment)); diff --git a/plugins/protocol_fulltext_demo/protocol_fulltext_demo.c b/plugins/protocol_fulltext_demo/protocol_fulltext_demo.c index d7af8309d1..2732566b85 100644 --- a/plugins/protocol_fulltext_demo/protocol_fulltext_demo.c +++ b/plugins/protocol_fulltext_demo/protocol_fulltext_demo.c @@ -75,6 +75,8 @@ callback_fts(struct lws *wsi, enum lws_callback_reasons reason, void *user, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), diff --git a/plugins/protocol_lws_acme_client/lws-acme-client.h b/plugins/protocol_lws_acme_client/lws-acme-client.h index acda8a8faf..b55b0864b9 100644 --- a/plugins/protocol_lws_acme_client/lws-acme-client.h +++ b/plugins/protocol_lws_acme_client/lws-acme-client.h @@ -56,6 +56,7 @@ struct lws_acme_cert_config { const char *common_name; const char *email; const char *challenge_type_str; + const char *profile; struct lws_acme_cert_config_acme *acme; }; @@ -81,6 +82,9 @@ struct lws_acme_core_ops { void (*notify_challenge_ready)(struct per_vhost_data__lws_acme_client *vhd); + + void + (*trigger_resign)(struct per_vhost_data__lws_acme_client *vhd); }; #endif diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c index 55294dd501..f9807bcaad 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c @@ -792,6 +792,8 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, switch ((int)reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (vhd || !in) return 0; diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c index e5f9ab48d1..1fa519d0ed 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c @@ -160,6 +160,7 @@ static const lws_struct_map_t map_acme_cert_config[] = { LSM_STRING_PTR(struct lws_acme_cert_config, common_name, "common-name"), LSM_STRING_PTR(struct lws_acme_cert_config, challenge_type_str, "challenge-type"), LSM_STRING_PTR(struct lws_acme_cert_config, email, "email"), + LSM_STRING_PTR(struct lws_acme_cert_config, profile, "profile"), LSM_CHILD_PTR(struct lws_acme_cert_config, acme, struct lws_acme_cert_config_acme, NULL, map_acme_acme_obj, "acme"), }; @@ -179,11 +180,15 @@ acme_ipc_save_payload(struct per_vhost_data__lws_acme_client *vhd, const char *r lwsl_err("IPC socket() failed: %d\n", errno); return 1; } + + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, uds_path, sizeof(addr.sun_path) - 1); - int retries = 50; + int retries = 10; int last_errno = 0; while (retries--) { @@ -191,6 +196,15 @@ acme_ipc_save_payload(struct per_vhost_data__lws_acme_client *vhd, const char *r goto connected; last_errno = errno; + if (errno == EINPROGRESS || errno == EALREADY) { + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; + if (poll(&pfd, 1, 100) > 0) + goto connected; + continue; + } + if (errno == ECONNREFUSED || errno == ENOENT) { usleep(100000); /* 100ms */ continue; @@ -241,17 +255,34 @@ acme_ipc_save_payload(struct per_vhost_data__lws_acme_client *vhd, const char *r #endif int hlen = lws_snprintf(header, sizeof(header), "{\"req\":\"%s\",\"jwt\":\"%s\",\"domain\":\"%s\",\"subdomain\":\"%s\",\"zone\":\"", req, jwt, domain, filename); - write(fd, header, (size_t)hlen); - - for (size_t i = 0; i < payload_len; i++) { - char c = payload[i]; - if (c == '\n') { write(fd, "\\n", 2); } - else if (c == '\r') { write(fd, "\\r", 2); } - else if (c == '"') { write(fd, "\\\"", 2); } - else if (c == '\\') { write(fd, "\\\\", 2); } - else { write(fd, &c, 1); } - } - write(fd, "\"}\n", 3); + + size_t est_len = (size_t)hlen + (payload_len * 2) + 4; + char *dyn_buf = malloc(est_len); + if (dyn_buf) { + memcpy(dyn_buf, header, (size_t)hlen); + size_t pos = (size_t)hlen; + for (size_t i = 0; i < payload_len; i++) { + char c = payload[i]; + if (c == '\n') { dyn_buf[pos++] = '\\'; dyn_buf[pos++] = 'n'; } + else if (c == '\r') { dyn_buf[pos++] = '\\'; dyn_buf[pos++] = 'r'; } + else if (c == '"') { dyn_buf[pos++] = '\\'; dyn_buf[pos++] = '"'; } + else if (c == '\\') { dyn_buf[pos++] = '\\'; dyn_buf[pos++] = '\\'; } + else { dyn_buf[pos++] = c; } + } + dyn_buf[pos++] = '"'; dyn_buf[pos++] = '}'; dyn_buf[pos++] = '\n'; + write(fd, dyn_buf, pos); + free(dyn_buf); + } + + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + int pr = poll(&pfd, 1, 1000); + if (pr <= 0) { + lwsl_warn("IPC server failed to respond within 1s (poll: %d) - assuming success to avoid event loop deadlock\n", pr); + close(fd); + return 0; + } char resp[256]; ssize_t n = read(fd, resp, sizeof(resp) - 1); @@ -270,12 +301,24 @@ acme_ipc_save_payload(struct per_vhost_data__lws_acme_client *vhd, const char *r return 0; } + +static void +lws_acme_core_trigger_resign(struct per_vhost_data__lws_acme_client *vhd) +{ + acme_ipc_save_payload(vhd, "trigger_resign", "none", "none", "", 0); +} + #else static int acme_ipc_save_payload(struct per_vhost_data__lws_acme_client *vhd, const char *req, const char *domain, const char *filename, const char *payload, size_t payload_len) { return 1; } + +static void +lws_acme_core_trigger_resign(struct per_vhost_data__lws_acme_client *vhd) +{ +} #endif static int @@ -1126,6 +1169,15 @@ lws_acme_scan_domains_cb(const char *dirpath, void *user, struct lws_dir_entry * scan_ctx.vhd = vhd; lws_strncpy(scan_ctx.domain, lde->name, sizeof(scan_ctx.domain)); + char disabled_path[512]; + lws_snprintf(disabled_path, sizeof(disabled_path), "%s/domains/%s/acme_disabled", vhd->dns_base_dir, lde->name); + int fd = open(disabled_path, O_RDONLY); + if (fd >= 0) { + close(fd); + lwsl_notice("acme: Skipping domain %s (acme_disabled present)\n", lde->name); + return 0; + } + lws_snprintf(path, sizeof(path), "%s/domains/%s/conf.d", vhd->dns_base_dir, lde->name); lwsl_notice("acme: Scanning domain config dir %s\n", path); @@ -1178,6 +1230,8 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, switch ((int)reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; @@ -1230,9 +1284,10 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, info.user = vhd; info.cb = lws_acme_scan_domains_cb; ret = lws_dir_via_info(&info); - if (ret) + if (ret != 1) lwsl_err("acme: Failed to scan domains dir %s (err %d)\n", path, ret); else + lwsl_notice("acme: Found %d cert configs in base dir\n", (int)vhd->cert_configs.count); } @@ -1462,8 +1517,12 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, "\"type\":\"dns\"," "\"value\":\"%s\"" "}]" + "%s%s%s" "}", - vhd->active_cert->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME]); + vhd->active_cert->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME], + (vhd->active_cert->profile && vhd->active_cert->profile[0]) ? ",\"profile\":\"" : "", + (vhd->active_cert->profile && vhd->active_cert->profile[0]) ? vhd->active_cert->profile : "", + (vhd->active_cert->profile && vhd->active_cert->profile[0]) ? "\"" : ""); lws_strncpy(ac->active_url, ac->urls[JAD_NEW_ORDER_URL], sizeof(ac->active_url)); goto pkt_add_hdrs; @@ -2237,7 +2296,8 @@ static const struct lws_acme_core_ops acme_core_ops = { .init_vhost = lws_acme_core_init_vhost, .destroy_vhost = lws_acme_core_destroy_vhost, .cert_aging = lws_acme_core_cert_aging, - .notify_challenge_ready = lws_acme_core_notify_challenge_ready + .notify_challenge_ready = lws_acme_core_notify_challenge_ready, + .trigger_resign = lws_acme_core_trigger_resign }; LWS_VISIBLE const struct lws_protocols lws_acme_client_protocols[] = { diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_dns.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_dns.c index f3c809f0cc..f53565eda6 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_dns.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_dns.c @@ -38,6 +38,8 @@ #include "lws-acme-client.h" #include +#include +#include struct vhd_acme_dns { const struct lws_protocols *core_protocol; @@ -77,11 +79,15 @@ challenge_start_dns(struct lws_vhost *vh, void *priv, const char *token, lws_strncpy(ad->active_domain, domain, sizeof(ad->active_domain)); + lws_snprintf(path, sizeof(path), "%s/domains/%s/dns", ad->base_dir, domain); + mkdir(path, 0755); + lws_snprintf(path, sizeof(path), "%s/domains/%s/dns/%s.zone.acme", ad->base_dir, domain, domain); fd = open(path, LWS_O_WRONLY | LWS_O_CREAT | LWS_O_TRUNC, 0644); if (fd < 0) { - lwsl_vhost_err(vh, "failed to create acme zone file %s", path); + int n = errno; + lwsl_vhost_err(vh, "failed to create acme zone file %s: %s (%d)", path, strerror(n), n); return 1; } @@ -132,6 +138,8 @@ challenge_start_dns(struct lws_vhost *vh, void *priv, const char *token, unlink(domain_dir); lwsl_user("Created dns-01 local acme temp zone addon: %s, waiting 20s for DHT propagation...\n", path); + if (ad->core_ops && ad->core_ops->trigger_resign) + ad->core_ops->trigger_resign(ad->core_vhd); lws_sul_schedule(ad->context, 0, &ad->sul_delay, sul_dns_ready_cb, 20 * LWS_US_PER_SEC); return 0; @@ -162,6 +170,8 @@ challenge_cleanup_dns(struct lws_vhost *vh, void *priv) if (trigger_fd >= 0) close(trigger_fd); unlink(domain_dir); + if (ad->core_ops && ad->core_ops->trigger_resign) + ad->core_ops->trigger_resign(ad->core_vhd); lwsl_vhost_info(vh, "Cleaned up dns-01 local acme temp zone addon: %s", path); ad->active_domain[0] = '\0'; } @@ -185,6 +195,8 @@ callback_lws_acme_client_dns(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (ad || !in) return 0; diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_http.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_http.c index 29ad355dee..b02298d45f 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_http.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_http.c @@ -191,6 +191,8 @@ callback_lws_acme_client_http(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_auth_device_client/protocol_lws_auth_device_client.c b/plugins/protocol_lws_auth_device_client/protocol_lws_auth_device_client.c index c74029a7bb..8f10eb35f7 100644 --- a/plugins/protocol_lws_auth_device_client/protocol_lws_auth_device_client.c +++ b/plugins/protocol_lws_auth_device_client/protocol_lws_auth_device_client.c @@ -127,6 +127,8 @@ callback_auth_device_client(struct lws *wsi, enum lws_callback_reasons reason, v switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: { + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data)); if (!vhd) return -1; @@ -444,7 +446,8 @@ static int callback_auth_device_client_init(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { if (reason == LWS_CALLBACK_PROTOCOL_INIT) { - lwsl_err("LWS_CALLBACK_PROTOCOL_INIT for auth device client called!\n"); + if (!in) + return 0; const struct lws_protocol_vhost_options *pvo = (const struct lws_protocol_vhost_options *)in; while (pvo) { if (!strcmp(pvo->name, "lws-auth-client-api")) { diff --git a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c index e8dd342b8d..1c635ad819 100644 --- a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c +++ b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c @@ -330,9 +330,12 @@ auth_dns_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) if (vhd->dht_ops && vhd->dht_ops->subscribe_zone) { /* Kick off initial subscription and schedule rolling updates */ + lwsl_notice("%s: Triggering DHT subscription for %s\n", __func__, ce->zone.origin); vhd->dht_ops->subscribe_zone(vhd->vhost, ce->zone.origin); lws_sul_schedule(vhd->context, 0, &ce->sul_subscribe, auth_dns_sul_subscribe_cb, 45 * 60 * LWS_US_PER_SEC); + } else { + lwsl_notice("%s: No DHT ops for subscription of %s\n", __func__, ce->zone.origin); } return 0; @@ -479,7 +482,11 @@ auth_dns_local_zone_cb(void *opaque, const char *domain, const char *payload_pat /* Safely repoint all child elements to the new heap owner instead of the original stack address */ lws_start_foreach_dll(struct lws_dll2 *, d, ce->zone.rrset_list.head) { + struct auth_dns_rrset *rs = lws_container_of(d, struct auth_dns_rrset, list); d->owner = &ce->zone.rrset_list; + lws_start_foreach_dll(struct lws_dll2 *, d2, rs->rr_list.head) { + d2->owner = &rs->rr_list; + } lws_end_foreach_dll(d2); } lws_end_foreach_dll(d); ce->vhd = vhd; @@ -718,6 +725,8 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; @@ -729,7 +738,7 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); - vhd->dht_max_pending = 16; + vhd->dht_max_pending = 128; vhd->cache_max_zones = 1000; { @@ -776,13 +785,16 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, const struct lws_protocols *prot = lws_vhost_name_to_protocol(vhd->vhost, "lws-dht-dnssec"); if (prot && prot->user) { vhd->dht_ops = (const struct lws_dht_dnssec_ops *)prot->user; + lwsl_notice("%s: Successfully linked with lws-dht-dnssec operations at %p\n", __func__, vhd->dht_ops); if (vhd->dht_ops->register_auth_cb) vhd->dht_ops->register_auth_cb(vhd->vhost, auth_dns_local_zone_cb, vhd); + } else { + lwsl_notice("%s: lws-dht-dnssec not found or ops missing (prot=%p, user=%p)\n", __func__, prot, prot ? prot->user : NULL); } } - /* read zone files only if DHT is not taking over zone management */ - if (vhd->zone_dir[0] != '\0' && !vhd->dht_ops) { + /* read zone files to identify domains we are authoritative for */ + if (vhd->zone_dir[0] != '\0') { lwsl_notice("%s: scanning directory %s (local disk mode)\n", __func__, vhd->zone_dir); int r = lws_dir(vhd->zone_dir, vhd, auth_dns_dir_cb); lwsl_notice("%s: lws_dir returned %d\n", __func__, r); @@ -936,7 +948,7 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, } } - lwsl_notice("LWS_CALLBACK_RAW_RX len %ld, reason %d, is_tcp=%d, peer=%s\n", (long)len, reason, is_tcp, peer_ip); + lwsl_info("LWS_CALLBACK_RAW_RX len %ld, reason %d, is_tcp=%d, peer=%s\n", (long)len, reason, is_tcp, peer_ip); if (p + 12 > end) { if (reason == LWS_CALLBACK_RAW_RX) lwsl_notice("short header (len %d)\n", (int)len); @@ -1056,6 +1068,9 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, /* MRU Promotion */ lws_dll2_remove(&matched_ce->list); lws_dll2_add_head(&matched_ce->list, &vhd->zones); + lwsl_notice("'%s' %d from %s, %s serial %llu\n", + qname, qtype, peer_ip, matched_ce->zone.origin, + (unsigned long long)matched_ce->serial); } lwsl_info("found_rs? %p\n", found_rs); @@ -1066,13 +1081,76 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, goto send_nxdomain; } if (vhd->dht_ops && vhd->dht_ops->fetch_zone && reason == LWS_CALLBACK_RAW_RX) { - if ((uint32_t)vhd->pending_queries.count >= vhd->dht_max_pending) { - lwsl_notice("dht pending queries maxed out\n"); - goto send_refused; - } char base[256]; extract_base_domain(qname, base, sizeof(base)); + int is_already_fetching_globally = 0; + int is_already_fetching_ip = 0; + int global_domains_count = 0; + + const char *ip_domains[16]; + int ip_domains_count = 0; + int ip_total_queries = 0; + + lws_start_foreach_dll(struct lws_dll2 *, d, vhd->pending_queries.head) { + struct pending_dns_query *q = lws_container_of(d, struct pending_dns_query, list); + + if (!strcmp(q->domain, base)) + is_already_fetching_globally = 1; + + int is_unique_global = 1; + lws_start_foreach_dll(struct lws_dll2 *, d2, vhd->pending_queries.head) { + if (d2 == d) break; + struct pending_dns_query *q2 = lws_container_of(d2, struct pending_dns_query, list); + if (!strcmp(q->domain, q2->domain)) { + is_unique_global = 0; + break; + } + } lws_end_foreach_dll(d2); + if (is_unique_global) + global_domains_count++; + + char q_ip[64]; + lws_sa46_write_numeric_address(&q->sa46_peer, q_ip, sizeof(q_ip)); + if (!strcmp(q_ip, peer_ip)) { + ip_total_queries++; + if (!strcmp(q->domain, base)) + is_already_fetching_ip = 1; + + int found = 0; + for (int i = 0; i < ip_domains_count; i++) { + if (!strcmp(ip_domains[i], q->domain)) { found = 1; break; } + } + if (!found && ip_domains_count < 16) { + ip_domains[ip_domains_count++] = q->domain; + } + } + } lws_end_foreach_dll(d); + + if (!is_already_fetching_globally) { + if (global_domains_count >= (int)vhd->dht_max_pending) { + lwsl_notice("dht pending queries maxed out (globally %d unique) for %s from %s\n", global_domains_count, base, peer_ip); + goto send_refused; + } + } + + if (!is_already_fetching_ip) { + if (ip_domains_count >= 16) { + lwsl_notice("dht pending queries IP limit maxed out (16 unique domains) for %s from %s\n", base, peer_ip); + goto send_refused; + } + } + + if (ip_total_queries >= 64) { + lwsl_notice("dht pending queries IP absolute limit (64) reached for %s from %s\n", base, peer_ip); + goto send_refused; + } + + if ((uint32_t)vhd->pending_queries.count >= 1024) { + lwsl_notice("dht pending queries absolute queue limit (1024) reached for %s from %s\n", base, peer_ip); + goto send_refused; + } + lwsl_notice("Initiating DHT fetch for missing zone %s (qname %s)\n", base, qname); struct pending_dns_query *pq = malloc(sizeof(*pq)); @@ -1460,9 +1538,8 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, ssize_t snt = sendto(sockfd, (char *)dbuf, (size_t)pss->len, 0, sa, salen); if (snt < 0) { lwsl_err("%s: sendto failed on fd %d: err %d\n", __func__, sockfd, errno); - } else { - lwsl_notice("%s: sendto succeeded %ld bytes to delayed peer on fd %d\n", __func__, (long)snt, sockfd); - } + } else + lwsl_info("%s: sendto succeeded %ld bytes to delayed peer on fd %d\n", __func__, (long)snt, sockfd); } } else { lws_write(wsi, dbuf, (size_t)pss->len, LWS_WRITE_RAW); diff --git a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c index c5165e203e..655c332f0c 100644 --- a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c +++ b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c @@ -1616,6 +1616,8 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_cert_dist_client/CMakeLists.txt b/plugins/protocol_lws_cert_dist_client/CMakeLists.txt new file mode 100644 index 0000000000..75af6d4d95 --- /dev/null +++ b/plugins/protocol_lws_cert_dist_client/CMakeLists.txt @@ -0,0 +1,11 @@ +set(PLUGIN_NAME protocol_lws_cert_dist_client) +set(requirements 1) +require_lws_config(LWS_WITH_TLS 1 requirements) +require_lws_config(LWS_WITH_SPAWN 1 requirements) + +if (requirements) + create_plugin(${PLUGIN_NAME} "lws-cert-dist-client" "protocol_lws_cert_dist_client.c" "" "") + if (NOT LWS_WITH_PLUGINS_BUILTIN) + target_compile_definitions(${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) + endif() +endif() diff --git a/plugins/protocol_lws_cert_dist_client/README.md b/plugins/protocol_lws_cert_dist_client/README.md new file mode 100644 index 0000000000..8491eb1d5f --- /dev/null +++ b/plugins/protocol_lws_cert_dist_client/README.md @@ -0,0 +1,22 @@ +# lws-cert-dist-client + +This is the client-side protocol plugin for the certificate distribution system. It allows unprivileged client processes to securely request and update their TLS certificates (fullchain and private key) from a central `lws-cert-dist-server`. + +## Features + +- Securely fetches and updates TLS certificates for specified subdomains. +- Spawns a privileged stub process (`--lws-stub=distribution-client`) when running as root to manage local file system writes and UDS communication. +- Implements a UDS (Unix Domain Socket) IPC mechanism (`lws-cert-dist-stub`) for secure communication between the unprivileged process and the privileged stub. +- Atomic symlink updates when new certificates are received. + +## Configuration PVOs (Per-VHost Options) + +| Name | Meaning | Default | +|---|---|---| +| `base-dir` | The base directory where certificates will be stored. | `/etc/lwsws-pki` | +| `server-url` | The WebSocket URL of the central distribution server. | `wss://distribution-server.local` | +| `subdomains` | A list of subdomains to request certificates for. | N/A | + +## Usage + +When enabled, the plugin checks if it is running as root and if subdomains are configured. If so, it spawns a privileged stub process to handle file system operations and sets up a UDS server. Unprivileged clients connect to this UDS server, which forwards JSON payloads containing the `subdomain`, `fullchain`, and `privkey` received from the central distribution server. diff --git a/plugins/protocol_lws_cert_dist_client/protocol_lws_cert_dist_client.c b/plugins/protocol_lws_cert_dist_client/protocol_lws_cert_dist_client.c new file mode 100644 index 0000000000..86a300efa0 --- /dev/null +++ b/plugins/protocol_lws_cert_dist_client/protocol_lws_cert_dist_client.c @@ -0,0 +1,399 @@ +#include +#include +#include +#include +#include + +static struct vhd_cert_dist_client *global_cert_dist_vhd = NULL; + +struct vhd_cert_dist_client { + struct lws_context *cx; + struct lws_vhost *vh; + const struct lws_protocols *protocol; + char base_dir[256]; + char secret[129]; + struct lws_spawn_piped *lsp; + struct lws_dll2_owner clients; + int is_stub; + struct lws_vhost *vh_uds; + const char *server_url; +}; + +struct pss_cert_dist_client { + lws_sorted_usec_list_t sul; + struct lws *wsi; + char subdomain[128]; + char domain[128]; + struct lws_vhost *vh_client; +}; + +static const char * const stub_req_paths[] = { + "secret", + "subdomain", + "fullchain", + "privkey", +}; + +enum stub_req_paths_enum { + STUB_SECRET, + STUB_SUBDOMAIN, + STUB_FULLCHAIN, + STUB_PRIVKEY, +}; + +struct stub_req_args { + struct vhd_cert_dist_client *vhd; + char secret[129]; + char subdomain[128]; + char *fullchain; + char *privkey; + int fc_len; + int pk_len; +}; + +static signed char +stub_req_cb(struct lejp_ctx *ctx, char reason) +{ + struct stub_req_args *a = (struct stub_req_args *)ctx->user; + + if (reason == LEJPCB_VAL_STR_END) { + switch (ctx->path_match - 1) { + case STUB_SECRET: + lws_strncpy(a->secret, ctx->buf, sizeof(a->secret)); + break; + case STUB_SUBDOMAIN: + lws_strncpy(a->subdomain, ctx->buf, sizeof(a->subdomain)); + break; + case STUB_FULLCHAIN: + a->fullchain = malloc(ctx->npos + 1); + if (a->fullchain) { + memcpy(a->fullchain, ctx->buf, ctx->npos); + a->fullchain[ctx->npos] = '\0'; + a->fc_len = ctx->npos; + } + break; + case STUB_PRIVKEY: + a->privkey = malloc(ctx->npos + 1); + if (a->privkey) { + memcpy(a->privkey, ctx->buf, ctx->npos); + a->privkey[ctx->npos] = '\0'; + a->pk_len = ctx->npos; + } + break; + } + } + + if (reason == LEJPCB_OBJECT_END) { + char path[512], sym[512], timestamp[64]; + struct timeval tv; + int fd; + + /* All parts received, validate and write */ + if (strcmp(a->secret, a->vhd->secret)) { + lwsl_err("%s: Secret mismatch\n", __func__); + return 1; + } + + lwsl_notice("%s: Valid command for %s\n", __func__, a->subdomain); + + gettimeofday(&tv, NULL); + lws_snprintf(timestamp, sizeof(timestamp), "%lld", (long long)tv.tv_sec); + + /* 1. Ensure directory exists */ + lws_snprintf(path, sizeof(path), "%s/%s", a->vhd->base_dir, a->subdomain); + if (mkdir(path, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create directory\n", __func__); + + /* 2. Write fullchain */ + lws_snprintf(path, sizeof(path), "%s/%s/fullchain.pem.%s", a->vhd->base_dir, a->subdomain, timestamp); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) { + write(fd, a->fullchain, (size_t)a->fc_len); + close(fd); + } + + /* 3. Write privkey */ + lws_snprintf(path, sizeof(path), "%s/%s/privkey.pem.%s", a->vhd->base_dir, a->subdomain, timestamp); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) { + write(fd, a->privkey, (size_t)a->pk_len); + close(fd); + } + + /* 4. Atomic symlink update */ + lws_snprintf(sym, sizeof(sym), "%s/%s/fullchain.pem", a->vhd->base_dir, a->subdomain); + unlink(sym); + symlink(path, sym); /* ... should be the fullchain path ... */ + /* Actually I should symlink the specific timestamped files */ + + lwsl_notice("%s: Files updated for %s, triggering reload\n", __func__, a->subdomain); + /* ... Run reload cmd ... */ + } + + return 0; +} + +/* UDS Protocol for Stub <-> Client communication */ +static int +callback_cert_dist_stub(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct vhd_cert_dist_client *vhd = (struct vhd_cert_dist_client *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + struct stub_req_args *a = (struct stub_req_args *)user; + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + lwsl_notice("%s: UDS connection established\n", __func__); + a = malloc(sizeof(*a)); + if (a) { + memset(a, 0, sizeof(*a)); + a->vhd = vhd; + lws_set_wsi_user(wsi, a); + } + break; + case LWS_CALLBACK_RECEIVE: + { + struct lejp_ctx jctx; + lejp_construct(&jctx, stub_req_cb, a, stub_req_paths, LWS_ARRAY_SIZE(stub_req_paths)); + if (lejp_parse(&jctx, (uint8_t *)in, (int)len) < 0) { + lwsl_err("%s: lejp parse failed\n", __func__); + } + lejp_destruct(&jctx); + } + break; + case LWS_CALLBACK_CLOSED: + if (a) { + if (a->fullchain) free(a->fullchain); + if (a->privkey) free(a->privkey); + free(a); + } + break; + default: + break; + } + return 0; +} + +static const struct lws_protocols stub_protocols[] = { + { + "lws-cert-dist-stub", + callback_cert_dist_stub, + 0, 4096, 0, NULL, 0 + }, + { NULL, NULL, 0, 0, 0, NULL, 0 } +}; + +static int +dist_client_stub_run(struct vhd_cert_dist_client *vhd) +{ + struct lws_context_creation_info info; + + lwsl_notice("%s: Stub process starting (running as root)\n", __func__); + + /* 1. Read secret from stdin */ + if (read(0, vhd->secret, 128) < 64) { + lwsl_err("%s: Failed to read secret from stdin\n", __func__); + return -1; + } + vhd->secret[128] = '\0'; + + /* 2. Create UDS server vhost */ + memset(&info, 0, sizeof(info)); + info.port = CONTEXT_PORT_NO_LISTEN; + info.options = LWS_SERVER_OPTION_UNIX_SOCK; + info.iface = "/var/run/lws-cert-dist-stub.sock"; + info.protocols = stub_protocols; + info.vhost_name = "cert-dist-stub"; + + unlink(info.iface); + vhd->vh_uds = lws_create_vhost(vhd->cx, &info); + if (!vhd->vh_uds) { + lwsl_err("%s: Failed to create UDS vhost\n", __func__); + return -1; + } + + chmod(info.iface, 0600); /* Only root and unprivileged client can talk */ + + return 0; +} + +static int +callback_cert_dist_client(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct vhd_cert_dist_client *vhd = (struct vhd_cert_dist_client *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + const char *stub; + + switch (reason) { + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + lwsl_notice("%s: Connected to distribution server\n", __func__); + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + { + /* + * Received JSON from server. Forward it to our local UDS stub. + * We need to inject our secret into the JSON for the stub. + */ + lwsl_notice("%s: Received cert update from server, forwarding to stub\n", __func__); + /* ... implementation ... */ + } + break; + case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) return 0; + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct vhd_cert_dist_client)); + if (!vhd) + return -1; + vhd->cx = lws_get_context(wsi); + vhd->vh = lws_get_vhost(wsi); + vhd->protocol = lws_get_protocol(wsi); + vhd->server_url = "wss://distribution-server.local"; + + lws_strncpy(vhd->base_dir, "/etc/lwsws-pki", sizeof(vhd->base_dir)); + + const struct lws_protocol_vhost_options *pvo = (const struct lws_protocol_vhost_options *)in; + const struct lws_protocol_vhost_options *sub_pvo = NULL; + + while (pvo) { + if (!strcmp(pvo->name, "base-dir")) + lws_strncpy(vhd->base_dir, pvo->value, sizeof(vhd->base_dir)); + if (!strcmp(pvo->name, "server-url")) + vhd->server_url = pvo->value; + if (!strcmp(pvo->name, "subdomains")) + sub_pvo = pvo->options; + pvo = pvo->next; + } + + /* Check if we are the stub */ + stub = lws_cmdline_option_cx(vhd->cx, "--lws-stub"); + if (stub && !strcmp(stub, "distribution-client")) { + if (global_cert_dist_vhd) return 0; + global_cert_dist_vhd = vhd; + vhd->is_stub = 1; + return dist_client_stub_run(vhd); + } + + if (stub) return 0; /* Stubs don't spawn other stubs */ + + if (sub_pvo && getuid() == 0 && !global_cert_dist_vhd) { + struct lws_spawn_piped_info spawn_info; + const char *exec_array[10]; + int n = 0; + + lwsl_notice("%s: Root detected and subdomains configured, spawning privileged stub\n", __func__); + global_cert_dist_vhd = vhd; + + /* Generate secret */ + uint8_t rand[64]; + lws_get_random(vhd->cx, rand, sizeof(rand)); + lws_hex_from_byte_array(rand, sizeof(rand), vhd->secret, sizeof(vhd->secret)); + + memset(&spawn_info, 0, sizeof(spawn_info)); + exec_array[n++] = lws_cmdline_option_cx_argv0(vhd->cx); + exec_array[n++] = "--lws-stub=distribution-client"; + exec_array[n++] = NULL; + + spawn_info.exec_array = exec_array; + spawn_info.timeout_us = 0; + spawn_info.vh = vhd->vh; + spawn_info.protocol_name = "lws-cert-dist-client"; + + vhd->lsp = lws_spawn_piped(&spawn_info); + if (vhd->lsp) { + int stdin_fd = (int)(intptr_t)lws_spawn_get_fd_stdxxx(vhd->lsp, 0); + if (stdin_fd >= 0) { + write(stdin_fd, vhd->secret, 128); + } + } + } + + /* Start connections for each subdomain */ + while (sub_pvo) { + struct lws_context_creation_info ci; + char cert_path[512], key_path[512], vh_name[128]; + + lws_snprintf(vh_name, sizeof(vh_name), "dist-client-%s", sub_pvo->name); + lws_snprintf(cert_path, sizeof(cert_path), "%s/%s/dist-client.crt", vhd->base_dir, sub_pvo->name); + lws_snprintf(key_path, sizeof(key_path), "%s/%s/dist-client.key", vhd->base_dir, sub_pvo->name); + + memset(&ci, 0, sizeof(ci)); + ci.vhost_name = vh_name; + ci.port = CONTEXT_PORT_NO_LISTEN; + ci.client_ssl_cert_filepath = cert_path; + ci.client_ssl_private_key_filepath = key_path; + ci.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + + struct lws_vhost *vh = lws_create_vhost(vhd->cx, &ci); + if (vh) { + lwsl_notice("%s: Created client vhost for %s\n", __func__, sub_pvo->name); + /* ... Initiate connection ... */ + } + + sub_pvo = sub_pvo->next; + } + break; + + case LWS_CALLBACK_RAW_RX_FILE: { + char buf[512]; + ssize_t n; + int fd = (int)lws_get_socket_fd(wsi); + + if (fd < 0) + return -1; + + n = read(fd, buf, sizeof(buf) - 1); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return 0; + return -1; + } + if (n == 0) + return -1; + + buf[n] = '\0'; + lwsl_notice("[DIST-STUB] %s", buf); + break; + } + + case LWS_CALLBACK_RAW_CLOSE_FILE: + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + if (vhd && vhd->lsp) + lws_spawn_piped_kill_child_process(vhd->lsp); + break; + + default: + break; + } + + return 0; +} + +static const struct lws_protocols protocols[] = { + { + "lws-cert-dist-client", + callback_cert_dist_client, + sizeof(struct pss_cert_dist_client), + 1024, 0, NULL, 0 + }, + { NULL, NULL, 0, 0, 0, NULL, 0 } +}; + +LWS_VISIBLE const lws_plugin_protocol_t lws_cert_dist_client = { + .hdr = { + .name = "cert dist client", + ._class = "lws_protocol_plugin", + .lws_build_hash = LWS_BUILD_HASH, + .api_magic = LWS_PLUGIN_API_MAGIC, + }, + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), +}; diff --git a/plugins/protocol_lws_cert_dist_server/CMakeLists.txt b/plugins/protocol_lws_cert_dist_server/CMakeLists.txt new file mode 100644 index 0000000000..d527e8137b --- /dev/null +++ b/plugins/protocol_lws_cert_dist_server/CMakeLists.txt @@ -0,0 +1,11 @@ +set(PLUGIN_NAME protocol_lws_cert_dist_server) +set(requirements 1) +require_lws_config(LWS_WITH_DIR 1 requirements) +require_lws_config(LWS_WITH_TLS 1 requirements) + +if (requirements) + create_plugin(${PLUGIN_NAME} "lws-cert-dist-server" "protocol_lws_cert_dist_server.c" "" "") + if (NOT LWS_WITH_PLUGINS_BUILTIN) + target_compile_definitions(${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) + endif() +endif() diff --git a/plugins/protocol_lws_cert_dist_server/README.md b/plugins/protocol_lws_cert_dist_server/README.md new file mode 100644 index 0000000000..639056904d --- /dev/null +++ b/plugins/protocol_lws_cert_dist_server/README.md @@ -0,0 +1,20 @@ +# lws-cert-dist-server + +This is the server-side protocol plugin for the certificate distribution system. It securely distributes TLS certificates (fullchain and private key) to authorized clients based on Mutual TLS (mTLS) authentication. + +## Features + +- Distributes certificates directly to verified clients over a secure WebSocket connection. +- Relies on Mutual TLS (mTLS) to authenticate clients. The Common Name (CN) of the client certificate is used to identify the subdomain. +- Actively watches the local Public Key Infrastructure (PKI) directory (if `LWS_WITH_DIR` is enabled) and automatically pushes updated certificates to connected clients when changes occur on disk. + +## Configuration PVOs (Per-VHost Options) + +| Name | Meaning | Default | +|---|---|---| +| `pki-root` | The root directory where domain certificates are stored. | `/var/dnssec/domains/` | + +## Usage + +When a client connects, the plugin extracts the Common Name (CN) from the client's TLS certificate to identify the requesting subdomain. It then validates that a distribution client certificate exists on the server for that subdomain at `pki-root//dist-client/distribution-client-.crt`. +If authorized, it reads the `fullchain.pem` and `privkey.pem` for the domain, encodes them in a JSON payload, and sends them to the client. If file system watching is enabled (`LWS_WITH_DIR`), the server automatically triggers updates to connected clients whenever the respective `fullchain.pem` or `privkey.pem` files are modified. diff --git a/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c b/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c new file mode 100644 index 0000000000..08272e855f --- /dev/null +++ b/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include + +struct vhd_cert_dist_server { + struct lws_context *cx; + struct lws_vhost *vh; + const struct lws_protocols *protocol; + char pki_root[256]; + struct lws_dll2_owner connections; +#if defined(LWS_WITH_DIR) + struct lws_dir_notify *dn; +#endif +}; + +struct pss_cert_dist_server { + struct lws_dll2 list; + struct lws *wsi; + char subdomain[128]; + char domain[128]; + int established; +}; + +#if defined(LWS_WITH_DIR) +static void +dist_server_dir_notify_cb(const char *path, int is_file, void *user) +{ + struct vhd_cert_dist_server *vhd = (struct vhd_cert_dist_server *)user; + + lwsl_info("%s: FS change: %s\n", __func__, path); + + /* + * If a fullchain.pem or privkey.pem changed, trigger updates for + * clients belonging to that domain. + */ + if (strstr(path, "fullchain.pem") || strstr(path, "privkey.pem")) { + lws_start_foreach_dll(struct lws_dll2 *, d, vhd->connections.head) { + struct pss_cert_dist_server *pss = + lws_container_of(d, struct pss_cert_dist_server, list); + + if (strstr(path, pss->domain)) { + lwsl_notice("%s: Triggering update for %s\n", __func__, pss->subdomain); + lws_callback_on_writable(pss->wsi); + } + } lws_end_foreach_dll(d); + } +} +#endif + +static int +callback_cert_dist_server(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct vhd_cert_dist_server *vhd = (struct vhd_cert_dist_server *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + struct pss_cert_dist_server *pss = (struct pss_cert_dist_server *)user; + union lws_tls_cert_info_results ir; + char path[512], *p; + + switch (reason) { + + case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct vhd_cert_dist_server)); + if (!vhd) + return -1; + vhd->cx = lws_get_context(wsi); + vhd->vh = lws_get_vhost(wsi); + vhd->protocol = lws_get_protocol(wsi); + + lws_strncpy(vhd->pki_root, "/var/dnssec/domains/", sizeof(vhd->pki_root)); + const struct lws_protocol_vhost_options *pvo = (const struct lws_protocol_vhost_options *)in; + while (pvo) { + if (!strcmp(pvo->name, "pki-root")) + lws_strncpy(vhd->pki_root, pvo->value, sizeof(vhd->pki_root)); + pvo = pvo->next; + } + +#if defined(LWS_WITH_DIR) + /* Watch the pki_root/ directory for changes */ + char scan_path[512]; + lws_snprintf(scan_path, sizeof(scan_path), "%s", vhd->pki_root); + vhd->dn = lws_dir_notify_create(vhd->cx, scan_path, dist_server_dir_notify_cb, vhd); +#endif + break; + + case LWS_CALLBACK_ESTABLISHED: + /* + * Extract the identity from the client certificate. + * The CN should be the subdomain. + */ + if (lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, sizeof(ir.ns.name))) { + lwsl_err("%s: No client cert common name found\n", __func__); + return -1; + } + + lws_strncpy(pss->subdomain, ir.ns.name, sizeof(pss->subdomain)); + pss->wsi = wsi; + + /* Derive domain from subdomain (assume last two parts or similar) */ + p = strchr(pss->subdomain, '.'); + if (p) + lws_strncpy(pss->domain, p + 1, sizeof(pss->domain)); + else + lws_strncpy(pss->domain, pss->subdomain, sizeof(pss->domain)); + + /* Verify the client cert still exists in the monitor's data dir */ + lws_snprintf(path, sizeof(path), "%s/%s/dist-client/distribution-client-%s.crt", + vhd->pki_root, pss->domain, pss->subdomain); + + if (access(path, F_OK)) { + lwsl_err("%s: Client cert file %s not found on server\n", __func__, path); + return -1; + } + + lwsl_notice("%s: Validated distribution client for %s\n", __func__, pss->subdomain); + lws_dll2_add_tail(&pss->list, &vhd->connections); + pss->established = 1; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLOSED: + lws_dll2_remove(&pss->list); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + if (!pss->established) + return -1; + + { + char cert_path[512], key_path[512]; + char *cert_buf = NULL, *key_buf = NULL; + struct stat st; + int fd, m; + + lws_snprintf(cert_path, sizeof(cert_path), "%s/%s/fullchain.pem", + vhd->pki_root, pss->domain); + lws_snprintf(key_path, sizeof(key_path), "%s/%s/privkey.pem", + vhd->pki_root, pss->domain); + + /* Read cert */ + fd = open(cert_path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + cert_buf = malloc((size_t)st.st_size + 1); + if (cert_buf) { + if (read(fd, cert_buf, (size_t)st.st_size) != (ssize_t)st.st_size) { + free(cert_buf); + cert_buf = NULL; + } else { + cert_buf[st.st_size] = '\0'; + } + } + } + close(fd); + } + + /* Read key */ + fd = open(key_path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + key_buf = malloc((size_t)st.st_size + 1); + if (key_buf) { + if (read(fd, key_buf, (size_t)st.st_size) != (ssize_t)st.st_size) { + free(key_buf); + key_buf = NULL; + } else { + key_buf[st.st_size] = '\0'; + } + } + } + close(fd); + } + + if (cert_buf && key_buf) { + /* Prepare JSON payload */ + size_t jlen = strlen(cert_buf) + strlen(key_buf) + 512; + char *json = malloc(LWS_PRE + jlen); + char *start = json + LWS_PRE, *p = start, *end = json + LWS_PRE + jlen; + + if (json) { + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "{\"subdomain\":\"%s\",\"fullchain\":\"", pss->subdomain); + + /* Escape newlines for JSON */ + char *src = cert_buf; + while (*src && p < end - 4) { + if (*src == '\n') { *p++ = '\\'; *p++ = 'n'; } + else if (*src == '\r') { *p++ = '\\'; *p++ = 'r'; } + else if (*src == '"') { *p++ = '\\'; *p++ = '"'; } + else *p++ = *src; + src++; + } + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\",\"privkey\":\""); + + src = key_buf; + while (*src && p < end - 4) { + if (*src == '\n') { *p++ = '\\'; *p++ = 'n'; } + else if (*src == '\r') { *p++ = '\\'; *p++ = 'r'; } + else if (*src == '"') { *p++ = '\\'; *p++ = '"'; } + else *p++ = *src; + src++; + } + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"}"); + + lwsl_notice("%s: Sending %d bytes to %s\n", __func__, (int)(p - start), pss->subdomain); + m = lws_write(wsi, (unsigned char *)start, lws_ptr_diff_size_t(p, start), LWS_WRITE_TEXT); + if (m < 0) { + lwsl_err("%s: Write failed\n", __func__); + free(json); + goto bail; + } + free(json); + } + } + +bail: + if (cert_buf) free(cert_buf); + if (key_buf) free(key_buf); + } + break; + + default: + break; + } + + return 0; +} + +static const struct lws_protocols protocols[] = { + { + "lws-cert-dist-server", + callback_cert_dist_server, + sizeof(struct pss_cert_dist_server), + 1024, /* rx buf size */ + 0, /* id */ + NULL, 0 /* user, tx_idl */ + }, + { NULL, NULL, 0, 0, 0, NULL, 0 } /* terminator */ +}; + +LWS_VISIBLE const lws_plugin_protocol_t lws_cert_dist_server = { + .hdr = { + .name = "cert dist server", + ._class = "lws_protocol_plugin", + .lws_build_hash = LWS_BUILD_HASH, + .api_magic = LWS_PLUGIN_API_MAGIC, + }, + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), +}; diff --git a/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c b/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c index 005bdcca6a..db82f662e7 100644 --- a/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c +++ b/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c @@ -116,6 +116,7 @@ struct vhd_dht_dnssec { lws_dll2_owner_t notify_strikes; lws_dll2_owner_t notify_ratelimiters; lws_dht_hash_t *myid; + int sweep_prefix_idx; }; static struct vhd_dht_dnssec *global_dnssec_vhd = NULL; @@ -166,7 +167,9 @@ static void notify_ratelimit_expire_cb(lws_sorted_usec_list_t *sul) { struct notify_ratelimit *nrl = lws_container_of(sul, struct notify_ratelimit, sul_decay); - lwsl_notice("%s: NOTIFY ratelimit decayed completely for IP\n", __func__); + char peer_ip[64] = "unknown"; + lws_sa46_write_numeric_address(&nrl->sa, peer_ip, sizeof(peer_ip)); + lwsl_notice("%s: DHT NOTIFY ratelimit decayed completely for IP %s\n", __func__, peer_ip); lws_dll2_remove(&nrl->list); free(nrl); } @@ -248,6 +251,9 @@ notify_fetch_completion_cb(void *opaque, const char *domain, int status) struct notify_strike_tracking *trk = (struct notify_strike_tracking *)opaque; if (status == 0) { add_peer_strike(trk->vhd, &trk->sa); + lwsl_notice("%s: Added strike to peer due to fetch network timeout for %s\n", __func__, domain); + } else if (status < 0) { + lwsl_notice("%s: Fetch for %s failed DNSSEC validation, skipping strike penalty for peer\n", __func__, domain); } else if (status == 1) { lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, trk->vhd->notify_strikes.head) { struct notify_strike *n = lws_container_of(d, struct notify_strike, list); @@ -353,33 +359,45 @@ do_fetch_zone(struct lws_context *cx, struct lws_dht_dnssec_fetch_zone_args *arg static int do_notify_peer_outdated(struct lws_vhost *vhost, const char *domain, const lws_sockaddr46 *sa46_peer, uint64_t newer_soa_serial); +static int +do_subscribe_zone(struct lws_vhost *vhost, const char *domain); + static void dht_dnssec_broadcast_notify(struct vhd_dht_dnssec *vhd, const char *domain, uint64_t soa_serial) { - struct sockaddr_in sin[32]; - struct sockaddr_in6 sin6[32]; - int num_v4 = 32, num_v6 = 32, i; + struct sockaddr_in sin[128]; + struct sockaddr_in6 sin6[128]; + int num_v4 = 128, num_v6 = 128, i; if (!vhd->dht) return; /* Send an unsolicited EVENT_NOTIFY to all known DHT participants to immediately propagate new zonefiles */ lws_dht_get_nodes(vhd->dht, sin, &num_v4, sin6, &num_v6); + lwsl_user("%s: Broadcasting SOA %llu for %s to %d IPv4 and %d IPv6 peers\n", + __func__, (unsigned long long)soa_serial, domain, num_v4, num_v6); + for (i = 0; i < num_v4; i++) { lws_sockaddr46 sa; + char ip[64]; memset(&sa, 0, sizeof(sa)); sa.sa4.sin_family = AF_INET; sa.sa4.sin_addr = sin[i].sin_addr; sa.sa4.sin_port = sin[i].sin_port; + lws_sa46_write_numeric_address(&sa, ip, sizeof(ip)); + lwsl_notice("%s: Broadast NOTIFY to %s:%u\n", __func__, ip, ntohs(sa.sa4.sin_port)); do_notify_peer_outdated(vhd->vhost, domain, &sa, soa_serial); } #if defined(LWS_WITH_IPV6) for (i = 0; i < num_v6; i++) { lws_sockaddr46 sa; + char ip[64]; memset(&sa, 0, sizeof(sa)); sa.sa6.sin6_family = AF_INET6; sa.sa6.sin6_addr = sin6[i].sin6_addr; sa.sa6.sin6_port = sin6[i].sin6_port; + lws_sa46_write_numeric_address(&sa, ip, sizeof(ip)); + lwsl_notice("%s: Broadast NOTIFY to %s:%u\n", __func__, ip, ntohs(sa.sa6.sin6_port)); do_notify_peer_outdated(vhd->vhost, domain, &sa, soa_serial); } #endif @@ -687,6 +705,10 @@ dht_dnssec_dnskey_cb(struct lws *wsi, const char *name, const struct addrinfo *d if (frag->ds_digest_len > 0 && memcmp(digest, frag->ds_digest, frag->ds_digest_len) == 0) { lwsl_user("%s: DS Hash Test matched the Embedded JWS JWK perfectly!\n", __func__); ds_hash_test_worked = 1; + } else { + lwsl_user("%s: DS Hash Test failed! Computed vs Fetched (%d bytes):\n", __func__, frag->ds_digest_len); + lwsl_hexdump_user(digest, frag->ds_digest_len); + lwsl_hexdump_user(frag->ds_digest, frag->ds_digest_len); } } else { lws_genhash_destroy(&hash_ctx, digest); @@ -819,7 +841,7 @@ dht_dnssec_dnskey_cb(struct lws *wsi, const char *name, const struct addrinfo *d free(jws_buf); if (!valid) { - lwsl_notice("%s: Cryptographic verification of JWS failed\n", __func__); + lwsl_notice("%s: Cryptographic verification of JWS failed for domain %s\n", __func__, frag->domain); add_peer_strike(frag->vhd, (const lws_sockaddr46 *)&frag->from_sa); goto drop; } @@ -863,7 +885,7 @@ dht_dnssec_dnskey_cb(struct lws *wsi, const char *name, const struct addrinfo *d if (!lws_hex_to_byte_array(frag->safe_hash, raw_hash, sizeof(raw_hash))) { lws_dht_hash_t *id = lws_dht_hash_create(LWS_DHT_HASH_TYPE_SHA256, 32, raw_hash); if (id) { - lws_dht_notify_subscribers(frag->dht_ctx, id, frag->payload_hash); + lws_dht_notify_subscribers(frag->dht_ctx, id, frag->payload_hash, NULL, 0); lws_dht_hash_destroy(&id); } } @@ -1063,7 +1085,7 @@ dht_dnssec_dnskey_cb(struct lws *wsi, const char *name, const struct addrinfo *d lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, vhd->fetch_reqs.head) { struct lws_dht_dnssec_fetch_req *req = lws_container_of(d, struct lws_dht_dnssec_fetch_req, list); if (!strcmp(req->target_hash, frag->safe_hash)) { - if (req->cb) req->cb(req->opaque, req->domain, 0); + if (req->cb) req->cb(req->opaque, req->domain, -1); lws_sul_cancel(&req->sul_timeout); lws_dll2_remove(d); free(req); @@ -1147,7 +1169,7 @@ dht_dnssec_ds_cb(struct lws *wsi, const char *ads, const struct addrinfo *result lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, frag->vhd->fetch_reqs.head) { struct lws_dht_dnssec_fetch_req *req = lws_container_of(d, struct lws_dht_dnssec_fetch_req, list); if (!strcmp(req->target_hash, frag->safe_hash)) { - if (req->cb) req->cb(req->opaque, req->domain, 0); + if (req->cb) req->cb(req->opaque, req->domain, -1); lws_sul_cancel(&req->sul_timeout); lws_dll2_remove(d); free(req); @@ -1202,7 +1224,7 @@ dht_dnssec_trigger_validation(struct lws_dht_ctx *ctx, struct vhd_dht_dnssec *vh } if (lws_jws_b64_compact_map(buf, (int)st.st_size, &map_b64) < 0) { - lwsl_notice("%s: compact JWS map failed\n", __func__); + lwsl_notice("%s: compact JWS map failed (st.st_size=%zu)\n", __func__, (size_t)st.st_size); free(temp); free(buf); return -1; @@ -1211,18 +1233,21 @@ dht_dnssec_trigger_validation(struct lws_dht_ctx *ctx, struct vhd_dht_dnssec *vh h = lws_jws_compact_decode(buf, (int)st.st_size, &map, &map_b64, temp, &temp_len); if (h != 3) { - lwsl_notice("%s: compact JWS decode failed (h=%d)\n", __func__, h); + lwsl_notice("%s: compact JWS decode failed (h=%d, st.st_size=%zu)\n", __func__, h, (size_t)st.st_size); free(temp); free(buf); return -1; } + lwsl_notice("%s: JWS Decoded: header_len=%u, payload_len=%u, sig_len=%u\n", + __func__, map.len[LJWS_JOSE], map.len[LJWS_PYLD], map.len[LJWS_SIG]); + /* Extract domain dynamically and strictly validate the syntax of the decoded zone file payload! */ struct auth_dns_zone parsed_zone; memset(&parsed_zone, 0, sizeof(parsed_zone)); if (lws_auth_dns_parse_zone_buf((const char *)map.buf[LJWS_PYLD], map.len[LJWS_PYLD], &parsed_zone, NULL, NULL)) { - lwsl_err("%s: Failed to syntax validate zonefile!\n", __func__); + lwsl_err("%s: Failed to syntax validate zonefile (payload_len=%u)!\n", __func__, map.len[LJWS_PYLD]); free(temp); free(buf); return -1; @@ -1534,7 +1559,7 @@ verb_put_handler(struct vhd_dht_dnssec *vhd, struct lws_dht_verb_dispatch_args * lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, vhd->fetch_reqs.head) { struct lws_dht_dnssec_fetch_req *req = lws_container_of(d, struct lws_dht_dnssec_fetch_req, list); if (!strcmp(req->target_hash, frag->safe_hash)) { - if (req->cb) req->cb(req->opaque, req->domain, 0); + if (req->cb) req->cb(req->opaque, req->domain, -1); lws_sul_cancel(&req->sul_timeout); lws_dll2_remove(d); free(req); @@ -1561,7 +1586,7 @@ verb_get_handler(struct vhd_dht_dnssec *vhd, struct lws_dht_verb_dispatch_args * struct stat st; - lwsl_user("%s: GET %s offset %llu len %llu\n", __func__, msg->hash, msg->offset, msg->len); + // lwsl_user("%s: GET %s offset %llu len %llu\n", __func__, msg->hash, msg->offset, msg->len); lws_snprintf(path, sizeof(path), "%s/%s", vhd->storage_path, msg->hash); fd = open(path, O_RDONLY); @@ -1791,7 +1816,7 @@ verb_rsp_handler(struct vhd_dht_dnssec *vhd, struct lws_dht_verb_dispatch_args * lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, vhd->fetch_reqs.head) { struct lws_dht_dnssec_fetch_req *req = lws_container_of(d, struct lws_dht_dnssec_fetch_req, list); if (!strcmp(req->target_hash, frag->safe_hash)) { - if (req->cb) req->cb(req->opaque, req->domain, 0); + if (req->cb) req->cb(req->opaque, req->domain, -1); lws_sul_cancel(&req->sul_timeout); lws_dll2_remove(d); free(req); @@ -1834,7 +1859,7 @@ verb_cap_rsp_handler(struct vhd_dht_dnssec *vhd, struct lws_dht_verb_dispatch_ar lwsl_user("%s: Peer supports lws-dht-dnssec via CAP_RSP! Proceeding with PUT.\n", __func__); if (!vhd->put_started) { vhd->put_started = 1; - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_put_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_put_cb, 10 * LWS_US_PER_MS); } } else if (strstr(pbuf, "\"lws-dht-store\"")) { lwsl_err("%s: Peer only supports basic raw store, missing lws-dht-dnssec capability. Aborting.\n", __func__); @@ -2050,12 +2075,22 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash, } uint64_t newer_soa = 0; + char newer_domain[256]; + newer_domain[0] = '\0'; + if (data_len >= 8) { const uint8_t *p = (const uint8_t *)data; newer_soa = ((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) | ((uint64_t)p[2] << 40) | ((uint64_t)p[3] << 32) | ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) | ((uint64_t)p[6] << 8) | p[7]; + + if (data_len > 8) { + size_t dl = data_len - 8; + if (dl > sizeof(newer_domain) - 1) dl = sizeof(newer_domain) - 1; + memcpy(newer_domain, p + 8, dl); + newer_domain[dl] = '\0'; + } } { @@ -2149,7 +2184,8 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash, } /* If we're not actively tracking the subscription but we have it hot in our local cache, - * we should still honor the NOTIFY to keep the cache from going stale during ACME validations. */ + * or we are an authoritative server and we just discovered the domain name, + * we should honor the NOTIFY. */ if (!found) { char hex_hash[LWS_GENHASH_LARGEST * 2 + 1]; lws_hex_from_byte_array(info_hash->id, info_hash->len, hex_hash, sizeof(hex_hash)); @@ -2161,22 +2197,41 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash, da.prefix = hex_hash; lws_dir(dir_path, &da, dht_dnssec_find_highest_serial_cb); - if (da.is_outdated) { - /* We don't have the domain string because it's just a hash, so use the hash as the domain name for internal tracking */ - lws_snprintf(target_domain, sizeof(target_domain), "dht-hash-%s", hex_hash); - lwsl_notice("%s: Hash %s is not actively subscribed but exists in cache. Honoring NOTIFY to keep hot!\n", __func__, hex_hash); - - /* Implicitly create a subscription so subsequent NOTIFYs are tracked via `found_sub` rate limiters */ - struct lws_dht_dnssec_subscribed_domain *nsub = malloc(sizeof(*nsub)); - if (nsub) { - memset(nsub, 0, sizeof(*nsub)); - lws_strncpy(nsub->domain, target_domain, sizeof(nsub->domain)); - memcpy(nsub->hash, info_hash->id, info_hash->len); - nsub->needs_initial_fetch = 0; - lws_dll2_add_tail(&nsub->list, &vhd->subscribed_domains); - found_sub = nsub; + if (da.is_outdated || (vhd->auth_cb && newer_domain[0])) { + if (newer_domain[0]) { + lws_strncpy(target_domain, newer_domain, sizeof(target_domain)); + if (da.is_outdated) + lwsl_user("%s: Discovered domain name '%s' for cached hash %s. Upgrading!\n", __func__, newer_domain, hex_hash); + else + lwsl_user("%s: Auto-subscribing to discovered authoritative domain: %s\n", __func__, newer_domain); + } else { + lws_snprintf(target_domain, sizeof(target_domain), "dht-hash-%s", hex_hash); + lwsl_notice("%s: Hash %s is not actively subscribed but exists in cache. Honoring NOTIFY to keep hot!\n", __func__, hex_hash); + } + + /* Implicitly create or upgrade a subscription so subsequent NOTIFYs are tracked via `found_sub` rate limiters */ + if (vhd->auth_cb && newer_domain[0]) { + do_subscribe_zone(vhd->vhost, newer_domain); + } else { + struct lws_dht_dnssec_subscribed_domain *nsub = malloc(sizeof(*nsub)); + if (nsub) { + memset(nsub, 0, sizeof(*nsub)); + lws_strncpy(nsub->domain, target_domain, sizeof(nsub->domain)); + memcpy(nsub->hash, info_hash->id, info_hash->len); + nsub->needs_initial_fetch = 0; + lws_dll2_add_tail(&nsub->list, &vhd->subscribed_domains); + } } - found = 1; + + /* Re-find the sub we just created or were already looking for */ + lws_start_foreach_dll(struct lws_dll2 *, d, vhd->subscribed_domains.head) { + struct lws_dht_dnssec_subscribed_domain *sub = lws_container_of(d, struct lws_dht_dnssec_subscribed_domain, list); + if (!memcmp(sub->hash, info_hash->id, info_hash->len)) { + found_sub = sub; + found = 1; + break; + } + } lws_end_foreach_dll(d); } } @@ -2285,21 +2340,47 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash, } do_fetch_zone(vhd->context, &args); - } else { + } + + /* + * [RELAY LOGIC] Even if we didn't find a local subscription or owner (found == 0), + * we might be a tracker/relay for other nodes who HAVE subscribed to us. + * We must relay the NOTIFY signal to our DHT subscribers. + */ + { + uint8_t pseudo_sha256[32]; + memset(pseudo_sha256, 0, sizeof(pseudo_sha256)); + /* Use the SOA serial as the 'version' hash for subscriber change detection */ + pseudo_sha256[0] = (uint8_t)(newer_soa >> 56); + pseudo_sha256[1] = (uint8_t)(newer_soa >> 48); + pseudo_sha256[2] = (uint8_t)(newer_soa >> 40); + pseudo_sha256[3] = (uint8_t)(newer_soa >> 32); + pseudo_sha256[4] = (uint8_t)(newer_soa >> 24); + pseudo_sha256[5] = (uint8_t)(newer_soa >> 16); + pseudo_sha256[6] = (uint8_t)(newer_soa >> 8); + pseudo_sha256[7] = (uint8_t)newer_soa; + + /* Pass the FULL incoming payload (serial + domain) to subscribers */ + int relayed = lws_dht_notify_subscribers(vhd->dht, info_hash, pseudo_sha256, (const uint8_t *)data, data_len); + if (relayed > 0) + lwsl_notice("%s: Relayed NOTIFY (len %d) to %d downstream DHT subscribers\n", __func__, (int)data_len, relayed); + } + + if (!found) { char h1[128], h2[128] = "NONE"; lws_hex_from_byte_array(info_hash->id, info_hash->len, h1, sizeof(h1)); if (vhd->subscribed_domains.head) { struct lws_dht_dnssec_subscribed_domain *sub = lws_container_of(vhd->subscribed_domains.head, struct lws_dht_dnssec_subscribed_domain, list); lws_hex_from_byte_array(sub->hash, info_hash->len, h2, sizeof(h2)); } - lwsl_notice("%s: Incoming NOTIFY hash (%s) does not match any active subscriptions (first sub is %s).\n", __func__, h1, h2); + lwsl_notice("%s: Incoming NOTIFY hash (%s) does not match any active local subscriptions (first sub is %s). [RELAY ONLY]\n", __func__, h1, h2); } break; } case LWS_DHT_EVENT_SEARCH_DONE: case LWS_DHT_EVENT_SEARCH_DONE6: { struct vhd_dht_dnssec *vhd = (struct vhd_dht_dnssec *)closure; - lwsl_notice("%s: Peer discovery completed! Probing known nodes for IPs...\n", __func__); + lwsl_notice("%s: Peer discovery completed (af=%d)! Probing known nodes for IPs...\n", __func__, event == LWS_DHT_EVENT_SEARCH_DONE ? 4 : 6); lws_dht_test_external_ips(vhd->dht); break; } @@ -2318,9 +2399,9 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash, if (!get_hash && vhd->cli_get_domain) { char domain_str[256]; - int dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_get_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_get_domain); if (!lws_genhash_init(&pctx, LWS_DHT_STORE_GENHASH) && - !lws_genhash_update(&pctx, domain_str, (size_t)dom_len) && + !lws_genhash_update(&pctx, domain_str, strlen(domain_str)) && !lws_genhash_destroy(&pctx, hash)) { lws_hex_from_byte_array(hash, (size_t)lws_genhash_size(LWS_DHT_STORE_GENHASH), computed_hash_hex, sizeof(computed_hash_hex)); get_hash = computed_hash_hex; @@ -2660,7 +2741,6 @@ dht_dnssec_sul_put_cb(struct lws_sorted_usec_list *sul) { char domain_str[256]; - int dom_len; if (!vhd->cli_domain) { lwsl_err("No domain specified for zone file upload (use --domain)\n"); @@ -2669,10 +2749,10 @@ dht_dnssec_sul_put_cb(struct lws_sorted_usec_list *sul) return; } - dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_domain); if (lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) || - lws_genhash_update(&ctx, domain_str, (size_t)dom_len) || + lws_genhash_update(&ctx, domain_str, strlen(domain_str)) || lws_genhash_destroy(&ctx, hash)) { lwsl_err("Hash calculation failed\n"); return; @@ -2707,13 +2787,12 @@ dht_dnssec_sul_get_cb(struct lws_sorted_usec_list *sul) if (!get_hash && vhd->cli_get_domain) { char domain_str[256]; - int dom_len; struct lws_genhash_ctx ctx; uint8_t hash[LWS_GENHASH_LARGEST]; - dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_get_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", vhd->cli_get_domain); if (!lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) && - !lws_genhash_update(&ctx, domain_str, (size_t)dom_len) && + !lws_genhash_update(&ctx, domain_str, strlen(domain_str)) && !lws_genhash_destroy(&ctx, hash)) { lws_hex_from_byte_array(hash, (size_t)lws_genhash_size(LWS_DHT_STORE_GENHASH), computed_hash_hex, sizeof(computed_hash_hex)); get_hash = computed_hash_hex; @@ -2862,10 +2941,98 @@ lws_dht_dnssec_domain_destroy(struct lws_dll2 *d, void *user) return 0; } +struct dht_dnssec_sweep_args { + struct vhd_dht_dnssec *vhd; + int items_scanned; +}; + +static int +dht_dnssec_sweep_level2_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + if (lde->type == LDOT_FILE) { + size_t nl = strlen(lde->name); + if (nl > 8 && !strcmp(lde->name + nl - 8, ".payload")) { + char *p = (char *)lde->name + nl - 9; + int uscore = 0; + uint64_t sig_expiry = 0; + while (p > lde->name) { + if (*p == '_') { + uscore++; + if (uscore == 2 && sig_expiry == 0) { + sig_expiry = strtoull(p + 1, NULL, 10); + } + } + p--; + } + + int is_legacy = (uscore < 3); + + if (is_legacy || (sig_expiry > 0 && sig_expiry < (uint64_t)lws_now_secs())) { + char fp[1024]; + lws_snprintf(fp, sizeof(fp), "%s/%s", dirpath, lde->name); + lwsl_notice("Expunging %s payload %s\n", is_legacy ? "legacy" : "expired", fp); + unlink(fp); + + char hash[128]; + p = (char *)lde->name; + int i = 0; + while (p < lde->name + nl && *p != '_' && *p != '.' && i < (int)sizeof(hash) - 1) { + hash[i++] = *p++; + } + hash[i] = '\0'; + lws_snprintf(fp, sizeof(fp), "%s/%s", dirpath, hash); + unlink(fp); + } + } + } + return 0; +} + +static int +dht_dnssec_sweep_level1_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + struct dht_dnssec_sweep_args *a = (struct dht_dnssec_sweep_args *)user; + if (lde->type == LDOT_DIR && lde->name[0] != '.') { + a->items_scanned++; + char next_path[1024]; + lws_snprintf(next_path, sizeof(next_path), "%s/%s", dirpath, lde->name); + lws_dir(next_path, a, dht_dnssec_sweep_level2_cb); + } + return 0; +} + static void dht_dnssec_sul_dump_cb(lws_sorted_usec_list_t *sul) { + struct vhd_dht_dnssec *vhd = lws_container_of(sul, struct vhd_dht_dnssec, sul_dump); + struct dht_dnssec_sweep_args a; + int tries = 0; + + memset(&a, 0, sizeof(a)); + a.vhd = vhd; + + while (tries < 256) { + char target_prefix[4]; + lws_snprintf(target_prefix, sizeof(target_prefix), "%02x", vhd->sweep_prefix_idx); + + char next_path[1024]; + lws_snprintf(next_path, sizeof(next_path), "%s/%s", vhd->storage_path, target_prefix); + + struct stat st; + if (stat(next_path, &st) == 0 && S_ISDIR(st.st_mode)) { + lws_dir(next_path, &a, dht_dnssec_sweep_level1_cb); + vhd->sweep_prefix_idx = (vhd->sweep_prefix_idx + 1) & 0xff; + if (a.items_scanned > 0) + break; + } else { + vhd->sweep_prefix_idx = (vhd->sweep_prefix_idx + 1) & 0xff; + } + tries++; + } + /* Periodic DHT Routing Table dumps have been disabled */ + /* Re-schedule the sweep every 60 seconds */ + lws_sul_schedule(vhd->context, 0, &vhd->sul_dump, dht_dnssec_sul_dump_cb, 60 * LWS_US_PER_SEC); } static int @@ -2904,6 +3071,8 @@ callback_dht_dnssec(struct lws* wsi, enum lws_callback_reasons reason, } case LWS_CALLBACK_PROTOCOL_INIT: { + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; const char *store_verbs[] = { "PUT", "GET", @@ -3106,24 +3275,24 @@ callback_dht_dnssec(struct lws* wsi, enum lws_callback_reasons reason, } else if (vhd->cli_put_file) { lwsl_notice("%s: Taking PUT branch\n", __func__); lwsl_user("%s: Starting PUT task\n", __func__); - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_cap_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_cap_cb, 10 * LWS_US_PER_MS); } else if (vhd->cli_bulk || vhd->gen_manifest) { lwsl_notice("%s: Taking BULK branch\n", __func__); lwsl_user("%s: Starting BULK task\n", __func__); - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_bulk_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_bulk_cb, 10 * LWS_US_PER_MS); } else if (vhd->cli_get_hash || vhd->cli_get_domain) { lwsl_notice("%s: Taking GET branch\n", __func__); lwsl_user("%s: Starting GET task\n", __func__); - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_get_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_get_cb, 10 * LWS_US_PER_MS); } else if (vhd->cli_receiver) { lwsl_notice("%s: Taking RECEIVER branch\n", __func__); lwsl_user("%s: Starting RECEIVER task\n", __func__); - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_manifest_rcv_cb, 10); - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_manifest_rcv_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_manifest_rcv_cb, 10 * LWS_US_PER_MS); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_manifest_rcv_cb, 10 * LWS_US_PER_MS); } else { lwsl_notice("%s: Taking BOOTSTRAP branch\n", __func__); /* Always schedule bootstrap checking, either to actively ping target_ip OR passively wait for peers to ping us */ - lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_bootstrap_cb, 10); + lws_sul_schedule(vhd->context, 0, &vhd->sul_bulk, dht_dnssec_sul_bootstrap_cb, 10 * LWS_US_PER_MS); } break; } @@ -3863,6 +4032,8 @@ do_signzone(struct lws_context *context, struct lws_dht_dnssec_signzone_args *ar int fd_acme = open(acmefile_path, O_RDONLY); int has_acmefile = (fd_acme >= 0); + lwsl_notice("%s: Checking for ACME addon at %s (found: %d)\n", __func__, acmefile_path, has_acmefile); + struct lws_dht_dnssec_domain *dom = NULL; if (v && args->domain) { @@ -3986,7 +4157,7 @@ do_add_temp_zone(struct lws_context *context, const char *domain, const char *zo struct lws_genhash_ctx ctx; char domain_str[256]; char clean_domain[256]; - int clen, dom_len; + int clen; dom = malloc(sizeof(*dom)); if (!dom) return 1; @@ -3998,10 +4169,10 @@ do_add_temp_zone(struct lws_context *context, const char *domain, const char *zo clen = (int)strlen(clean_domain); if (clen > 0 && clean_domain[clen - 1] == '.') clean_domain[clen - 1] = '\0'; - dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); if (lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) || - lws_genhash_update(&ctx, domain_str, (size_t)dom_len) || + lws_genhash_update(&ctx, domain_str, strlen(domain_str)) || lws_genhash_destroy(&ctx, dom->hash)) { free(dom); return 1; @@ -4173,10 +4344,10 @@ do_fetch_zone(struct lws_context *context, struct lws_dht_dnssec_fetch_zone_args lws_hex_to_byte_array(hex, hash, (int)lws_genhash_size(LWS_DHT_STORE_GENHASH)); } else { char domain_str[256]; - int dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); if (lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) || - lws_genhash_update(&ctx, domain_str, (size_t)dom_len) || + lws_genhash_update(&ctx, domain_str, strlen(domain_str)) || lws_genhash_destroy(&ctx, hash)) { return 1; } @@ -4524,7 +4695,6 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain) struct vhd_dht_dnssec *vhd; const struct lws_protocols *prot; char domain_str[256]; - int dom_len; struct lws_genhash_ctx ctx; uint8_t hash[LWS_GENHASH_LARGEST]; @@ -4539,9 +4709,14 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain) if (clen > 0 && clean_domain[clen - 1] == '.') clean_domain[clen - 1] = '\0'; - dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); + + for (int i = 0; i < (int)strlen(clean_domain); i++) + clean_domain[i] = (char)tolower(clean_domain[i]); + + lwsl_notice("%s: Normalizing domain to %s for DHT hash calculation\n", __func__, clean_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); if (lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) || - lws_genhash_update(&ctx, domain_str, (size_t)dom_len) || + lws_genhash_update(&ctx, domain_str, strlen(domain_str)) || lws_genhash_destroy(&ctx, hash)) { return 1; } @@ -4562,7 +4737,23 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain) memset(nsub, 0, sizeof(*nsub)); lws_strncpy(nsub->domain, domain, sizeof(nsub->domain)); memcpy(nsub->hash, hash, (size_t)lws_genhash_size(LWS_DHT_STORE_GENHASH)); - nsub->needs_initial_fetch = 1; + + char hex[65]; + lws_hex_from_byte_array(hash, (size_t)lws_genhash_size(LWS_DHT_STORE_GENHASH), hex, sizeof(hex)); + struct dht_dnssec_fetch_dir_args fda; + memset(&fda, 0, sizeof(fda)); + fda.prefix = hex; + char dir_path[256]; + lws_snprintf(dir_path, sizeof(dir_path), "%s/%.2s/%.2s", vhd->storage_path, hex, hex + 2); + lws_dir(dir_path, &fda, dht_dnssec_fetch_payload_cb); + + if (fda.found) { + nsub->needs_initial_fetch = 0; + nsub->last_notify_fetch = time(NULL); + } else { + nsub->needs_initial_fetch = 1; + } + lws_dll2_add_tail(&nsub->list, &vhd->subscribed_domains); } } @@ -4613,7 +4804,6 @@ do_notify_peer_outdated(struct lws_vhost *vhost, const char *domain, struct vhd_dht_dnssec *vhd; const struct lws_protocols *prot; char domain_str[256]; - int dom_len; struct lws_genhash_ctx ctx; uint8_t hash[LWS_GENHASH_LARGEST]; uint8_t payload[32]; // Up to 32 bytes to pass the serial @@ -4629,9 +4819,9 @@ do_notify_peer_outdated(struct lws_vhost *vhost, const char *domain, if (clen > 0 && clean_domain[clen - 1] == '.') clean_domain[clen - 1] = '\0'; - dom_len = lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); + lws_snprintf(domain_str, sizeof(domain_str), "lws-dnssec-dht-%s", clean_domain); if (lws_genhash_init(&ctx, LWS_DHT_STORE_GENHASH) || - lws_genhash_update(&ctx, domain_str, (size_t)dom_len) || + lws_genhash_update(&ctx, domain_str, strlen(domain_str)) || lws_genhash_destroy(&ctx, hash)) { return 1; } @@ -4646,6 +4836,11 @@ do_notify_peer_outdated(struct lws_vhost *vhost, const char *domain, payload[6] = (uint8_t)(newer_soa_serial >> 8); payload[7] = (uint8_t)(newer_soa_serial & 0xff); + /* Append the domain name to the payload to allow auto-discovery on authoritative nodes */ + size_t payload_len = 8; + lws_strncpy((char *)payload + 8, clean_domain, sizeof(payload) - 8); + payload_len += strlen(clean_domain) + 1; + uint8_t tid[4]; char peer_ip[64]; tid[0] = 'n'; @@ -4669,14 +4864,14 @@ do_notify_peer_outdated(struct lws_vhost *vhost, const char *domain, lws_dht_send_notify(vhd->dht, (const struct sockaddr *)&sa46_peer->sa4, sizeof(sa46_peer->sa4), tid, sizeof(tid), proper_hash, NULL, - payload, 8); + payload, payload_len); } #if defined(LWS_WITH_IPV6) else if (sa46_peer->sa6.sin6_family == AF_INET6) { lws_dht_send_notify(vhd->dht, (const struct sockaddr *)&sa46_peer->sa6, sizeof(sa46_peer->sa6), tid, sizeof(tid), proper_hash, NULL, - payload, 8); + payload, payload_len); } #endif @@ -4701,7 +4896,7 @@ LWS_VISIBLE const struct lws_protocols lws_dht_dnssec_protocols[] = { { .name = "lws-dht-dnssec", .callback = callback_dht_dnssec, - .per_session_data_size = sizeof(struct vhd_dht_dnssec), + .per_session_data_size = 0, .rx_buffer_size = 4096, .user = (void *)&ops }, diff --git a/plugins/protocol_lws_dht_dnssec_monitor/CMakeLists.txt b/plugins/protocol_lws_dht_dnssec_monitor/CMakeLists.txt index c826620ba7..c52c6a03fa 100644 --- a/plugins/protocol_lws_dht_dnssec_monitor/CMakeLists.txt +++ b/plugins/protocol_lws_dht_dnssec_monitor/CMakeLists.txt @@ -6,7 +6,7 @@ require_lws_config(LWS_WITH_SYS_ASYNC_DNS_DNSSEC 1 requirements) require_lws_config(LWS_WITH_SPAWN 1 requirements) if (requirements) - create_plugin(${PLUGIN_NAME} "lws-dht-dnssec-monitor" "protocol_lws_dht_dnssec_monitor.c" "" "") + create_plugin(${PLUGIN_NAME} "lws-dht-dnssec-monitor" "protocol_lws_dht_dnssec_monitor.c;monitor-pki.c;monitor-acme.c;monitor-api.c;monitor-dnssec.c;monitor-whois.c;monitor-utils.c;monitor-proxy.c" "" "") if (NOT LWS_WITH_PLUGINS_BUILTIN) target_compile_definitions(${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) endif() diff --git a/plugins/protocol_lws_dht_dnssec_monitor/assets/index.html b/plugins/protocol_lws_dht_dnssec_monitor/assets/index.html index c3958bfb1e..9856e3fb58 100644 --- a/plugins/protocol_lws_dht_dnssec_monitor/assets/index.html +++ b/plugins/protocol_lws_dht_dnssec_monitor/assets/index.html @@ -5,7 +5,7 @@ DNSSEC Monitor - + @@ -20,7 +20,6 @@

Reconnecting...

DNSSEC Monitor

-

Manage DNSSEC and TLS

@@ -35,11 +34,19 @@

DNSSEC Monitor

-
-
-

Domains

- -
+
+ + + + +
+ +
+
+
+

Domains

+ +
@@ -54,57 +61,13 @@

Domains

- -
-

Global ACME Configuration

-
- -
-
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
-
- - -
-
-
-

Zone Records:
---

+

---

@@ -112,6 +75,11 @@

Zone Records:
---

+
+ +
@@ -151,6 +119,124 @@

Edit Record

+ + +
+
+
+

Cross-Domain TLS Certificates

+
+
+ + + + + + + + + + + + + +
FQDN:PortLocal ExpiryRemote ExpiryIssuerActions
Loading...
+
+
+
+ +
+
+
+

Global ACME Configuration

+
+ +
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+ + +
+
+
+
+ +
+
+
+

Distribution PKI Management

+
+
+
+

Distribution Root CA

+

The root CA used to sign client and server certificates for the distribution service.

+ +
+
+

Distribution Server Certificate

+

The TLS certificate and private key used by the distribution server.

+
+ +
+ +
+
+
+

Client Certificates

+

Generate and download client certificates for your provisioned TLS endpoints.

+
+ + + + + + + + + + + +
DomainEndpointActions
Loading...
+
+
+
+
@@ -204,31 +290,9 @@

Registrar DNSSEC Info

- -
- + diff --git a/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.css b/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.css index feaab33062..b7abe9e50f 100644 --- a/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.css +++ b/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.css @@ -30,7 +30,7 @@ body { font-family: var(--font-family); height: 100vh; overflow: hidden; - line-height: 1.5; + line-height: 1; } a { @@ -122,7 +122,7 @@ a:hover { .app-container { max-width: 1440px; margin: 0 auto; - padding: 2rem; + padding: 1rem; height: 100vh; display: flex; flex-direction: column; @@ -132,7 +132,7 @@ header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 2rem; + /* margin-bottom: 2rem; */ padding-bottom: 1rem; border-bottom: 1px solid var(--panel-border); } @@ -184,7 +184,7 @@ main { flex-direction: column; flex: 1; min-height: 0; - gap: 2rem; +/* gap: 2rem; */ } .panel { @@ -804,7 +804,7 @@ body.is-disconnected .app-container { .dns-layout-container { width: 100%; border-collapse: separate; - border-spacing: 10px 5px; + /* border-spacing: 10px 5px; */ background: rgba(0,0,0,0.2); border-radius: 8px; font-size: 70%; @@ -813,6 +813,7 @@ body.is-disconnected .app-container { font-weight: bold; color: var(--primary-color); vertical-align: top; + padding: 4px; } .dns-overall-btn { display: inline-block; @@ -852,9 +853,6 @@ body.is-disconnected .app-container { /* Global ACME Config UI */ .acme-global-config { - margin-top: 2rem; - padding-top: 2rem; - border-top: 1px solid var(--panel-border); display: flex; flex-direction: column; gap: 1rem; @@ -912,3 +910,107 @@ body.is-disconnected .app-container { border-radius: 6px; resize: vertical; } + +/* Tabs Styling */ +.tabs-header { + display: flex; + border-bottom: 1px solid var(--panel-border); + margin-bottom: 1.5rem; + gap: 0.5rem; +} + +.tab-link { + padding: 0.75rem 1.5rem; + border: none; + background: transparent; + color: var(--text-secondary); + font-family: inherit; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + border-bottom: 3px solid transparent; + border-radius: 6px 6px 0 0; + transition: all 0.2s; +} + +.tab-link:hover { + color: var(--text-primary); + background: rgba(255, 255, 255, 0.05); +} + +.tab-link.active { + color: var(--primary-color); + border-bottom-color: var(--primary-color); +} + +.tab-content { + display: none; +} + +.tab-content.active { + display: flex; + flex-direction: column; + flex: 1; + min-height: 0; + overflow-y: auto; + animation: fadeIn 0.3s; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(5px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Distribution PKI */ +.dist-pki-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; + margin-bottom: 1.5rem; +} + +.dist-pki-row .dist-pki-section { + margin-bottom: 0; +} + +.dist-pki-section { + background: rgba(0, 0, 0, 0.15); + border: 1px solid var(--panel-border); + border-radius: var(--radius); + padding: 1.5rem; + margin-bottom: 1.5rem; +} + +.dist-pki-section h3 { + margin-top: 0; + margin-bottom: 0.5rem; + color: var(--text-primary); + font-size: 1.1rem; +} + +.dist-pki-section .help-text { + color: var(--text-secondary); + font-size: 0.9rem; + margin-bottom: 1.25rem; +} + +.dist-pki-section .btn { + margin-bottom: 0.5rem; +} + +.ds-status-details { + margin-top: 4px; + font-size: 0.85em; + padding-left: 8px; + border-left: 2px solid #555; +} + +.dns-lookup-failed-alert { + font-weight: bold; + margin-top: 8px; + padding: 4px; + border: 1px solid red; + background: rgba(255, 0, 0, 0.1); +} + +.mb-10 { margin-bottom: 10px; } diff --git a/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.js b/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.js index f071ec482e..88ecca591d 100644 --- a/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.js +++ b/plugins/protocol_lws_dht_dnssec_monitor/assets/monitor.js @@ -7,24 +7,45 @@ let certCheckTimers = {}; let concurrentChecks = 0; const MAX_CONCURRENT = 5; +window.activeTls = []; +window.allTlsCache = {}; +window.certStatusCache = {}; + function processCertQueue() { while (concurrentChecks < MAX_CONCURRENT && certCheckQueue.length > 0) { let task = certCheckQueue.shift(); concurrentChecks++; let span = document.getElementById(`cert-status-${task.fqdn}`); if (span) span.innerText = 'Checking...'; - sendReq({ req: 'check_cert', domain: currentDomain, subdomain: task.fqdn, port: task.port }); + let reqDomain = task.domain || currentDomain; + sendReq({ req: 'check_cert', domain: reqDomain, subdomain: task.fqdn, port: task.port }); certCheckTimers[task.fqdn] = setTimeout(() => { + console.log('[INSTRUMENT] TIMER FIRED for:', task.fqdn, 'port:', task.port); + let cacheKey = task.fqdn + ':' + task.port; + let prev = window.certStatusCache[cacheKey]; + window.certStatusCache[cacheKey] = { + status: 'error', + msg: 'Connection Timeout', + local_msg: 'Unknown', + issuer: 'Unknown', + fqdn: task.fqdn, + port: task.port + }; + let s = document.getElementById(`cert-status-${task.fqdn}`); - if (s && s.innerText === 'Checking...') { + if (s) { s.innerText = 'Timeout'; - s.classList.remove('text-green', 'text-gray'); s.classList.add('text-red'); + s.classList.remove('text-green', 'text-gray'); + s.classList.add('text-red'); } if (certCheckTimers[task.fqdn]) delete certCheckTimers[task.fqdn]; concurrentChecks = Math.max(0, concurrentChecks - 1); + + updateGlobalTlsTable(); + if (typeof renderDomainDetails === 'function') renderDomainDetails(); processCertQueue(); - }, 5000); + }, 20000); } } @@ -239,14 +260,35 @@ function connect() { }; ws.onmessage = function(msg) { - console.log('[INSTRUMENT] WS onmessage: Raw payload: ', msg.data); - try { - const data = JSON.parse(msg.data); - console.log('[INSTRUMENT] WS onmessage: Parsed data: ', data); - handleResponse(data); - } catch(e) { - console.error('[INSTRUMENT] Failed to parse WS msg:', e); - console.log('[INSTRUMENT] Raw message fragment/broken:', msg.data); + window.wsStreamBuffer = (window.wsStreamBuffer || '') + msg.data; + const parts = window.wsStreamBuffer.split('\n'); + let testBuffer = ''; + let successfullyParsedUpToPart = -1; + + for (let i = 0; i < parts.length; i++) { + testBuffer += (testBuffer ? '\n' : '') + parts[i]; + let p = testBuffer.trim(); + if (!p) { + testBuffer = ''; + successfullyParsedUpToPart = i; + continue; + } + + try { + const data = JSON.parse(p); + handleResponse(data); + testBuffer = ''; + successfullyParsedUpToPart = i; + } catch(e) { + // Expected when receiving fragmented JSON over UDS. + // The state machine will buffer and re-attempt on the next chunk. + } + } + + if (successfullyParsedUpToPart === parts.length - 1) { + window.wsStreamBuffer = ''; + } else { + window.wsStreamBuffer = parts.slice(successfullyParsedUpToPart + 1).join('\n'); } }; @@ -261,9 +303,6 @@ function connect() { }; } -window.activeTls = []; -window.certStatusCache = {}; - function sendReq(obj) { if (ws && ws.readyState === WebSocket.OPEN) { console.log('[INSTRUMENT] sendReq: Dispatching payload -> ', obj); @@ -332,6 +371,13 @@ function handleResponse(data) { window.domainsCache = data.domains || []; console.log('[DEBUG] Calling renderDomains with cache length:', window.domainsCache.length); renderDomains(window.domainsCache); + setTimeout(() => { + sendReq({ req: 'get_all_tls' }); + }, 50); + + setTimeout(() => { + sendReq({ req: 'get_dist_server_domain' }); + }, 100); let didSelect = false; // Restore saved domain state if possible @@ -340,7 +386,7 @@ function handleResponse(data) { console.log('[DEBUG] No currentDomain. Found saved domain in localStorage:', saved); if (saved && window.domainsCache.find(d => d.name === saved)) { console.log('[DEBUG] Restoring saved domain state:', saved); - selectDomain(saved); + setTimeout(() => selectDomain(saved), 150); didSelect = true; } else if (saved) { console.warn('[DEBUG] Saved domain not found in domainsCache:', saved); @@ -359,7 +405,7 @@ function handleResponse(data) { // Bootstrap phase 2: now safe to fetch suffix config if (!didSelect) { window.didBootstrapPhase2 = true; - sendReq({ req: 'get_ipv6_suffix' }); + setTimeout(() => sendReq({ req: 'get_ipv6_suffix' }), 150); } break; case 'create_domain': @@ -405,12 +451,41 @@ function handleResponse(data) { document.getElementById('acme-country').value = data.config.country || ''; document.getElementById('acme-state').value = data.config.state || ''; document.getElementById('acme-locality').value = data.config.locality || ''; + if (document.getElementById('acme-profile')) { + document.getElementById('acme-profile').value = data.config.profile || ''; + window.currentAcmeProfile = data.config.profile || ''; + } } + sendReq({ req: 'get_acme_profiles' }); sendReq({ req: 'get_acme_log' }); break; + case 'get_acme_profiles': + if (data.profiles && typeof data.profiles === 'object') { + let select = document.getElementById('acme-profile'); + if (select) { + let currentValue = window.currentAcmeProfile || select.value || ''; + select.innerHTML = ''; + for (let p in data.profiles) { + let opt = document.createElement('option'); + opt.value = p; + opt.textContent = p.charAt(0).toUpperCase() + p.slice(1) + ' (' + p + ')'; + select.appendChild(opt); + } + if (currentValue && data.profiles[currentValue]) { + select.value = currentValue; + } + select.onchange = function() { + window.currentAcmeProfile = this.value; + }; + } + } + break; case 'set_acme_config': showToast('ACME configuration saved'); break; + case 'set_domain_acme': + showToast('Domain ACME preference saved'); + break; case 'get_acme_log': if (data.log) { const logEl = document.getElementById('acme-log'); @@ -421,26 +496,67 @@ function handleResponse(data) { } break; case 'get_tls': - console.log('[DEBUG] get_tls response received. data.tls:', data.tls); + console.log('[DEBUG] get_tls response received. data.domain:', data.domain, 'data.tls:', data.tls); if (data.tls) { - window.activeTls = data.tls; + if (data.domain === currentDomain) window.activeTls = data.tls; + window.allTlsCache[data.domain || currentDomain] = data.tls; + + // Enqueue background cert checks for all these endpoints + data.tls.forEach(t => { + let cacheKey = t.fqdn + ':' + t.port; + if (!window.certStatusCache[cacheKey] && !certCheckTimers[t.fqdn] && !certCheckQueue.some(q => q.fqdn === t.fqdn && q.port === t.port)) { + certCheckQueue.push({fqdn: t.fqdn, port: t.port, domain: data.domain}); + } + }); } else { - window.activeTls = []; + if (data.domain === currentDomain) window.activeTls = []; + window.allTlsCache[data.domain || currentDomain] = []; } console.log('[DEBUG] activeTls set. Length:', window.activeTls.length); - if (currentZone) { + if (currentZone && data.domain === currentDomain) { console.log('[DEBUG] Calling renderZoneTable from get_tls'); renderZoneTable(); - } else { + } else if (data.domain === currentDomain) { console.log('[DEBUG] No currentZone available in get_tls'); } processCertQueue(); + updateGlobalTlsTable(); + updateDistClientsTable(); + if (!window.didBootstrapPhase2) { window.didBootstrapPhase2 = true; sendReq({ req: 'get_ipv6_suffix' }); } break; + case 'get_all_tls': + if (data.all_tls) { + data.all_tls.forEach(d => { + window.allTlsCache[d.domain] = d.tls; + if (d.domain === currentDomain) window.activeTls = d.tls; + d.tls.forEach(t => { + let cacheKey = t.fqdn + ':' + t.port; + if (!window.certStatusCache[cacheKey] && !certCheckTimers[t.fqdn] && !certCheckQueue.some(q => q.fqdn === t.fqdn && q.port === t.port)) { + certCheckQueue.push({fqdn: t.fqdn, port: t.port, domain: d.domain}); + } + }); + }); + } + if (currentZone) { + renderZoneTable(); + } + processCertQueue(); + updateGlobalTlsTable(); + updateDistClientsTable(); + break; + case 'get_dist_server_domain': + if (data.status === 'ok' && data.domain) { + const domainInput = document.getElementById('dist-server-domain'); + if (domainInput && !domainInput.value) { + domainInput.value = data.domain; + } + } + break; case 'create_tls': showToast('TLS Port mapped successfully'); sendReq({ req: 'get_tls', domain: currentDomain }); @@ -450,12 +566,16 @@ function handleResponse(data) { sendReq({ req: 'get_tls', domain: currentDomain }); break; case 'cert_status': + console.log('[INSTRUMENT] cert_status RECEIVED for:', data.subdomain, data.port, 'status:', data.status, 'msg:', data.msg); window.certStatusCache[data.subdomain + ':' + data.port] = data; let span = document.getElementById(`cert-status-${data.subdomain}`); if (certCheckTimers[data.subdomain]) { + console.log('[INSTRUMENT] CLEARING timer for:', data.subdomain); clearTimeout(certCheckTimers[data.subdomain]); delete certCheckTimers[data.subdomain]; concurrentChecks = Math.max(0, concurrentChecks - 1); + } else { + console.log('[INSTRUMENT] NO TIMER FOUND to clear for:', data.subdomain); } if (span) { if (data.status === 'ok') { @@ -466,16 +586,84 @@ function handleResponse(data) { span.classList.remove('text-green', 'text-gray'); span.classList.add('text-red'); } } - updateTlsSummary(); - let modal = document.getElementById('modal-tls-details'); - if (modal && !modal.classList.contains('hidden-panel') && modal.classList.contains('show')) { - renderTlsDetailsModal(); - } + updateGlobalTlsTable(); + renderZoneTable(); + + // Re-trigger the queue to process the next one processCertQueue(); break; + case 'download_dist_ca': + if (data.status === 'ok') { + const blob = new Blob([data.ca], { type: 'application/x-x509-ca-cert' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `distribution-ca.crt`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + showToast('CA Certificate downloaded'); + } + break; + case 'download_dist_server': + if (data.status === 'ok') { + const triggerDownload = (content, filename) => { + const blob = new Blob([content], { type: 'application/x-pem-file' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }; + + const srvPem = `${data.cert}\n${data.ca}\n`; + const filenameBase = data.domain ? `distribution-server-${data.domain}` : 'distribution-server'; + triggerDownload(srvPem, `${filenameBase}.crt`); + + setTimeout(() => { + triggerDownload(`${data.key}\n`, `${filenameBase}.key`); + }, 100); + + showToast('Server Certificate and Key downloaded'); + } else if (data.status === 'error') { + showToast(data.msg || 'Error fetching server certificates', true); + } + break; + case 'provisioning_bundle': + if (data.status === 'ok') { + const triggerDownload = (content, filename) => { + const blob = new Blob([content], { type: 'application/x-pem-file' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }; + + triggerDownload(`${data.cert}\n`, `distribution-client-${data.subdomain}.crt`); + + setTimeout(() => { + triggerDownload(`${data.key}\n`, `distribution-client-${data.subdomain}.key`); + }, 100); + + showToast('Provisioning keys downloaded'); + } + break; } } +function downloadProvisioningBundle(domain, subdomain) { + showToast('Preparing provisioning bundle...'); + sendReq({ req: 'provisioning_bundle', domain: domain, subdomain: subdomain }); +} + function renderDomains(domains) { console.log('[DEBUG] renderDomains called with domains:', domains); const tbody = document.querySelector('#table-domains tbody'); @@ -565,6 +753,14 @@ function selectDomain(domain) { currentDomainObj = window.domainsCache ? window.domainsCache.find(d => d.name === domain) : null; console.log('[DEBUG] currentDomainObj set to:', currentDomainObj); + const cbAcme = document.getElementById('cb-domain-acme-enable'); + if (cbAcme && currentDomainObj) { + cbAcme.checked = currentDomainObj.acme_enabled === true; + cbAcme.onchange = function() { + sendReq({ req: 'set_domain_acme', domain: currentDomain, enabled: this.checked }); + }; + } + document.querySelector('#detail-title span').textContent = domain; document.getElementById('detail-panel')?.classList.remove('hidden-panel'); document.getElementById('domain-panel')?.classList.add('hidden-panel'); @@ -577,9 +773,11 @@ function selectDomain(domain) { }); renderWhoisHeader(); - window.activeTls = []; - console.log('[DEBUG] dispatching get_zone from selectDomain'); - sendReq({ req: 'get_zone', domain: domain }); + sendReq({ req: 'get_tls', domain: domain }); + setTimeout(() => { + console.log('[DEBUG] dispatching get_zone from selectDomain'); + sendReq({ req: 'get_zone', domain: domain }); + }, 50); } function formatExpiryInterval(timestampSec) { @@ -602,50 +800,86 @@ function renderWhoisHeader() { } const w = currentDomainObj.whois; - const d = currentDomainObj.dns || {}; const expiryDateStr = w.expiry_date ? new Date(w.expiry_date * 1000).toLocaleDateString() : 'Unknown'; const expiryInterval = w.expiry_date ? formatExpiryInterval(w.expiry_date) : ''; const expiryDate = w.expiry_date ? `${expiryDateStr} (${expiryInterval})` : 'Unknown'; const nsList = (w.nameservers || []).join('
') || 'None'; - const dnsSerial = d.serial !== undefined ? d.serial : 'Unknown'; - let isDnsExpired = false; - if (d.expiry) { - if (d.expiry < Math.floor(Date.now() / 1000)) { - isDnsExpired = true; - } - } - const dnsSignedOk = d.signed_ok === 1 && !isDnsExpired; - let dnsExpiryStr = formatExpiryInterval(d.expiry); let dsStatusHTML = ''; - let isMatch = false; let isSigned = false; + let localMismatch = false; + let globalMismatch = false; + + const localDs = (currentDomainObj.local_ds || '').trim().toUpperCase(); - if (w.ds_data) { - const localDs = (currentDomainObj.local_ds || '').trim().toUpperCase(); - const whoisDs = w.ds_data.trim().toUpperCase(); - if (localDs && whoisDs) { - isMatch = whoisDs.includes(localDs) || localDs.includes(whoisDs); + if (currentDomainObj.dns_ds) { + const dnsDs = currentDomainObj.dns_ds.trim().toUpperCase(); + if (localDs && dnsDs && !dnsDs.includes(localDs) && !localDs.includes(dnsDs)) { + localMismatch = true; } - dsStatusHTML = `DNSSEC: ${isMatch ? '✔' : '✘'}`; - } else { - let dnssecVal = w.dnssec ? w.dnssec.trim().toLowerCase() : ''; - if (dnssecVal === 'signeddelegation' || dnssecVal === 'yes' || dnssecVal === 'signed' || dnssecVal === 'active') { - isSigned = true; + } + + if (currentDomainObj.dns_ds_global) { + const dnsDsGlobal = currentDomainObj.dns_ds_global.trim().toUpperCase(); + if (localDs && dnsDsGlobal && !dnsDsGlobal.includes(localDs) && !localDs.includes(dnsDsGlobal)) { + globalMismatch = true; } - dsStatusHTML = `DNSSEC Delegation: ${isSigned ? '✔' : '⚠'}`; + } + + let dnssecVal = w.dnssec ? w.dnssec.trim().toLowerCase() : ''; + if (dnssecVal === 'signeddelegation' || dnssecVal === 'yes' || dnssecVal === 'signed' || dnssecVal === 'active') { + isSigned = true; + } + dsStatusHTML = `DNSSEC Delegation: ${isSigned ? '✔' : '⚠'}`; + + const extractKeyTag = (dsStr) => { + if (!dsStr) return 'Missing'; + const parts = dsStr.trim().split(/\s+/); + return parts.length > 0 ? parts[0] : 'Unknown'; + }; + + if (currentDomainObj.dns_ds || currentDomainObj.dns_ds_global) { + dsStatusHTML += `
`; + if (currentDomainObj.dns_ds_global) { + const globalTag = extractKeyTag(currentDomainObj.dns_ds_global); + dsStatusHTML += `
Global DNS Cache (8.8.8.8): ${globalMismatch ? `✘ Disagrees with Local Keys (${globalTag})` : `✔ Matches Local Keys (${globalTag})`}
`; + } + if (currentDomainObj.dns_ds) { + const systemTag = extractKeyTag(currentDomainObj.dns_ds); + dsStatusHTML += `
System DNS Cache: ${localMismatch ? `✘ Disagrees with Local Keys (${systemTag})` : `✔ Matches Local Keys (${systemTag})`}
`; + } + dsStatusHTML += `
`; + } + + if (localMismatch || globalMismatch) { + dsStatusHTML += `
âš  Registrar DNSSEC key differs`; } if (currentDomainObj.alg) { dsStatusHTML += `
Key: ${currentDomainObj.alg}   replace   info`; } - let overallSigned = (w.ds_data ? isMatch : isSigned) && dnsSignedOk; + let overallSigned = isSigned && !localMismatch && !globalMismatch; let overrideClass = overallSigned ? 'ok' : 'warn'; let overrideText = overallSigned ? '✔ DNSSEC
OK' : '✘
INSECURE'; - let sigsColor = !d.expiry ? 'dns-fg-gray' : (isDnsExpired ? 'dns-fg-gray' : (dnsSignedOk ? 'dns-fg-green' : 'dns-fg-red')); - let sigsTick = !d.expiry ? 'n/a' : (isDnsExpired ? '⚠' : (dnsSignedOk ? '✔' : '✘')); + let dnssecLookupsHTML = ''; + if (localMismatch || globalMismatch) { + dnssecLookupsHTML = ` + + +
+ âš  DNS Lookup Failed due to DNSSEC Error +
+ + `; + } else if (isSigned) { + dnssecLookupsHTML = ` + + DNS: + ✔ Lookup OK (Validated Local & Global) + `; + } hdr.innerHTML = ` @@ -657,14 +891,10 @@ function renderWhoisHeader() { ${dsStatusHTML} - - - - - + ${dnssecLookupsHTML} - +
DNS: ${dnsSerial}Sigexp: ${dnsExpiryStr}Sigs: ${sigsTick}
TLS:Loading...   View TLS DetailsLoading...
`; @@ -728,60 +958,32 @@ function renderWhoisHeader() { if (lnkDetails) { lnkDetails.onclick = (e) => { e.preventDefault(); - renderTlsDetailsModal(); - let modal = document.getElementById('modal-tls-details'); - if (modal) modal.classList.add('show'); + document.getElementById('tab-btn-tls').click(); }; } } -function updateTlsSummary() { - const row = document.getElementById('tls-summary-row'); - const content = document.getElementById('tls-summary-content'); - if (!row || !content) return; - - if (!window.activeTls || window.activeTls.length === 0) { - row.classList.add('hide'); - return; - } - - let minDays = null; - window.activeTls.forEach(t => { - let cached = window.certStatusCache[t.fqdn + ':' + t.port]; - if (cached && cached.status === 'ok') { - // parse days from local_msg or msg - let parseDays = (str) => { - if (!str) return null; - let m = str.match(/(\d+)\s*days?/i); - return m ? parseInt(m[1], 10) : null; - }; - let d1 = parseDays(cached.local_msg); - let d2 = parseDays(cached.msg); - let d = d1 !== null ? d1 : (d2 !== null ? d2 : null); - if (d !== null) { - if (minDays === null || d < minDays) minDays = d; - } - } - }); - - let expStr = minDays !== null ? `Min Expiry: ${minDays}d` : 'Checking...'; - content.innerText = `TLS: ${window.activeTls.length} certs, ${expStr}`; - row.classList.remove('hide'); -} - -function renderTlsDetailsModal() { - let domainNameEl = document.getElementById('tls-summary-domain-name'); - if (domainNameEl) domainNameEl.textContent = currentDomain; - const tbody = document.querySelector('#table-tls-details tbody'); +function updateGlobalTlsTable() { + const tbody = document.querySelector('#table-all-tls tbody'); if (!tbody) return; tbody.innerHTML = ''; - if (!window.activeTls || window.activeTls.length === 0) { - tbody.innerHTML = 'No TLS subdomains configured.'; + let allTlsList = []; + for (const [dom, tlsArr] of Object.entries(window.allTlsCache)) { + if (Array.isArray(tlsArr)) { + tlsArr.forEach(t => allTlsList.push({ domain: dom, ...t })); + } + } + + if (allTlsList.length === 0) { + tbody.innerHTML = 'No TLS subdomains configured across any domain.'; + + const row = document.getElementById('tls-summary-row'); + if (row) row.classList.add('hide'); return; } - window.activeTls.forEach(t => { + allTlsList.forEach(t => { const tr = document.createElement('tr'); let cached = window.certStatusCache[t.fqdn + ':' + t.port]; let locExp = 'Checking...'; @@ -795,8 +997,8 @@ function renderTlsDetailsModal() { issuer = cached.issuer || 'Unknown'; } else { remExp = `${cached.msg}`; - locExp = 'Error'; - issuer = 'Error'; + locExp = cached.local_msg || 'Error'; + issuer = cached.issuer || 'Error'; } } @@ -805,7 +1007,90 @@ function renderTlsDetailsModal() { ${locExp} ${remExp} ${issuer} + + `; + const btn = tr.querySelector('.btn-provision'); + if (btn) btn.addEventListener('click', () => document.getElementById('tab-btn-dist').click()); + tbody.appendChild(tr); + }); + + const row = document.getElementById('tls-summary-row'); + const content = document.getElementById('tls-summary-content'); + if (row && content) { + let minDays = null; + let allChecked = true; + let anyError = false; + + allTlsList.forEach(t => { + if (t.domain !== currentDomain) return; + let cached = window.certStatusCache[t.fqdn + ':' + t.port]; + if (!cached) { + allChecked = false; + return; + } + if (cached.status === 'ok') { + let parseDays = (str) => { + if (!str) return null; + let m = str.match(/(\d+)\s*days?/i); + return m ? parseInt(m[1], 10) : null; + }; + let d1 = parseDays(cached.local_msg); + let d2 = parseDays(cached.msg); + let d = d1 !== null ? d1 : (d2 !== null ? d2 : null); + if (d !== null) { + if (minDays === null || d < minDays) minDays = d; + } else { + anyError = true; + } + } else { + anyError = true; + } + }); + + let localTlsCount = allTlsList.filter(t => t.domain === currentDomain).length; + if (localTlsCount > 0) { + let expStr = 'Checking...'; + if (minDays !== null) { + expStr = `Min Expiry: ${minDays}d`; + if (anyError) expStr += ' (Some Errors)'; + } else if (allChecked) { + expStr = anyError ? 'Error/Timeout' : 'Unknown'; + } + + content.innerText = `TLS: ${localTlsCount} certs, ${expStr}`; + row.classList.remove('hide'); + } else { + row.classList.add('hide'); + } + } +} + +function updateDistClientsTable() { + const tbody = document.querySelector('#table-dist-clients tbody'); + if (!tbody) return; + tbody.innerHTML = ''; + + let allTlsList = []; + for (const [dom, tlsArr] of Object.entries(window.allTlsCache)) { + if (Array.isArray(tlsArr)) { + tlsArr.forEach(t => allTlsList.push({ domain: dom, ...t })); + } + } + + if (allTlsList.length === 0) { + tbody.innerHTML = 'No TLS endpoints to provision.'; + return; + } + + allTlsList.forEach(t => { + const tr = document.createElement('tr'); + tr.innerHTML = ` + ${t.domain} + ${t.fqdn}:${t.port} + `; + const btn = tr.querySelector('.btn-dist-gen'); + if (btn) btn.addEventListener('click', () => downloadProvisioningBundle(t.domain, t.fqdn)); tbody.appendChild(tr); }); } @@ -929,15 +1214,17 @@ function renderZoneTable() { concurrentChecks = Math.max(0, concurrentChecks - 1); } delete window.certStatusCache[fqdn + ':' + pnum]; - certCheckQueue.push({fqdn: fqdn, port: pnum}); + if (!certCheckQueue.some(q => q.fqdn === fqdn && q.port === pnum)) { + certCheckQueue.push({fqdn: fqdn, port: pnum, domain: currentDomain}); + } processCertQueue(); } }; if (portInput.value) { let pnum = parseInt(portInput.value, 10); - if (!window.certStatusCache[fqdn + ':' + pnum] && !certCheckTimers[fqdn]) { - certCheckQueue.push({fqdn: fqdn, port: pnum}); + if (!window.certStatusCache[fqdn + ':' + pnum] && !certCheckTimers[fqdn] && !certCheckQueue.some(q => q.fqdn === fqdn && q.port === pnum)) { + certCheckQueue.push({fqdn: fqdn, port: pnum, domain: currentDomain}); } } } @@ -1053,12 +1340,51 @@ function openEditor(id) { } } - document.addEventListener('DOMContentLoaded', () => { +function initApp() { if (typeof window.renderLwsLoginStatus === 'function') { window.renderLwsLoginStatus('user-info'); } connect(); + // Tab Switching Logic + const tabs = document.querySelectorAll('.tab-link'); + const tabContents = document.querySelectorAll('.tab-content'); + for (let i = 0; i < tabs.length; i++) { + let tab = tabs[i]; + tab.addEventListener('click', () => { + console.log("Tab clicked: " + tab.getAttribute('data-tab')); + for (let j = 0; j < tabs.length; j++) tabs[j].classList.remove('active'); + for (let j = 0; j < tabContents.length; j++) tabContents[j].classList.remove('active'); + + tab.classList.add('active'); + const target = document.getElementById(tab.getAttribute('data-tab')); + if (target) target.classList.add('active'); + }); + } + + const btnDownloadCa = document.getElementById('btn-dist-download-ca'); + if (btnDownloadCa) btnDownloadCa.onclick = () => sendReq({ req: 'download_dist_ca' }); + + const btnDownloadServer = document.getElementById('btn-dist-download-server'); + const domainInput = document.getElementById('dist-server-domain'); + if (domainInput) { + const saveDomain = () => { + sendReq({ req: 'set_dist_server_domain', domain: domainInput.value.trim() }); + }; + domainInput.addEventListener('change', saveDomain); + domainInput.addEventListener('blur', saveDomain); + } + if (btnDownloadServer) { + btnDownloadServer.onclick = () => { + if (domainInput && domainInput.value.trim() !== '') { + sendReq({ req: 'set_dist_server_domain', domain: domainInput.value.trim() }); + sendReq({ req: 'download_dist_server', domain: domainInput.value.trim() }); + } else { + alert('Please enter a valid domain for the Distribution Server.'); + } + }; + } + const rawEditor = document.getElementById('raw-zone-editor'); // Centralized substitution logic @@ -1086,10 +1412,27 @@ function openEditor(id) { if (ext_ipv6) subs.push({ key: 'EXTIP6', val: ext_ipv6 }); if (window.activeTls) { + console.log("getSubstitutions: window.activeTls has", window.activeTls.length, "items", window.activeTls); window.activeTls.forEach(t => { - if (t.dane0) subs.push({ key: `DANE0/${t.fqdn}`, val: t.dane0 }); - if (t.dane1) subs.push({ key: `DANE1/${t.fqdn}`, val: t.dane1 }); + console.log("getSubstitutions: checking TLS item:", t); + let fullFqdn = t.fqdn === '@' ? currentDomain : t.fqdn + '.' + currentDomain; + if (t.dane0) { + subs.push({ key: `DANE0/${t.fqdn}`, val: t.dane0 }); + subs.push({ key: `DANE0/${fullFqdn}`, val: t.dane0 }); + console.log(`getSubstitutions: Added DANE0 for ${fullFqdn}`); + } else { + console.log(`getSubstitutions: t.dane0 is falsy/missing for ${fullFqdn}`); + } + if (t.dane1) { + subs.push({ key: `DANE1/${t.fqdn}`, val: t.dane1 }); + subs.push({ key: `DANE1/${fullFqdn}`, val: t.dane1 }); + console.log(`getSubstitutions: Added DANE1 for ${fullFqdn}`); + } else { + console.log(`getSubstitutions: t.dane1 is falsy/missing for ${fullFqdn}`); + } }); + } else { + console.log("getSubstitutions: window.activeTls is falsy/missing!"); } console.log("getSubstitutions returning:", subs); @@ -1102,6 +1445,11 @@ function openEditor(id) { let regex = new RegExp(`\\$\\{${sub.key}\\}`, 'g'); text = text.replace(regex, `\$\{${sub.key}\}` + '\u2007'.repeat(sub.val.length)); }); + + // Unconditionally pad any DANE0/DANE1 macro with 70 chars if it hasn't been padded yet + // The DANE hash format is "3 1 1 <64_char_hex>" which is 70 characters. + text = text.replace(/(\$\{DANE[01]\/[^}]+?\})(?!\u2007)/g, '$1' + '\u2007'.repeat(70)); + return text; }; @@ -1336,11 +1684,18 @@ function openEditor(id) { organization: document.getElementById('acme-org').value.trim(), country: document.getElementById('acme-country').value.trim(), state: document.getElementById('acme-state').value.trim(), - locality: document.getElementById('acme-locality').value.trim() + locality: document.getElementById('acme-locality').value.trim(), + profile: document.getElementById('acme-profile') ? document.getElementById('acme-profile').value : '' }); }; } -}); +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initApp); +} else { + initApp(); +} function openModal(id) { document.getElementById(id).classList.add('show'); diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-acme.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-acme.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-api.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-api.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-dnssec.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-dnssec.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-pki.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-pki.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-proxy.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-proxy.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-utils.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-utils.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/monitor-whois.c b/plugins/protocol_lws_dht_dnssec_monitor/monitor-whois.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/private.h b/plugins/protocol_lws_dht_dnssec_monitor/private.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/protocol_lws_dht_dnssec_monitor/protocol_lws_dht_dnssec_monitor.c b/plugins/protocol_lws_dht_dnssec_monitor/protocol_lws_dht_dnssec_monitor.c index a37c25d647..f8c30000b3 100644 --- a/plugins/protocol_lws_dht_dnssec_monitor/protocol_lws_dht_dnssec_monitor.c +++ b/plugins/protocol_lws_dht_dnssec_monitor/protocol_lws_dht_dnssec_monitor.c @@ -22,12 +22,10 @@ * DNSSEC signing tasks over operations exported by lws-dht-dnssec. */ -#define _GNU_SOURCE #if !defined (LWS_PLUGIN_STATIC) #define LWS_DLL #define LWS_INTERNAL #include -#include #endif #if !defined(_GNU_SOURCE) @@ -39,182 +37,16 @@ #include #include #include -#include -#include #if defined(WIN32) || defined(_WIN32) #else #include -#include -#include #endif -#include -#include - -struct monitor_req_args { - char req[32]; - char domain[128]; - char subdomain[128]; - char email[128]; - char organization[128]; - char directory_url[256]; - char *zone_buf; - int zone_len; - int zone_alloc; - char jwt[2048]; - char suffix[64]; - char key_type[32]; - int port; - int enabled; - int production; - char country[8]; - char state[128]; - char locality[128]; -}; - -static const char * const monitor_req_paths[] = { - "req", - "domain", - "subdomain", - "email", - "organization", - "directory_url", - "zone", - "jwt", - "suffix", - "key_type", - "port", - "enabled", - "production", - "country", - "state", - "locality" -}; - -enum enum_req_paths { - LRP_REQ, - LRP_DOMAIN, - LRP_SUBDOMAIN, - LRP_EMAIL, - LRP_ORG, - LRP_DIR_URL, - LRP_ZONE, - LRP_JWT, - LRP_SUFFIX, - LRP_KEY_TYPE, - LRP_PORT, - LRP_ENABLED, - LRP_PRODUCTION, - LRP_COUNTRY, - LRP_STATE, - LRP_LOCALITY -}; - -static signed char -monitor_req_cb(struct lejp_ctx *ctx, char reason) -{ - struct monitor_req_args *a = (struct monitor_req_args *)ctx->user; - - if (reason == LEJPCB_VAL_STR_START) { - if (ctx->path_match - 1 == LRP_ZONE) { - a->zone_len = 0; - } - } - - if (reason == LEJPCB_VAL_NUM_INT) { - if (ctx->path_match - 1 == LRP_PORT) { - a->port = atoi(ctx->buf); - lwsl_notice("[INSTRUMENT] monitor_req_cb: Parsed port natively from JSON INT: %d\n", a->port); - } - } - - if (reason == LEJPCB_VAL_TRUE) { - if (ctx->path_match - 1 == LRP_ENABLED) a->enabled = 1; - if (ctx->path_match - 1 == LRP_PRODUCTION) a->production = 1; - } - - if (reason == LEJPCB_VAL_FALSE) { - if (ctx->path_match - 1 == LRP_ENABLED) a->enabled = 0; - if (ctx->path_match - 1 == LRP_PRODUCTION) a->production = 0; - } - - if (reason == LEJPCB_VAL_STR_CHUNK || reason == LEJPCB_VAL_STR_END) { - switch (ctx->path_match - 1) { - case LRP_REQ: - lws_strncpy(a->req, ctx->buf, sizeof(a->req)); - break; - case LRP_DOMAIN: - lws_strncpy(a->domain, ctx->buf, sizeof(a->domain)); - break; - case LRP_SUBDOMAIN: - lws_strncpy(a->subdomain, ctx->buf, sizeof(a->subdomain)); - break; - case LRP_EMAIL: - lws_strncpy(a->email, ctx->buf, sizeof(a->email)); - break; - case LRP_ORG: - lws_strncpy(a->organization, ctx->buf, sizeof(a->organization)); - break; - case LRP_DIR_URL: - lws_strncpy(a->directory_url, ctx->buf, sizeof(a->directory_url)); - break; - case LRP_ZONE: - if (!a->zone_buf) { - a->zone_alloc = 8192; - a->zone_buf = malloc((size_t)a->zone_alloc); - if (!a->zone_buf) return -1; - } - if (a->zone_len + ctx->npos >= a->zone_alloc) { - a->zone_alloc *= 2; - char *nb = realloc(a->zone_buf, (size_t)a->zone_alloc); - if (!nb) return -1; - a->zone_buf = nb; - } - memcpy(a->zone_buf + a->zone_len, ctx->buf, ctx->npos); - a->zone_len += ctx->npos; - if (reason == LEJPCB_VAL_STR_END) { - a->zone_buf[a->zone_len] = '\0'; - } - break; - case LRP_JWT: - lws_strncpy(a->jwt, ctx->buf, sizeof(a->jwt)); - break; - case LRP_SUFFIX: - lws_strncpy(a->suffix, ctx->buf, sizeof(a->suffix)); - break; - case LRP_KEY_TYPE: - lws_strncpy(a->key_type, ctx->buf, sizeof(a->key_type)); - break; - case LRP_PORT: - a->port = atoi(ctx->buf); - break; - case LRP_COUNTRY: - lws_strncpy(a->country, ctx->buf, sizeof(a->country)); - break; - case LRP_STATE: - lws_strncpy(a->state, ctx->buf, sizeof(a->state)); - break; - case LRP_LOCALITY: - lws_strncpy(a->locality, ctx->buf, sizeof(a->locality)); - break; - } - } - - if (reason == LEJPCB_FAILED) { - lwsl_err("[INSTRUMENT] monitor_req_cb: LEJP JSON Parse FAILED at struct offset %d\n", (int)ctx->st[ctx->sp].s); - } - - return 0; -} - -struct whois_query_info { - lws_dll2_t list; - char domain[128]; - struct vhd *vhd; -}; +#define PSS_MAGIC 0x50535301 struct pss { + uint32_t magic; struct lws *wsi; struct lws *cwsi; @@ -233,12 +65,6 @@ struct pss { int send_ext_ips; }; -struct published_jws_info { - lws_dll2_t list; - char domain[128]; - time_t mtime; -}; - struct vhd { struct lws_context *context; struct lws_vhost *vhost; @@ -268,43 +94,48 @@ struct vhd { lws_dll2_owner_t ui_clients; struct lws_smd_peer *smd_peer; char ext_ips[256]; - lws_dll2_owner_t completed_checks; - lws_dll2_owner_t whois_queries; - lws_dll2_owner_t published_jws; - lws_sorted_usec_list_t sul_timer_scan; - lws_sorted_usec_list_t sul_timer_proxy_scan; - int acme_enabled; + /* ACME client configuration state */ int acme_production; char acme_email[128]; - char acme_organization[128]; - char acme_country[8]; - char acme_state[128]; - char acme_locality[128]; - lws_dll2_owner_t active_probes; + char acme_profile[128]; + + uid_t proxy_uid; + gid_t proxy_gid; + + /* UDS Proxy clients queue */ + lws_dll2_owner_t clients; +}; + +#define ACME_PROFILES_MAGIC 0xAC3E0001 +struct acme_profiles_fetch_info { + uint32_t magic; + struct pss *root_pss; + char *json; + size_t json_len; + size_t json_alloc; }; +#define CERT_CHECK_MAGIC 0xCE670001 struct cert_check_info { - lws_dll2_t active_list; uint32_t magic; - char domain[128]; char fqdn[128]; + char domain[128]; int port; - int starttls_state; - int is_automated; + int starttls_state; /* 0=none, 1=wait 220, 2=sent EHLO, 3=wait 250, 4=sent STARTTLS, 5=wait 220 */ }; -#define CERT_CHECK_MAGIC 0xCE87C4EC struct cert_check_result { lws_dll2_t list; char fqdn[128]; - char msg[128]; - char local_msg[128]; - char issuer[128]; int port; int status_err; + char msg[256]; + char local_msg[128]; + char issuer[128]; }; + static struct vhd *global_root_vhd = NULL; extern const struct lws_protocols lws_dht_dnssec_monitor_protocols[]; @@ -338,1279 +169,734 @@ struct parsed_config { struct vhd *vhd; char common_name[256]; char email[256]; - char key_type[64]; - char key_curve[64]; - int key_bits; }; -static void -whois_cb(void *opaque, const struct lws_whois_results *res) -{ - struct whois_query_info *wqi = (struct whois_query_info *)opaque; - int n; - char buf[2048]; - char ns_list[1024] = ""; - - lwsl_notice("[INSTRUMENT] %s: callback triggered for %s. res is %s\n", __func__, wqi->domain, res ? "NOT NULL" : "NULL"); - - char s_dnssec[256] = "", s_ds[1024] = ""; - if (res) { - lws_strncpy(s_dnssec, res->dnssec, sizeof(s_dnssec)); - lws_strncpy(s_ds, res->ds_data, sizeof(s_ds)); - for (size_t i = 0; i < strlen(s_dnssec); i++) { - if (!isalnum((unsigned char)s_dnssec[i]) && s_dnssec[i] != '-' && s_dnssec[i] != '.' && s_dnssec[i] != ':' && s_dnssec[i] != ' ') - s_dnssec[i] = ' '; - } - for (size_t i = 0; i < strlen(s_ds); i++) { - if (!isalnum((unsigned char)s_ds[i]) && s_ds[i] != '-' && s_ds[i] != '.' && s_ds[i] != ':' && s_ds[i] != ' ') - s_ds[i] = ' '; - } - - char *p_ns, *token, *saveptr; - /* Convert comma-separated nameservers to JSON array of strings */ - p_ns = strdup(res->nameservers); - if (p_ns) { - token = strtok_r(p_ns, ", ", &saveptr); - while (token) { - char stoken[256]; - lws_strncpy(stoken, token, sizeof(stoken)); - for (size_t i = 0; i < strlen(stoken); i++) { - if (!isalnum((unsigned char)stoken[i]) && stoken[i] != '-' && stoken[i] != '.' && stoken[i] != ':') - stoken[i] = ' '; - } - - if (ns_list[0]) - strncat(ns_list, ", ", sizeof(ns_list) - strlen(ns_list) - 1); - strncat(ns_list, "\"", sizeof(ns_list) - strlen(ns_list) - 1); - strncat(ns_list, stoken, sizeof(ns_list) - strlen(ns_list) - 1); - strncat(ns_list, "\"", sizeof(ns_list) - strlen(ns_list) - 1); - token = strtok_r(NULL, ", ", &saveptr); - } - free(p_ns); - } +static const char * const config_paths[] = { + "common-name", + "email", +}; +enum enum_config_paths { + LEJP_CONF_COMMON_NAME, + LEJP_CONF_EMAIL, +}; - n = lws_snprintf(buf, sizeof(buf), - "{\n \"creation_date\": %llu,\n \"expiry_date\": %llu,\n \"updated_date\": %llu,\n" - " \"nameservers\": [%s],\n" - " \"dnssec\": \"%s\",\n \"ds_data\": \"%s\",\n \"last_query\": %llu\n}\n", - (unsigned long long)res->creation_date, (unsigned long long)res->expiry_date, - (unsigned long long)res->updated_date, - ns_list, s_dnssec, s_ds, (unsigned long long)lws_now_secs()); - - lwsl_notice("[INSTRUMENT] whois_cb: formatted JSON for %s, size = %d\n", wqi->domain, n); - } else { - lwsl_notice("[INSTRUMENT] whois_cb: res is NULL for %s, skipping UDS publish\n", wqi->domain); - n = 0; /* Let it organically fail or retry without caching `{}` */ - } - - if (n > 0) { - char b64[8192] = {0}, jwt[1024] = {0}, uds_json[10240] = {0}, temp[2048] = {0}; - size_t jwt_len = sizeof(jwt); - lws_b64_encode_string(buf, (int)strlen(buf), b64, sizeof(b64)); - - if (wqi->vhd->auth_jwk.kty == LWS_GENCRYPTO_KTY_OCT) { - char jwt_payload[512]; - unsigned long now = (unsigned long)lws_now_secs(); - lws_snprintf(jwt_payload, sizeof(jwt_payload), - "{\"iss\":\"acme-ipc\",\"aud\":\"dnssec-monitor\",\"nbf\":%lu,\"exp\":%lu}", - now, now + 300); - - if (lws_jwt_sign_compact(wqi->vhd->context, &wqi->vhd->auth_jwk, "HS256", - jwt, &jwt_len, temp, sizeof(temp), "%s", jwt_payload)) { - lwsl_err("[INSTRUMENT] %s: failed to generate jwt for whois\n", __func__); - } - } +static signed char +cb_conf(struct lejp_ctx *ctx, char reason) +{ + struct parsed_config *pc = (struct parsed_config *)ctx->user; - int payload_n = lws_snprintf(uds_json, sizeof(uds_json), - "{\"req\":\"update_whois\",\"domain\":\"%s\",\"jwt\":\"%s\",\"zone\":\"%s\"}\n", - wqi->domain, jwt, b64); - int fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd >= 0) { - struct sockaddr_un sun; - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - lws_strncpy(sun.sun_path, wqi->vhd->uds_path, sizeof(sun.sun_path)); - if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) { - if (write(fd, uds_json, (size_t)payload_n) < 0) { - lwsl_err("[INSTRUMENT] %s: Failed writing whois payload to UDS, errno: %d\n", __func__, errno); - } else { - lwsl_notice("[INSTRUMENT] %s: Tunneled WHOIS for %s to Root over UDS (payload %d bytes)\n", __func__, wqi->domain, payload_n); - } - } else { - lwsl_err("[INSTRUMENT] %s: Failed connecting to root UDS at %s for whois pass-back, errno: %d\n", __func__, sun.sun_path, errno); - } - close(fd); - } else { - lwsl_err("[INSTRUMENT] %s: socket creation failed! errno: %d\n", __func__, errno); + if (reason == LEJPCB_VAL_STR_END) { + switch (ctx->path_match - 1) { + case LEJP_CONF_COMMON_NAME: + lws_strncpy(pc->common_name, ctx->buf, sizeof(pc->common_name)); + break; + case LEJP_CONF_EMAIL: + lws_strncpy(pc->email, ctx->buf, sizeof(pc->email)); + break; } - - lws_dll2_remove(&wqi->list); - free(wqi); } + + return 0; } static int -whois_trigger(struct vhd *vhd, const char *domain) +scan_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) { - struct lws_whois_args args; - struct whois_query_info *wqi; - - wqi = malloc(sizeof(*wqi)); - if (!wqi) - return 1; + struct vhd *vhd = (struct vhd *)user; + char filepath[1024]; + int fd; + struct stat st; + char *buf; + struct parsed_config pc; + struct lejp_ctx jctx; - memset(wqi, 0, sizeof(*wqi)); - lws_strncpy(wqi->domain, domain, sizeof(wqi->domain)); - wqi->vhd = vhd; + if (lde->type != LDOT_DIR) + return 0; - memset(&args, 0, sizeof(args)); - args.context = vhd->context; - args.domain = domain; - args.cb = whois_cb; - args.opaque = wqi; + if (lde->name[0] == '.') + return 0; - lwsl_notice("%s: Triggering core WHOIS for %s\n", __func__, domain); + lws_snprintf(filepath, sizeof(filepath), "%s/%s/conf.d/%s.json", dirpath, lde->name, lde->name); - lws_dll2_add_tail(&wqi->list, &vhd->whois_queries); + fd = open(filepath, O_RDONLY); + if (fd < 0) + return 0; - if (lws_whois_query(&args)) { - lwsl_err("%s: Failed to trigger core WHOIS for %s\n", __func__, domain); - lws_dll2_remove(&wqi->list); - free(wqi); - return 1; + if (fstat(fd, &st) < 0 || st.st_size == 0) { + close(fd); + return 0; } - return 0; -} + buf = malloc((size_t)st.st_size + 1); + if (!buf) { + close(fd); + return 0; + } -static int -calc_local_ds(struct vhd *vhd, const char *domain, char *out, size_t out_len) -{ - char key_path[1024]; - int fd; - char buf[2048]; - - lws_snprintf(key_path, sizeof(key_path), "%s/domains/%s/%s.ksk.key", vhd->base_dir, domain, domain); - fd = open(key_path, O_RDONLY); - if (fd < 0) return 1; - - ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (read(fd, buf, (size_t)st.st_size) != st.st_size) { + free(buf); + close(fd); + return 0; + } + buf[st.st_size] = '\0'; close(fd); - if (n <= 0) return 1; - buf[n] = '\0'; - - /* Parse BIND format: domain IN DNSKEY flags proto alg b64 */ - char d[256], b64[1024]; - int flags, proto, alg; - if (sscanf(buf, "%255s IN DNSKEY %d %d %d %1023s", d, &flags, &proto, &alg, b64) != 5) - return 1; - - /* This is a simplification, ideally we'd use the ops->dsfromkey if it returned a string. - * But we can just use the command line or implement the hashing here if needed. - * For now, we'll just report we can't do it until we have a better way. - * Wait, let's actually implement it since we need it for the Red X / Green Tick. - */ - uint8_t rdata[2048]; - rdata[0] = (uint8_t)(flags >> 8); - rdata[1] = (uint8_t)(flags & 0xff); - rdata[2] = (uint8_t)proto; - rdata[3] = (uint8_t)alg; - int b64_len = lws_b64_decode_string(b64, (char *)rdata + 4, sizeof(rdata) - 4); - if (b64_len < 0) return 1; - - size_t rdata_len = 4 + (size_t)b64_len; - uint32_t ac = 0; - for (size_t i = 0; i < rdata_len; i++) - ac += (i & 1) ? rdata[i] : (uint32_t)rdata[i] << 8; - ac += (ac >> 16) & 0xFFFF; - uint16_t keytag = (uint16_t)(ac & 0xFFFF); - - /* Wire format name */ - uint8_t payload[1024]; - uint8_t *p = payload; - const char *ps = domain; - while (*ps) { - const char *dot = strchr(ps, '.'); - if (!dot) dot = ps + strlen(ps); - int l = (int)(dot - ps); - *p++ = (uint8_t)l; - for (int i = 0; i < l; i++) *p++ = (uint8_t)tolower(ps[i]); - ps = dot; - if (*ps == '.') ps++; - } - *p++ = 0; - memcpy(p, rdata, rdata_len); - size_t pay_len = (size_t)lws_ptr_diff(p + rdata_len, payload); - - enum lws_genhash_types htype = LWS_GENHASH_TYPE_SHA256; - int dtype = 2; - int dlen = 32; - if (alg == 14) { - htype = LWS_GENHASH_TYPE_SHA384; - dtype = 4; - dlen = 48; - } - - struct lws_genhash_ctx ctx; - uint8_t digest[64]; - if (lws_genhash_init(&ctx, htype)) return 1; - if (lws_genhash_update(&ctx, payload, pay_len)) { - lws_genhash_destroy(&ctx, NULL); - return 1; + + memset(&pc, 0, sizeof(pc)); + pc.vhd = vhd; + lejp_construct(&jctx, cb_conf, &pc, config_paths, LWS_ARRAY_SIZE(config_paths)); + int m = lejp_parse(&jctx, (uint8_t *)buf, (int)st.st_size); + lejp_destruct(&jctx); + free(buf); + + if (m < 0 && m != LEJP_REJECT_UNKNOWN) { + lwsl_err("%s: JSON decode failed for %s: %d\n", __func__, filepath, m); + return 0; } - lws_genhash_destroy(&ctx, digest); - - char *po = out; - char *pe = out + out_len; - po += lws_snprintf(po, lws_ptr_diff_size_t(pe, po), "%u %d %d ", keytag, alg, dtype); - for (int i = 0; i < dlen; i++) - po += lws_snprintf(po, lws_ptr_diff_size_t(pe, po), "%02X", digest[i]); - - return 0; -} -static int skip_name(const uint8_t *p, int len, int *offset) { - while (*offset < len) { - uint8_t l = p[*offset]; - if (l == 0) { - (*offset)++; - return 0; - } - if ((l & 0xC0) == 0xC0) { - (*offset) += 2; + if (pc.common_name[0]) { + if (strchr(pc.common_name, '/') || strstr(pc.common_name, "..")) { + lwsl_err("%s: Invalid common-name containing path traversal characters: %s\n", __func__, pc.common_name); return 0; } - (*offset) += l + 1; - } - return -1; -} -static uint32_t parse_soa_serial(const uint8_t *p, int len) { - int offset = 0; - if (skip_name(p, len, &offset)) return 0; - if (skip_name(p, len, &offset)) return 0; - if (offset + 4 <= len) { - return ((uint32_t)p[offset] << 24) | ((uint32_t)p[offset+1] << 16) | - ((uint32_t)p[offset+2] << 8) | ((uint32_t)p[offset+3]); - } - return 0; -} + lwsl_info("%s: Parsed domain %s from %s\n", __func__, pc.common_name, filepath); -struct dnssec_async_req { - struct vhd *vhd; - char domain[128]; -}; + /* Directory format requires /domains// */ + char key_path[1024]; -static struct lws * -dnssec_state_dns_cb(struct lws *wsi, const char *ads, const struct addrinfo *result, int n, void *opaque) -{ - struct dnssec_async_req *req = (struct dnssec_async_req *)opaque; - struct vhd *vhd = req->vhd; - uint16_t paylen = 0; - uint32_t serial = 0, expiry = 0, inception = 0; - int signed_ok = (n & LWS_ADNS_DNSSEC_VALID) ? 1 : 0; - - const uint8_t *soa = lws_async_dns_get_rr_cache(vhd->context, req->domain, 0x06 /* SOA */, &paylen); - if (soa) { - serial = parse_soa_serial(soa, paylen); - } else { - lwsl_warn("%s: No SOA record cached natively for %s (n=%d)\n", __func__, req->domain, n); - } + /* Check ZSK */ + lws_snprintf(key_path, sizeof(key_path), "%s/domains/%s/%s.zsk.private.jwk", vhd->base_dir, pc.common_name, pc.common_name); + int has_zsk = (access(key_path, F_OK) == 0); - const uint8_t *rrsig = lws_async_dns_get_rr_cache(vhd->context, req->domain, 0x2e /* RRSIG */, &paylen); - if (rrsig && paylen >= 16) { - int t_covered = ((uint16_t)rrsig[0] << 8) | rrsig[1]; - if (t_covered == 0x06 /* SOA */) { - expiry = ((uint32_t)rrsig[8] << 24) | ((uint32_t)rrsig[9] << 16) | ((uint32_t)rrsig[10] << 8) | rrsig[11]; - inception = ((uint32_t)rrsig[12] << 24) | ((uint32_t)rrsig[13] << 16) | ((uint32_t)rrsig[14] << 8) | rrsig[15]; + /* Check KSK */ + lws_snprintf(key_path, sizeof(key_path), "%s/domains/%s/%s.ksk.private.jwk", vhd->base_dir, pc.common_name, pc.common_name); + int has_ksk = (access(key_path, F_OK) == 0); - uint32_t cnow = (uint32_t)lws_now_secs(); - if (expiry <= inception || expiry <= cnow) { - signed_ok = 0; - } - } else { - lwsl_warn("%s: Cached RRSIG was not covering SOA (t_covered: %d) for %s\n", __func__, t_covered, req->domain); - } - } else { - lwsl_warn("%s: No RRSIG record returned dynamically for %s (paylen: %u)\n", __func__, req->domain, paylen); - } - - /* Also write dns_state.json */ - char dbuf[1024]; - char out_path[1024]; - lws_snprintf(out_path, sizeof(out_path), "%s/domains/%s/dns_state.json", vhd->base_dir, req->domain); - int dfd = open(out_path, O_CREAT | O_TRUNC | O_WRONLY, 0644); - if (dfd >= 0) { - int jn = lws_snprintf(dbuf, sizeof(dbuf), "{\"serial\": %u, \"signed_ok\": %d, \"expiry\": %u, \"inception\": %u}\n", serial, signed_ok, expiry, inception); - write(dfd, dbuf, (size_t)jn); - close(dfd); - } - - uint32_t now = (uint32_t)lws_now_secs(); - if (expiry > inception && expiry > now && vhd->ops && vhd->ops->bump_zone_serial) { - uint32_t valid_dur = expiry - inception; - uint32_t remaining = expiry - now; - if (remaining < (valid_dur / 5)) { /* < 20% remaining means > 80% expired */ - char input_path[1024]; + if (!has_zsk || !has_ksk) { + lwsl_notice("%s: Missing keys for %s, automatically generating...\n", __func__, pc.common_name); char wd[512]; - lwsl_notice("%s: Signature for %s > 80%% expired, auto-bumping and resigning\n", __func__, req->domain); - lws_snprintf(input_path, sizeof(input_path), "%s/domains/%s/%s.zone", vhd->base_dir, req->domain, req->domain); - lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, req->domain); - - vhd->ops->bump_zone_serial(vhd->context, input_path); + lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, pc.common_name); - struct lws_dht_dnssec_signzone_args sargs; - memset(&sargs, 0, sizeof(sargs)); - sargs.domain = req->domain; - sargs.workdir = wd; - sargs.sign_validity_duration = vhd->signature_duration; - vhd->ops->signzone(vhd->context, &sargs); - } - } - - free(req); - return wsi; -} - -static void check_dnssec_state_via_dns(struct vhd *vhd, const char *domain) -{ - struct dnssec_async_req *req = malloc(sizeof(*req)); - if (!req) return; - req->vhd = vhd; - lws_strncpy(req->domain, domain, sizeof(req->domain)); - - lwsl_info("%s: Issuing async DNS check for %s (NOCACHE|WANT_DNSSEC|IGNORE_HOSTS)\n", __func__, domain); - lws_async_dns_query(vhd->context, 0, domain, - LWS_ADNS_RECORD_SOA | LWS_ADNS_NOCACHE | - LWS_ADNS_WANT_DNSSEC | LWS_ADNS_IGNORE_HOSTS_FILE, - dnssec_state_dns_cb, NULL, req, NULL); -} - -struct acme_pvo_alloc { - struct lws_protocol_vhost_options pvo_core; - struct lws_protocol_vhost_options pvo_acme; - struct lws_protocol_vhost_options pvo1; - struct lws_protocol_vhost_options pvo2; - struct lws_protocol_vhost_options pvo3; - struct lws_protocol_vhost_options pvo4; - char root_domain[256]; - char common_name[256]; -}; + struct lws_dht_dnssec_keygen_args kargs; + memset(&kargs, 0, sizeof(kargs)); + kargs.domain = pc.common_name; + kargs.workdir = wd; -static void -acme_vhost_finalize(struct lws_vhost *vh, void *arg) -{ - lwsl_notice("%s: Finalizing ACME vhost %s (arg: %p)\n", __func__, vh ? lws_get_vhost_name(vh) : "NULL", arg); - if (arg) - free(arg); -} + /* Assume ES256 fallback if unspecified (or whatever dnssec module defaults to) */ + kargs.curve = "P-256"; -static int -scan_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) -{ - struct vhd *vhd = (struct vhd *)user; - - if (lde->type != LDOT_DIR) - return 0; - - if (lde->name[0] == '.') - return 0; - - if (strchr(lde->name, '/') || strstr(lde->name, "..")) { - lwsl_err("%s: Invalid common-name containing path traversal characters: %s\n", __func__, lde->name); - return 0; - } - - lwsl_info("%s: Parsed domain %s from folder\n", __func__, lde->name); - - /* Directory format requires /domains// */ - char key_path[1024]; - const char *common_name = lde->name; - const char *key_type = "EC"; - const char *key_curve = "P-256"; - int key_bits = 256; - - /* Check ZSK */ - lws_snprintf(key_path, sizeof(key_path), "%s/domains/%s/%s.zsk.private.jwk", vhd->base_dir, common_name, common_name); - int has_zsk = (access(key_path, F_OK) == 0); - - /* Check KSK */ - lws_snprintf(key_path, sizeof(key_path), "%s/domains/%s/%s.ksk.private.jwk", vhd->base_dir, common_name, common_name); - int has_ksk = (access(key_path, F_OK) == 0); - - if (!has_zsk || !has_ksk) { - lwsl_notice("%s: Missing keys for %s, automatically generating...\n", __func__, common_name); - char wd[512]; - lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, common_name); - - struct lws_dht_dnssec_keygen_args kargs; - memset(&kargs, 0, sizeof(kargs)); - kargs.domain = common_name; - kargs.workdir = wd; - - kargs.type = key_type; - kargs.curve = key_curve; - kargs.bits = key_bits; - - if (vhd->ops->keygen(vhd->context, &kargs)) - lwsl_err("%s: Failed to generate keys for %s\n", __func__, common_name); - } - - /* Check resign triggers */ - char input_path[1024]; - char output_path[1024]; - char jws_path[1024]; - char zsk_path[1024]; - char ksk_path[1024]; - - lws_snprintf(input_path, sizeof(input_path), "%s/domains/%s/%s.zone", vhd->base_dir, common_name, common_name); - lws_snprintf(output_path, sizeof(output_path), "%s/domains/%s/%s.zone.signed", vhd->base_dir, common_name, common_name); - lws_snprintf(jws_path, sizeof(jws_path), "%s/domains/%s/%s.zone.signed.jws", vhd->base_dir, common_name, common_name); - lws_snprintf(zsk_path, sizeof(zsk_path), "%s/domains/%s/%s.zsk.private.jwk", vhd->base_dir, common_name, common_name); - lws_snprintf(ksk_path, sizeof(ksk_path), "%s/domains/%s/%s.ksk.private.jwk", vhd->base_dir, common_name, common_name); - - char acme_path[1024]; - lws_snprintf(acme_path, sizeof(acme_path), "%s.acme", input_path); - struct stat st_acme; - int has_acme = (stat(acme_path, &st_acme) == 0); - - int needs_resign = 0; - struct stat st_in, st_out; + if (vhd->ops->keygen(vhd->context, &kargs)) + lwsl_err("%s: Failed to generate keys for %s\n", __func__, pc.common_name); + } - if (stat(input_path, &st_in) == 0) { - if (stat(output_path, &st_out) != 0) { - /* output doesn't exist */ - lwsl_user("dnssec_monitor: %s does not exist! Triggering resign!\n", output_path); - needs_resign = 1; - } else { - if (st_in.st_mtime > st_out.st_mtime) { - /* unsigned zone is newer than signed zone */ - lwsl_user("dnssec-monitor: unsigned zone %s (mtime %lu) is newer than signed zone %s (mtime %lu)! Triggering resign!\n", input_path, (unsigned long)st_in.st_mtime, output_path, (unsigned long)st_out.st_mtime); - needs_resign = 1; - } else if (has_acme && st_acme.st_mtime > st_out.st_mtime) { - lwsl_user("dnssec-monitor: .acme challenge file %s (mtime %lu) is newer than signed zone %s (mtime %lu)! Triggering resign!\n", acme_path, (unsigned long)st_acme.st_mtime, output_path, (unsigned long)st_out.st_mtime); + /* Check resign triggers */ + char input_path[1024]; + char output_path[1024]; + char jws_path[1024]; + char zsk_path[1024]; + char ksk_path[1024]; + + lws_snprintf(input_path, sizeof(input_path), "%s/domains/%s/%s.zone", vhd->base_dir, pc.common_name, pc.common_name); + lws_snprintf(output_path, sizeof(output_path), "%s/domains/%s/%s.zone.signed", vhd->base_dir, pc.common_name, pc.common_name); + lws_snprintf(jws_path, sizeof(jws_path), "%s/domains/%s/%s.zone.signed.jws", vhd->base_dir, pc.common_name, pc.common_name); + lws_snprintf(zsk_path, sizeof(zsk_path), "%s/domains/%s/%s.zsk.private.jwk", vhd->base_dir, pc.common_name, pc.common_name); + lws_snprintf(ksk_path, sizeof(ksk_path), "%s/domains/%s/%s.ksk.private.jwk", vhd->base_dir, pc.common_name, pc.common_name); + + char acme_path[1024]; + lws_snprintf(acme_path, sizeof(acme_path), "%s.acme", input_path); + struct stat st_acme; + int has_acme = (stat(acme_path, &st_acme) == 0); + + int needs_resign = 0; + struct stat st_in, st_out; + + if (stat(input_path, &st_in) == 0) { + if (stat(output_path, &st_out) != 0) { + /* output doesn't exist */ + lwsl_user("dnssec_monitor: %s does not exist! Triggering resign!\n", output_path); needs_resign = 1; } else { - lwsl_info("dnssec-monitor: unsigned zone %s (mtime %lu) is NOT newer than signed zone %s (mtime %lu), skipping resign.\n", input_path, (unsigned long)st_in.st_mtime, output_path, (unsigned long)st_out.st_mtime); - } - if (!needs_resign) { - char dns_path[1024]; - struct stat st_dns; - int trigger_dns = 0; - lws_snprintf(dns_path, sizeof(dns_path), "%s/domains/%s/dns_state.json", vhd->base_dir, common_name); - if (stat(dns_path, &st_dns) < 0) { - trigger_dns = 1; + if (st_in.st_mtime > st_out.st_mtime) { + /* unsigned zone is newer than signed zone */ + lwsl_user("dnssec-monitor: unsigned zone %s (mtime %lu) is newer than signed zone %s (mtime %lu)! Triggering resign!\n", input_path, (unsigned long)st_in.st_mtime, output_path, (unsigned long)st_out.st_mtime); + needs_resign = 1; + } else if (has_acme && st_acme.st_mtime > st_out.st_mtime) { + lwsl_user("dnssec-monitor: .acme challenge file %s (mtime %lu) is newer than signed zone %s (mtime %lu)! Triggering resign!\n", acme_path, (unsigned long)st_acme.st_mtime, output_path, (unsigned long)st_out.st_mtime); + needs_resign = 1; } else { - if ((unsigned long long)lws_now_secs() - (unsigned long long)st_dns.st_mtime > 300) - trigger_dns = 1; + lwsl_info("dnssec-monitor: unsigned zone %s (mtime %lu) is NOT newer than signed zone %s (mtime %lu), skipping resign.\n", input_path, (unsigned long)st_in.st_mtime, output_path, (unsigned long)st_out.st_mtime); } - if (trigger_dns) - check_dnssec_state_via_dns(vhd, common_name); + /* TODO: 75% lifetime exhaustion check, but requires parsing the signature. */ } + } else { + lwsl_info("%s: Missing domain %s base zone config, skipping resign\n", __func__, input_path); } - } else { - lwsl_info("%s: Missing domain %s base zone config, skipping resign\n", __func__, input_path); - } - if (needs_resign) { - char wd[512]; - lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, common_name); - - lwsl_user("%s: Signing zone for %s\n", __func__, common_name); - struct lws_dht_dnssec_signzone_args sargs; - memset(&sargs, 0, sizeof(sargs)); - sargs.domain = common_name; - sargs.workdir = wd; - sargs.sign_validity_duration = vhd->signature_duration; + if (needs_resign) { + char wd[512]; + lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, pc.common_name); - if (vhd->ops->signzone(vhd->context, &sargs)) { - lwsl_user("%s: Failed signing zone for %s\n", __func__, common_name); - } else { - lwsl_user("%s: Successfully signed zone for %s\n", __func__, common_name); - } - } - - if (vhd->acme_enabled) { - char cert_path[1024]; - lws_snprintf(cert_path, sizeof(cert_path), "%s/domains/%s/certs/%s/crt/%s-latest.crt", vhd->base_dir, common_name, vhd->acme_production ? "production" : "staging", common_name); - - int needs_acme = 1; - - int cfd = open(cert_path, O_RDONLY); - if (cfd >= 0) { - struct stat st; - if (!fstat(cfd, &st) && st.st_size > 0) { - char *cert_buf = malloc((size_t)st.st_size + 1); - if (cert_buf) { - if (read(cfd, cert_buf, (size_t)st.st_size) == st.st_size) { - cert_buf[st.st_size] = '\0'; - struct lws_x509_cert *x509; - if (!lws_x509_create(&x509)) { - if (!lws_x509_parse_from_pem(x509, cert_buf, (size_t)st.st_size + 1)) { - union lws_tls_cert_info_results res_from, res_to; - if (!lws_x509_info(x509, LWS_TLS_CERT_INFO_VALIDITY_FROM, &res_from, 0) && - !lws_x509_info(x509, LWS_TLS_CERT_INFO_VALIDITY_TO, &res_to, 0)) { - time_t now = time(NULL); - time_t total = res_to.time - res_from.time; - time_t remaining = res_to.time - now; - - if (total > 0 && remaining >= (total / 5)) { - needs_acme = 0; - } - } - } - lws_x509_destroy(&x509); - } - } - free(cert_buf); - } - } - close(cfd); - } else { - /* No local cert, launch a probe to the port to check validity if ACME is needed */ - int probe_active = 0; - lws_start_foreach_dll(struct lws_dll2 *, d, vhd->active_probes.head) { - struct cert_check_info *p = lws_container_of(d, struct cert_check_info, active_list); - if (!strcmp(p->fqdn, common_name) && p->is_automated) { - probe_active = 1; - break; - } - } lws_end_foreach_dll(d); + lwsl_user("%s: Signing zone for %s\n", __func__, pc.common_name); + struct lws_dht_dnssec_signzone_args sargs; + memset(&sargs, 0, sizeof(sargs)); + sargs.domain = pc.common_name; + sargs.workdir = wd; + sargs.sign_validity_duration = vhd->signature_duration; - if (probe_active) { - lwsl_info("%s: Automated probe already active for %s, skipping duplicate\n", __func__, common_name); - needs_acme = 0; + if (vhd->ops->signzone(vhd->context, &sargs)) { + lwsl_user("%s: Failed signing zone for %s\n", __func__, pc.common_name); } else { - struct cert_check_info *cci = malloc(sizeof(*cci)); - if (cci) { - memset(cci, 0, sizeof(*cci)); - cci->magic = CERT_CHECK_MAGIC; - lws_strncpy(cci->fqdn, common_name, sizeof(cci->fqdn)); - cci->port = 443; - cci->is_automated = 1; - cci->starttls_state = 0; - lws_dll2_add_tail(&cci->active_list, &vhd->active_probes); - - struct lws_client_connect_info cinfo; - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.context = vhd->context; - cinfo.port = 443; - cinfo.address = common_name; - cinfo.host = cinfo.address; - cinfo.origin = cinfo.address; - cinfo.ssl_connection = LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_ALLOW_EXPIRED | LCCSCF_USE_SSL; - cinfo.protocol = "lws-dht-dnssec-monitor"; - cinfo.vhost = vhd->vhost; - cinfo.opaque_user_data = cci; - cinfo.alpn = "http/1.1"; - cinfo.method = "RAW"; - - needs_acme = 0; /* Wait for probe to complete and trigger ACME if needed */ - if (!lws_client_connect_via_info(&cinfo)) { - lwsl_err("%s: Failed to start automated cert probe for %s\n", __func__, common_name); - lws_dll2_remove(&cci->active_list); - free(cci); - } + lwsl_user("%s: Successfully signed zone for %s\n", __func__, pc.common_name); } } - } - - if (needs_acme) { - lwsl_notice("%s: ACME needed for %s\n", __func__, common_name); - - char conf_dir[1024]; - lws_snprintf(conf_dir, sizeof(conf_dir), "%s/domains/%s/conf.d", vhd->base_dir, common_name); - if (mkdir(conf_dir, 0755) < 0 && errno != EEXIST) - lwsl_notice("%s: Failed to create conf.d dir\n", __func__); - - char json_path[1024]; - lws_snprintf(json_path, sizeof(json_path), "%s/%s.json", conf_dir, common_name); - - int jfd = open(json_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); - if (jfd >= 0) { - char jbuf[1024]; - int jn = lws_snprintf(jbuf, sizeof(jbuf), - "{\n \"common-name\": \"%s\",\n \"challenge-type\": \"dns-01\",\n" - " \"email\": \"%s\",\n \"acme\": {\n" - " \"organization\": \"%s\",\n" - " \"country\": \"%s\",\n" - " \"state\": \"%s\",\n" - " \"locality\": \"%s\",\n" - " \"directory-url\": \"%s\"\n }\n}\n", - common_name, - vhd->acme_email[0] ? vhd->acme_email : "admin@domain.com", - vhd->acme_organization, vhd->acme_country, vhd->acme_state, vhd->acme_locality, - vhd->acme_production ? "https://acme-v02.api.letsencrypt.org/directory" : "https://acme-staging-v02.api.letsencrypt.org/directory"); - - if (write(jfd, jbuf, (size_t)jn) < 0) { - lwsl_err("%s: Failed to write generated ACME config\n", __func__); - } - close(jfd); - } - - char vh_name[256]; - lws_snprintf(vh_name, sizeof(vh_name), "acme_%s", common_name); - if (!lws_get_vhost_by_name(vhd->context, vh_name)) { - struct lws_context_creation_info info; - struct acme_pvo_alloc *pa = malloc(sizeof(*pa)); - - if (!pa) { - lwsl_err("%s: OOM allocating ACME PVOs\n", __func__); - return 0; - } - memset(pa, 0, sizeof(*pa)); - lws_strncpy(pa->root_domain, common_name, sizeof(pa->root_domain)); - lws_strncpy(pa->common_name, common_name, sizeof(pa->common_name)); - - memset(&info, 0, sizeof(info)); - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.vhost_name = vh_name; - - pa->pvo_core.name = "lws-acme-client-core"; - pa->pvo_core.next = &pa->pvo_acme; - - pa->pvo_acme.name = "lws-acme-client-dns"; - pa->pvo_acme.options = &pa->pvo1; - info.pvo = &pa->pvo_core; - - pa->pvo1.name = "root-domain"; - pa->pvo1.value = pa->root_domain; - pa->pvo1.next = &pa->pvo2; - - pa->pvo2.name = "common-name"; - pa->pvo2.value = pa->common_name; - pa->pvo2.next = &pa->pvo3; + } - pa->pvo3.name = "email"; - pa->pvo3.value = vhd->acme_email[0] ? vhd->acme_email : "admin@domain.com"; - pa->pvo3.next = &pa->pvo4; + return 0; +} - pa->pvo4.name = "directory-url"; - pa->pvo4.value = vhd->acme_production ? "https://acme-v02.api.letsencrypt.org/directory" : "https://acme-staging-v02.api.letsencrypt.org/directory"; +#if defined(LWS_WITH_DIR) +static void +dir_notify_cb(const char *path, int is_file, void *user) +{ + struct vhd *vhd = (struct vhd *)user; + char scan_path[1024]; - info.finalize = acme_vhost_finalize; - info.finalize_arg = pa; + lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - if (lws_create_vhost(vhd->context, &info)) { - lwsl_notice("%s: ACME vhost %s spawned natively\n", __func__, vh_name); - } else { - lwsl_err("%s: Failed to spawn ACME vhost %s\n", __func__, vh_name); - free(pa); - } - } - } - } else { - char json_path[1024]; - lws_snprintf(json_path, sizeof(json_path), "%s/domains/%s/tls/%s.json", vhd->base_dir, common_name, common_name); - unlink(json_path); - } + lwsl_user("%s: Detected inotify filesystem change %s (file: %d), manually rescanning domains: %s\n", __func__, path, is_file, scan_path); - return 0; + lws_dir(scan_path, vhd, scan_dir_cb); } +#endif static int -scan_whois_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +parent_scan_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) { +/* commented to pause log spew */ +#if 0 struct vhd *vhd = (struct vhd *)user; - if (lde->type != LDOT_DIR || lde->name[0] == '.') return 0; - if (vhd->whois_queries.count < 4) { - char whois_path[1024]; - struct stat st_whois; - int trigger = 0; + char jws_path[1024], pub_path[1024]; + lws_snprintf(jws_path, sizeof(jws_path), "%s/domains/%s/%s.zone.signed.jws", vhd->base_dir, lde->name, lde->name); + lws_snprintf(pub_path, sizeof(pub_path), "%s.published", jws_path); - lws_snprintf(whois_path, sizeof(whois_path), "%s/domains/%s/whois.json", vhd->base_dir, lde->name); - if (stat(whois_path, &st_whois) < 0) { - trigger = 1; - } else { - if ((unsigned long long)lws_now_secs() - (unsigned long long)st_whois.st_mtime > 300) - trigger = 1; + struct stat st_jws, st_pub; + if (stat(jws_path, &st_jws) == 0) { + int fd_pub = open(pub_path, O_RDWR); + int needs_pub = 0; + + if (fd_pub < 0) { + fd_pub = open(pub_path, O_CREAT | O_RDWR, 0666); + needs_pub = 1; + } else if (fstat(fd_pub, &st_pub) == 0) { + if (st_jws.st_mtime > st_pub.st_mtime) + needs_pub = 1; } - if (trigger) { - whois_trigger(vhd, lde->name); + if (needs_pub) { + lwsl_notice("%s: Parent detected new JWS for %s! Triggering DHT publication loop.\n", __func__, lde->name); + if (vhd->ops && vhd->ops->publish_jws) { + vhd->ops->publish_jws(vhd->vhost, jws_path); + if (fd_pub >= 0) { + if (write(fd_pub, "\n", 1) < 0) {} + } + } } + if (fd_pub >= 0) close(fd_pub); } - +#endif return 0; } -#if defined(LWS_WITH_DIR) static void -dir_notify_cb(const char *path, int is_file, void *user) +parent_dnssec_monitor_timer_cb(struct lws_sorted_usec_list *sul) { - struct vhd *vhd = (struct vhd *)user; + struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer); char scan_path[1024]; + // lwsl_notice("%s: Parent timer fired!\n", __func__); + lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); + lws_dir(scan_path, vhd, parent_scan_dir_cb); + lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 5 * LWS_US_PER_SEC); +} - lwsl_user("%s: Detected inotify filesystem change %s (file: %d), manually rescanning domains: %s\n", __func__, path, is_file, scan_path); +static void +dnssec_monitor_timer_cb(struct lws_sorted_usec_list *sul) +{ + struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer); + char scan_path[1024]; + // lwsl_notice("%s: Child timer fired!\n", __func__); + + lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); lws_dir(scan_path, vhd, scan_dir_cb); + + lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, dnssec_monitor_timer_cb, 5 * LWS_US_PER_SEC); } -#endif -struct tls_config_args { - char challenge_type[64]; + +#include +#include + +struct monitor_req_args { + char req[32]; + char domain[128]; + char subdomain[128]; char email[128]; + char organization[128]; char directory_url[256]; + char *zone_buf; + int zone_len; + int zone_alloc; + char jwt[2048]; + char suffix[64]; int port; + int enabled; + int production; + char country[128]; + char state[128]; + char locality[128]; + char profile[128]; + char key_type[32]; }; -static const char * const tls_config_paths[] = { - "challenge-type", - "port", +static const char * const monitor_req_paths[] = { + "req", + "domain", + "subdomain", "email", - "acme.directory-url", + "organization", + "directory_url", + "zone", + "jwt", + "suffix", + "port", + "enabled", + "production", + "country", + "state", + "locality", + "profile", + "key_type" }; -enum enum_tls_config_paths { - LTC_CHALLENGE_TYPE, - LTC_PORT, - LTC_EMAIL, - LTC_DIRECTORY_URL, +enum enum_req_paths { + LRP_REQ, + LRP_DOMAIN, + LRP_SUBDOMAIN, + LRP_EMAIL, + LRP_ORG, + LRP_DIR_URL, + LRP_ZONE, + LRP_JWT, + LRP_SUFFIX, + LRP_PORT, + LRP_ENABLED, + LRP_PRODUCTION, + LRP_COUNTRY, + LRP_STATE, + LRP_LOCALITY, + LRP_PROFILE, + LRP_KEY_TYPE }; static signed char -tls_config_cb(struct lejp_ctx *ctx, char reason) +monitor_req_cb(struct lejp_ctx *ctx, char reason) { - struct tls_config_args *a = (struct tls_config_args *)ctx->user; + struct monitor_req_args *a = (struct monitor_req_args *)ctx->user; if (reason == LEJPCB_VAL_NUM_INT) { - if (ctx->path_match - 1 == LTC_PORT) + if (ctx->path_match - 1 == LRP_PORT) { a->port = atoi(ctx->buf); - return 0; + } } - if (reason != LEJPCB_VAL_STR_END) - return 0; - - switch (ctx->path_match - 1) { - case LTC_CHALLENGE_TYPE: - lws_strncpy(a->challenge_type, ctx->buf, sizeof(a->challenge_type)); - break; - case LTC_EMAIL: - lws_strncpy(a->email, ctx->buf, sizeof(a->email)); - break; - case LTC_DIRECTORY_URL: - lws_strncpy(a->directory_url, ctx->buf, sizeof(a->directory_url)); - break; + if (reason == LEJPCB_VAL_TRUE) { + if (ctx->path_match - 1 == LRP_ENABLED) a->enabled = 1; + if (ctx->path_match - 1 == LRP_PRODUCTION) a->production = 1; } - return 0; -} - -struct scan_tls_ctx { - struct vhd *vhd; - const char *domain; -}; - -static int -scan_tls_configs_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) -{ - struct scan_tls_ctx *ctx = (struct scan_tls_ctx *)user; - struct vhd *vhd = ctx->vhd; - - if (lde->type != LDOT_FILE || !strstr(lde->name, ".json")) return 0; - - char subdomain[256]; - lws_strncpy(subdomain, lde->name, sizeof(subdomain)); - char *p = strstr(subdomain, ".json"); - if (p) *p = '\0'; - - char config_path[1024]; - lws_snprintf(config_path, sizeof(config_path), "%s/%s", dirpath, lde->name); - - int fd = open(config_path, O_RDONLY); - if (fd < 0) return 0; - - struct tls_config_args a; - memset(&a, 0, sizeof(a)); - - struct lejp_ctx jctx; - lejp_construct(&jctx, tls_config_cb, &a, tls_config_paths, LWS_ARRAY_SIZE(tls_config_paths)); - - char buf[1024]; - int n; - while ((n = (int)read(fd, buf, sizeof(buf))) > 0) { - if (lejp_parse(&jctx, (uint8_t *)buf, n) < 0) - break; + if (reason == LEJPCB_VAL_FALSE) { + if (ctx->path_match - 1 == LRP_ENABLED) a->enabled = 0; + if (ctx->path_match - 1 == LRP_PRODUCTION) a->production = 0; } - close(fd); - lejp_destruct(&jctx); - - if (a.port <= 0 || strcmp(a.challenge_type, "dns-01")) - return 0; - - /* check expiry */ - char cert_path[1024]; - lws_snprintf(cert_path, sizeof(cert_path), "%s/domains/%s/certs/%s/crt/%s", vhd->base_dir, ctx->domain, vhd->acme_production ? "production" : "staging", subdomain); - int needs_acme = 1; - - fd = open(cert_path, O_RDONLY); - if (fd >= 0) { - struct stat st; - if (!fstat(fd, &st) && st.st_size > 0) { - char *cert_buf = malloc((size_t)st.st_size + 1); - if (cert_buf) { - if (read(fd, cert_buf, (size_t)st.st_size) == st.st_size) { - cert_buf[st.st_size] = '\0'; - struct lws_x509_cert *x509; - if (!lws_x509_create(&x509)) { - if (!lws_x509_parse_from_pem(x509, cert_buf, (size_t)st.st_size + 1)) { - union lws_tls_cert_info_results res; - if (!lws_x509_info(x509, LWS_TLS_CERT_INFO_VALIDITY_TO, &res, 0)) { - time_t now = time(NULL); - if (res.time > now + (7 * 24 * 3600)) { - needs_acme = 0; - } - } - } - lws_x509_destroy(&x509); - } - } - free(cert_buf); - } + if (reason == LEJPCB_VAL_STR_START) { + if (ctx->path_match - 1 == LRP_ZONE) { + a->zone_len = 0; } - close(fd); } - if (needs_acme) { - lwsl_notice("%s: ACME needed for %s (port %d)\n", __func__, subdomain, a.port); - - /* Check if already running by vhost name */ - char vh_name[256]; - lws_snprintf(vh_name, sizeof(vh_name), "acme_%s", subdomain); - if (!lws_get_vhost_by_name(vhd->context, vh_name)) { - struct lws_context_creation_info info; - struct acme_pvo_alloc *pa = malloc(sizeof(*pa)); - - if (!pa) { - lwsl_err("%s: OOM allocating ACME PVOs\n", __func__); - return 0; + if (reason == LEJPCB_VAL_STR_CHUNK || reason == LEJPCB_VAL_STR_END) { + switch (ctx->path_match - 1) { + case LRP_REQ: + lws_strncpy(a->req, ctx->buf, sizeof(a->req)); + break; + case LRP_DOMAIN: + lws_strncpy(a->domain, ctx->buf, sizeof(a->domain)); + break; + case LRP_SUBDOMAIN: + lws_strncpy(a->subdomain, ctx->buf, sizeof(a->subdomain)); + break; + case LRP_EMAIL: + lws_strncpy(a->email, ctx->buf, sizeof(a->email)); + break; + case LRP_ORG: + lws_strncpy(a->organization, ctx->buf, sizeof(a->organization)); + break; + case LRP_DIR_URL: + lws_strncpy(a->directory_url, ctx->buf, sizeof(a->directory_url)); + break; + case LRP_ZONE: + if (!a->zone_buf) { + a->zone_alloc = 8192; + a->zone_buf = malloc((size_t)a->zone_alloc); + if (!a->zone_buf) return -1; } - memset(pa, 0, sizeof(*pa)); - lws_strncpy(pa->root_domain, ctx->domain, sizeof(pa->root_domain)); - lws_strncpy(pa->common_name, subdomain, sizeof(pa->common_name)); - - memset(&info, 0, sizeof(info)); - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.vhost_name = vh_name; - - pa->pvo_core.name = "lws-acme-client-core"; - pa->pvo_core.next = &pa->pvo_acme; - - pa->pvo_acme.name = "lws-acme-client-dns"; - pa->pvo_acme.options = &pa->pvo1; - info.pvo = &pa->pvo_core; + if (a->zone_len + ctx->npos >= a->zone_alloc) { + a->zone_alloc *= 2; + char *nb = realloc(a->zone_buf, (size_t)a->zone_alloc); + if (!nb) return -1; + a->zone_buf = nb; + } + memcpy(a->zone_buf + a->zone_len, ctx->buf, ctx->npos); + a->zone_len += ctx->npos; + if (reason == LEJPCB_VAL_STR_END) { + a->zone_buf[a->zone_len] = '\0'; + } + break; + case LRP_JWT: + lws_strncpy(a->jwt, ctx->buf, sizeof(a->jwt)); + break; + case LRP_SUFFIX: + lws_strncpy(a->suffix, ctx->buf, sizeof(a->suffix)); + break; + case LRP_COUNTRY: + lws_strncpy(a->country, ctx->buf, sizeof(a->country)); + break; + case LRP_STATE: + lws_strncpy(a->state, ctx->buf, sizeof(a->state)); + break; + case LRP_LOCALITY: + lws_strncpy(a->locality, ctx->buf, sizeof(a->locality)); + break; + case LRP_PROFILE: + lws_strncpy(a->profile, ctx->buf, sizeof(a->profile)); + break; + case LRP_KEY_TYPE: + lws_strncpy(a->key_type, ctx->buf, sizeof(a->key_type)); + break; + } + } - pa->pvo1.name = "root-domain"; - pa->pvo1.value = pa->root_domain; - pa->pvo1.next = &pa->pvo2; + return 0; +} - pa->pvo2.name = "common-name"; - pa->pvo2.value = pa->common_name; - pa->pvo2.next = &pa->pvo3; - pa->pvo3.name = "email"; - pa->pvo3.value = a.email[0] ? a.email : "admin@domain.com"; - pa->pvo3.next = &pa->pvo4; +#include +#include +#include - pa->pvo4.name = "directory-url"; - pa->pvo4.value = vhd->acme_production ? "https://acme-v02.api.letsencrypt.org/directory" : "https://acme-staging-v02.api.letsencrypt.org/directory"; +static char * +read_file(const char *path) +{ + int fd = open(path, O_RDONLY); + struct stat st; + char *buf = NULL; - info.finalize = acme_vhost_finalize; - info.finalize_arg = pa; + if (fd < 0) + return NULL; - if (lws_create_vhost(vhd->context, &info)) { - lwsl_notice("%s: ACME vhost %s spawned natively\n", __func__, vh_name); + if (!fstat(fd, &st)) { + buf = malloc((size_t)st.st_size + 1); + if (buf) { + if (read(fd, buf, (size_t)st.st_size) != st.st_size) { + free(buf); + buf = NULL; } else { - lwsl_err("%s: Failed to spawn ACME vhost %s\n", __func__, vh_name); - free(pa); - } - } - } else if (a.port > 0) { - struct cert_check_info *cci = malloc(sizeof(*cci)); - if (cci) { - memset(cci, 0, sizeof(*cci)); - cci->magic = CERT_CHECK_MAGIC; - lws_strncpy(cci->fqdn, subdomain, sizeof(cci->fqdn)); - cci->port = a.port; - cci->is_automated = 1; - - int starttls = (a.port == 25 || a.port == 587); - cci->starttls_state = starttls ? 1 : 0; - - struct lws_client_connect_info cinfo; - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.context = vhd->context; - cinfo.port = a.port; - cinfo.address = subdomain; - cinfo.host = cinfo.address; - cinfo.origin = cinfo.address; - cinfo.ssl_connection = LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_ALLOW_EXPIRED; - if (!starttls) cinfo.ssl_connection |= LCCSCF_USE_SSL; - cinfo.protocol = "lws-dht-dnssec-monitor"; - cinfo.vhost = vhd->vhost; - cinfo.opaque_user_data = cci; - cinfo.alpn = "http/1.1"; - cinfo.method = "RAW"; - - if (!lws_client_connect_via_info(&cinfo)) { - lwsl_err("%s: Failed to start automated cert probe for %s:%d\n", __func__, subdomain, a.port); - free(cci); + buf[st.st_size] = '\0'; } } } + close(fd); - return 0; + return buf; } static int -scan_tls_domains_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +write_pem(const char *path, const char *type, const uint8_t *der, size_t der_len) { - struct vhd *vhd = (struct vhd *)user; + char *b64; + size_t b64_len = (size_t)lws_base64_size((int)der_len) + 1; + int fd, n; + size_t pos = 0, len; + char hdr[128]; + + b64 = malloc(b64_len); + if (!b64) + return 1; - if (lde->type != LDOT_DIR || lde->name[0] == '.') return 0; + lws_b64_encode_string((const char *)der, (int)der_len, b64, (int)b64_len); + len = strlen(b64); - char tls_path[1024]; - lws_snprintf(tls_path, sizeof(tls_path), "%s/domains/%s/conf.d", vhd->base_dir, lde->name); + fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) { + free(b64); + return 1; + } - struct scan_tls_ctx ctx = { vhd, lde->name }; - lws_dir(tls_path, &ctx, scan_tls_configs_cb); + n = lws_snprintf(hdr, sizeof(hdr), "-----BEGIN %s-----\n", type); + if (write(fd, hdr, (size_t)n) != n) goto bail; - return 0; -} + while (pos < len) { + size_t chunk = len - pos > 64 ? 64 : len - pos; + if (write(fd, b64 + pos, chunk) != (ssize_t)chunk) goto bail; + if (write(fd, "\n", 1) != 1) goto bail; + pos += chunk; + } -static void -proxy_dnssec_scan_timer_cb(struct lws_sorted_usec_list *sul) -{ - struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer_proxy_scan); - char scan_path[1024]; + n = lws_snprintf(hdr, sizeof(hdr), "-----END %s-----\n", type); + if (write(fd, hdr, (size_t)n) != n) goto bail; - lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - lws_dir(scan_path, vhd, scan_tls_domains_cb); - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer_proxy_scan, proxy_dnssec_scan_timer_cb, 300 * LWS_US_PER_SEC); + close(fd); + free(b64); + return 0; + +bail: + close(fd); + free(b64); + return 1; } static int -scan_jws_publish_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +generate_cert_internal(struct vhd *vhd, const char *cn, const char *out_crt, const char *out_key, + const char *ca_crt_path, const char *ca_key_path, int is_ca, int is_server) { - struct vhd *vhd = (struct vhd *)user; + struct lws_x509_cert_gen_info info; + uint8_t *cert_buf = NULL, *key_buf = NULL; + size_t cert_len = 0, key_len = 0; + char *ca_crt_pem = NULL, *ca_key_pem = NULL; + int ret = 1; - if (lde->type != LDOT_DIR || lde->name[0] == '.') - return 0; + memset(&info, 0, sizeof(info)); + info.san = cn; + info.curve_name = "P-521"; /* Force ECDSA P-521 for Distribution PKI */ + info.is_ca = is_ca; + info.is_server = is_server; - if (vhd->ops && vhd->ops->publish_jws) { - char jws_path[1024]; - struct stat st; - - lws_snprintf(jws_path, sizeof(jws_path), "%s/domains/%s/%s.zone.signed.jws", vhd->base_dir, lde->name, lde->name); - - if (stat(jws_path, &st) == 0) { - /* Check if we already published this version */ - struct published_jws_info *p = NULL; - lws_start_foreach_dll(struct lws_dll2 *, d, vhd->published_jws.head) { - struct published_jws_info *tp = lws_container_of(d, struct published_jws_info, list); - if (!strcmp(tp->domain, lde->name)) { - p = tp; - break; - } - } lws_end_foreach_dll(d); - - if (!p || p->mtime != st.st_mtime) { - if (!p) { - p = malloc(sizeof(*p)); - if (!p) return 0; - memset(p, 0, sizeof(*p)); - lws_strncpy(p->domain, lde->name, sizeof(p->domain)); - lws_dll2_add_tail(&p->list, &vhd->published_jws); - } - p->mtime = st.st_mtime; - lwsl_notice("%s: Engaging parent monitor to execute DHT publication for %s\n", __func__, lde->name); - vhd->ops->publish_jws(vhd->vhost, jws_path); - } + if (!is_ca && ca_crt_path && ca_key_path) { + ca_crt_pem = read_file(ca_crt_path); + ca_key_pem = read_file(ca_key_path); + if (!ca_crt_pem || !ca_key_pem) { + lwsl_err("%s: failed to read CA cert or key\n", __func__); + goto bail; } + info.ca_cert_pem = ca_crt_pem; + info.ca_key_pem = ca_key_pem; } - return 0; -} -static void -parent_dnssec_monitor_timer_cb(struct lws_sorted_usec_list *sul) -{ - struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer); - char scan_path[1024]; + if (lws_x509_create_cert(vhd->context, &cert_buf, &cert_len, &key_buf, &key_len, &info)) { + lwsl_err("%s: failed to create cert\n", __func__); + goto bail; + } - lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - lws_dir(scan_path, vhd, scan_jws_publish_cb); - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 5 * LWS_US_PER_SEC); + if (write_pem(out_crt, "CERTIFICATE", cert_buf, cert_len)) { + lwsl_err("%s: failed to write cert\n", __func__); + goto bail; + } + + if (write_pem(out_key, "PRIVATE KEY", key_buf, key_len)) { + lwsl_err("%s: failed to write key\n", __func__); + goto bail; + } + + ret = 0; + +bail: + if (ca_crt_pem) free(ca_crt_pem); + if (ca_key_pem) free(ca_key_pem); + if (cert_buf) free(cert_buf); + if (key_buf) free(key_buf); + + return ret; } static void -root_dnssec_scan_timer_cb(struct lws_sorted_usec_list *sul) +generate_dist_pki(struct vhd *vhd) { - struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer_scan); - char scan_path[1024]; + char path_crt[1024], path_key[1024], path_dir[1024]; - /* Reload ACME config */ - char acme_path[1024]; - lws_snprintf(acme_path, sizeof(acme_path), "%s/acme_config.json", vhd->base_dir); - int fd = open(acme_path, O_RDONLY); - if (fd >= 0) { - char buf[4096]; - ssize_t n = read(fd, buf, sizeof(buf) - 1); - if (n > 0) { - buf[n] = '\0'; - struct monitor_req_args a; - memset(&a, 0, sizeof(a)); - struct lejp_ctx jctx; - lejp_construct(&jctx, monitor_req_cb, &a, monitor_req_paths, LWS_ARRAY_SIZE(monitor_req_paths)); - lejp_parse(&jctx, (uint8_t *)buf, (int)n); - lejp_destruct(&jctx); - - vhd->acme_enabled = a.enabled; - vhd->acme_production = a.production; - lws_strncpy(vhd->acme_email, a.email, sizeof(vhd->acme_email)); - lws_strncpy(vhd->acme_organization, a.organization, sizeof(vhd->acme_organization)); - lws_strncpy(vhd->acme_country, a.country, sizeof(vhd->acme_country)); - lws_strncpy(vhd->acme_state, a.state, sizeof(vhd->acme_state)); - lws_strncpy(vhd->acme_locality, a.locality, sizeof(vhd->acme_locality)); - } - close(fd); - } + lws_snprintf(path_dir, sizeof(path_dir), "%s/pki", vhd->base_dir); + if (mkdir(path_dir, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create pki dir\n", __func__); - lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - lws_dir(scan_path, vhd, scan_dir_cb); - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer_scan, root_dnssec_scan_timer_cb, 5 * LWS_US_PER_SEC); + lws_snprintf(path_crt, sizeof(path_crt), "%s/pki/distribution-ca.crt", vhd->base_dir); + lws_snprintf(path_key, sizeof(path_key), "%s/pki/distribution-ca.key", vhd->base_dir); + + if (access(path_crt, F_OK) != 0) { + lwsl_notice("%s: Generating Distribution CA\n", __func__); + generate_cert_internal(vhd, "dnssec-monitor-distribution-ca", path_crt, path_key, NULL, NULL, 1, 0); + } } +static void +generate_dist_server_cert(struct vhd *vhd, const char *domain) +{ + char path_crt[1024], path_key[1024], path_dir[1024]; + char ca_crt[1024], ca_key[1024]; + lws_snprintf(path_dir, sizeof(path_dir), "%s/pki", vhd->base_dir); + if (mkdir(path_dir, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create pki dir\n", __func__); + lws_snprintf(path_crt, sizeof(path_crt), "%s/pki/distribution-server-%s.crt", vhd->base_dir, domain); + lws_snprintf(path_key, sizeof(path_key), "%s/pki/distribution-server-%s.key", vhd->base_dir, domain); + if (access(path_crt, F_OK) == 0) return; + lws_snprintf(ca_crt, sizeof(ca_crt), "%s/pki/distribution-ca.crt", vhd->base_dir); + lws_snprintf(ca_key, sizeof(ca_key), "%s/pki/distribution-ca.key", vhd->base_dir); -#include -#include -#include - + lwsl_notice("%s: Generating Distribution Server Cert for %s\n", __func__, domain); + generate_cert_internal(vhd, domain, path_crt, path_key, ca_crt, ca_key, 0, 1); +} static void -handle_req_check_cert(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +generate_client_cert(struct vhd *vhd, const char *domain, const char *subdomain) { - struct lws_client_connect_info i; - memset(&i, 0, sizeof(i)); - i.context = vhd->context; - - struct lws_vhost *vh = lws_get_vhost_by_name(vhd->context, "root-monitor-dummy"); - i.vhost = vh ? vh : vhd->vhost; - - i.address = a->subdomain; - i.port = a->port; - i.ssl_connection = LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; + char path_dir[1024], path_crt[1024], path_key[1024]; + char ca_crt[1024], ca_key[1024]; - int starttls = (a->port == 25 || a->port == 587); - if (!starttls) - i.ssl_connection |= LCCSCF_USE_SSL; + lws_snprintf(path_dir, sizeof(path_dir), "%s/domains/%s/dist-client", vhd->base_dir, domain); + if (mkdir(path_dir, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create dist-client dir\n", __func__); - i.alpn = "http/1.1"; - i.method = "RAW"; - i.path = "/"; - i.host = i.address; - i.origin = i.address; - i.protocol = "lws-dht-dnssec-monitor"; - struct cert_check_info *cci = malloc(sizeof(*cci)); - if (cci) { - memset(cci, 0, sizeof(*cci)); - cci->magic = CERT_CHECK_MAGIC; - lws_strncpy(cci->fqdn, a->subdomain, sizeof(cci->fqdn)); - lws_strncpy(cci->domain, a->domain, sizeof(cci->domain)); - cci->port = a->port; - cci->starttls_state = starttls ? 1 : 0; - i.opaque_user_data = cci; - } - - lwsl_notice("%s: Dispatching %s TLS probe to %s:%d (STARTTLS: %d)\n", __func__, starttls ? "cleartext" : "direct", a->subdomain, a->port, starttls); - - if (!cci || !lws_client_connect_via_info(&i)) { - lwsl_err("%s: Failed to start cert check for %s:%d\n", __func__, a->subdomain, a->port); + lws_snprintf(path_crt, sizeof(path_crt), "%s/distribution-client-%s.crt", path_dir, subdomain); + lws_snprintf(path_key, sizeof(path_key), "%s/distribution-client-%s.key", path_dir, subdomain); - if (cci) free(cci); + if (access(path_crt, F_OK) == 0) return; - struct cert_check_result *cr = malloc(sizeof(*cr)); - if (cr) { - memset(cr, 0, sizeof(*cr)); - lws_strncpy(cr->fqdn, a->subdomain, sizeof(cr->fqdn)); - lws_strncpy(cr->msg, "Connection failed", sizeof(cr->msg)); - cr->port = a->port; - cr->status_err = 1; - lws_dll2_add_tail(&cr->list, &vhd->completed_checks); - lws_callback_on_writable_all_protocol(vhd->context, lws_get_protocol(root_pss->wsi)); - } - } + lws_snprintf(ca_crt, sizeof(ca_crt), "%s/pki/distribution-ca.crt", vhd->base_dir); + lws_snprintf(ca_key, sizeof(ca_key), "%s/pki/distribution-ca.key", vhd->base_dir); + + lwsl_notice("%s: Generating Client Cert for %s\n", __func__, subdomain); + generate_cert_internal(vhd, subdomain, path_crt, path_key, ca_crt, ca_key, 0, 0); } static void handle_req_status(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"status\",\"status\":\"ok\"}\n"); root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } -static int cmp_str(const void *a, const void *b) { - return strcmp(*(const char **)a, *(const char **)b); -} - static void handle_req_get_domains(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - lwsl_notice("[INSTRUMENT] handle_req_get_domains: entering.\n"); - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char path[1024]; DIR *d; struct dirent *de; lws_snprintf(path, sizeof(path), "%s/domains", vhd->base_dir); - lwsl_notice("[INSTRUMENT] handle_req_get_domains: opening dir '%s'\n", path); d = opendir(path); if (!d) { - lwsl_notice("[INSTRUMENT] handle_req_get_domains: failed to open dir.\n"); tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_domains\",\"status\":\"error\",\"msg\":\"Cannot open base_dir\"}\n"); } else { - char **doms = NULL; - size_t count = 0, alloc = 0; - + int first = 1; + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_domains\",\"status\":\"ok\",\"domains\":["); while ((de = readdir(d))) { if (de->d_name[0] == '.') continue; if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) { - if (count >= alloc) { - alloc = alloc ? alloc * 2 : 16; - char **ndoms = realloc(doms, alloc * sizeof(char *)); - if (!ndoms) break; - doms = ndoms; + char whois_path[1024], whois_buf[2048] = "{}"; + char dns_path[1024], dns_buf[1024] = "{}"; + char ds_path[1024], ds_buf[256] = ""; + int fd; + + lws_snprintf(whois_path, sizeof(whois_path), "%s/domains/%s/whois.json", vhd->base_dir, de->d_name); + if ((fd = open(whois_path, O_RDONLY)) >= 0) { + ssize_t nw = read(fd, whois_buf, sizeof(whois_buf) - 1); + if (nw > 0) whois_buf[nw] = '\0'; + close(fd); } - doms[count++] = strdup(de->d_name); - } - } - closedir(d); - - if (count) { - qsort(doms, count, sizeof(char *), cmp_str); - } - - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_domains\",\"status\":\"ok\",\"domains\":["); - for (size_t i = 0; i < count; i++) { - char whois_path[1024], whois_buf[2048] = "{}"; - char local_ds[256] = ""; - - if (i > 0) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); - - lws_snprintf(whois_path, sizeof(whois_path), "%s/domains/%s/whois.json", vhd->base_dir, doms[i]); - int fd_w = open(whois_path, O_RDONLY); - if (fd_w >= 0) { - ssize_t nw = read(fd_w, whois_buf, sizeof(whois_buf) - 1); - if (nw > 0) whois_buf[nw] = '\0'; - close(fd_w); - } - char dns_path[1024], dns_buf[1024] = "{}"; - lws_snprintf(dns_path, sizeof(dns_path), "%s/domains/%s/dns_state.json", vhd->base_dir, doms[i]); - int fd_d = open(dns_path, O_RDONLY); - if (fd_d >= 0) { - ssize_t nw = read(fd_d, dns_buf, sizeof(dns_buf) - 1); - if (nw > 0) dns_buf[nw] = '\0'; - close(fd_d); - } + lws_snprintf(dns_path, sizeof(dns_path), "%s/domains/%s/dns_state.json", vhd->base_dir, de->d_name); + if ((fd = open(dns_path, O_RDONLY)) >= 0) { + ssize_t nw = read(fd, dns_buf, sizeof(dns_buf) - 1); + if (nw > 0) dns_buf[nw] = '\0'; + close(fd); + } - char alg_buf[32] = ""; - char zsk_path[1024]; - lws_snprintf(zsk_path, sizeof(zsk_path), "%s/domains/%s/%s.zsk.private.jwk", vhd->base_dir, doms[i], doms[i]); - lwsl_user("dnssec-monitor: trying to read JWK from %s\n", zsk_path); - int fd_z = open(zsk_path, O_RDONLY); - if (fd_z >= 0) { - char jwk_buf[2048]; - ssize_t nj = read(fd_z, jwk_buf, sizeof(jwk_buf) - 1); - if (nj > 0) { - jwk_buf[nj] = '\0'; - char *p = strstr(jwk_buf, "\"alg\""); - if (p) { - p = strchr(p, ':'); - if (p) { - while (*p == ':' || *p == ' ' || *p == '"' || *p == '\t' || *p == '\n') p++; - char *end = strchr(p, '"'); - if (end && (end - p) < (int)sizeof(alg_buf)) { - lws_strncpy(alg_buf, p, lws_ptr_diff_size_t(end, p) + 1); - lwsl_user("dnssec-monitor: extracted alg: '%s'\n", alg_buf); - } else { - lwsl_user("dnssec-monitor: failed to parse end of alg string\n"); - } - } - } else { - if (strstr(jwk_buf, "\"P-256\"")) { - lws_strncpy(alg_buf, "ES256", sizeof(alg_buf)); - lwsl_user("dnssec-monitor: inferred alg: '%s'\n", alg_buf); - } else if (strstr(jwk_buf, "\"P-384\"")) { - lws_strncpy(alg_buf, "ES384", sizeof(alg_buf)); - lwsl_user("dnssec-monitor: inferred alg: '%s'\n", alg_buf); - } else if (strstr(jwk_buf, "\"RSA\"")) { - lws_strncpy(alg_buf, "RS256", sizeof(alg_buf)); - lwsl_user("dnssec-monitor: inferred alg: '%s'\n", alg_buf); - } else { - lwsl_user("dnssec-monitor: could not find \"alg\" or infer algorithm in JWK\n"); - } + lws_snprintf(ds_path, sizeof(ds_path), "%s/domains/%s/dns_ds.txt", vhd->base_dir, de->d_name); + if ((fd = open(ds_path, O_RDONLY)) >= 0) { + ssize_t nw = read(fd, ds_buf, sizeof(ds_buf) - 1); + if (nw > 0) { + ds_buf[nw] = '\0'; + char *nl = strchr(ds_buf, '\n'); + if (nl) *nl = '\0'; } - } else { - lwsl_user("dnssec-monitor: failed to read JWK (read %d bytes)\n", (int)nj); + close(fd); } - close(fd_z); - } else { - lwsl_user("dnssec-monitor: failed to open JWK file\n"); - } - calc_local_ds(vhd, doms[i], local_ds, sizeof(local_ds)); - - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), - "{\"name\":\"%s\",\"whois\":%s,\"dns\":%s,\"local_ds\":\"%s\",\"alg\":\"%s\"}", - doms[i], whois_buf[0] ? whois_buf : "{}", dns_buf[0] ? dns_buf : "{}", local_ds, alg_buf); - free(doms[i]); + if (!first) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), + "{\"name\":\"%s\",\"whois\":%s,\"dns\":%s,\"local_ds\":\"%s\"}", + de->d_name, whois_buf[0] ? whois_buf : "{}", dns_buf[0] ? dns_buf : "{}", ds_buf); + first = 0; + } } - if (doms) free(doms); + closedir(d); tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "]}\n"); - lwsl_notice("[INSTRUMENT] handle_req_get_domains: constructed JSON array.\n"); } root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); - lwsl_notice("[INSTRUMENT] handle_req_get_domains: exiting, tx_len=%d.\n", (int)root_pss->tx_len); } static void handle_req_create_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; int r = 0; lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s", vhd->base_dir, a->domain); - if (mkdir(d_path, 0755) < 0 && errno != EEXIST) { + if (mkdir(d_path, 0700) < 0 && errno != EEXIST) { lwsl_notice("%s: Failed to create domain dir\n", __func__); r = -1; } + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d", vhd->base_dir, a->domain); + if (mkdir(d_path, 0700) < 0 && errno != EEXIST) { + lwsl_notice("%s: Failed to create conf.d dir\n", __func__); + r = -1; + } + if (r) { tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Failed making dirs\"}\n", a->req); } else { - int fd; + char buf[1024]; + int fd, n; + + /* Create minimal json */ + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d/%s.json", vhd->base_dir, a->domain, a->domain); + fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0600); + if (fd >= 0) { + n = lws_snprintf(buf, sizeof(buf), "{\n \"common-name\": \"%s\"\n}\n", a->domain); + if (write(fd, buf, (size_t)n) < 0) { + lwsl_err("%s: Failed to write conf.d\n", __func__); + } + close(fd); + } /* Touch empty zone */ lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/%s.zone", vhd->base_dir, a->domain, a->domain); - fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0600); if (fd >= 0) close(fd); tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); @@ -1621,7 +907,7 @@ handle_req_create_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_r static void handle_req_delete_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; @@ -1636,7 +922,7 @@ handle_req_delete_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_r static void handle_req_get_zone(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; @@ -1675,7 +961,7 @@ handle_req_get_zone(struct vhd *vhd, struct pss *root_pss, struct monitor_req_ar static void handle_req_get_ipv6_suffix(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char path[1024]; char suffix[64] = {0}; @@ -1702,7 +988,7 @@ handle_req_get_ipv6_suffix(struct vhd *vhd, struct pss *root_pss, struct monitor static void handle_req_set_ipv6_suffix(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char path[1024]; @@ -1710,10 +996,10 @@ handle_req_set_ipv6_suffix(struct vhd *vhd, struct pss *root_pss, struct monitor if (!a->suffix[0]) { unlink(path); } else { - int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600); if (fd < 0 && errno == EACCES) { lws_snprintf(path, sizeof(path), "%s/domains/ipv6_suffix.txt", vhd->base_dir); - fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0600); } if (fd >= 0) { if (write(fd, a->suffix, strlen(a->suffix)) < 0) { @@ -1734,14 +1020,14 @@ handle_req_set_ipv6_suffix(struct vhd *vhd, struct pss *root_pss, struct monitor static void handle_req_update_zone(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; if (!a->zone_buf) goto fail; lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/%s.zone", vhd->base_dir, a->domain, a->domain); - int fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + int fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0600); if (fd >= 0) { if (write(fd, a->zone_buf, (size_t)a->zone_len) == (ssize_t)a->zone_len) { tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); @@ -1756,379 +1042,393 @@ handle_req_update_zone(struct vhd *vhd, struct pss *root_pss, struct monitor_req root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } -#if 0 static void -extract_dane_hash(const char *cert_path, char *dane_out, size_t dane_out_len) +handle_req_get_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - dane_out[0] = '\0'; - int cfd = open(cert_path, O_RDONLY); - if (cfd < 0) return; - - struct stat st; - if (fstat(cfd, &st) || st.st_size <= 0) { - close(cfd); - return; - } - - char *pembuf = malloc((size_t)st.st_size); - if (!pembuf || read(cfd, pembuf, (size_t)st.st_size) != st.st_size) { - if (pembuf) free(pembuf); - close(cfd); - return; - } - close(cfd); - - struct lws_x509_cert *cert = NULL; - if (lws_x509_create(&cert)) { - free(pembuf); - return; - } + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + char d_path[1024]; + DIR *d; + struct dirent *de; - if (lws_x509_parse_from_pem(cert, pembuf, (size_t)st.st_size) < 0) { - free(pembuf); - lws_x509_destroy(&cert); - return; - } - free(pembuf); - - union lws_tls_cert_info_results res1; - union lws_tls_cert_info_results *res; - res1.ns.len = 0; - - if (lws_x509_info(cert, LWS_TLS_CERT_INFO_DER_SPKI, &res1, 0) == -1 && res1.ns.len > 0) { - size_t alloc_len = sizeof(*res) - sizeof(res1.ns.name) + (size_t)res1.ns.len; - res = malloc(alloc_len); - if (res) { - res->ns.len = 0; - if (lws_x509_info(cert, LWS_TLS_CERT_INFO_DER_SPKI, res, (size_t)res1.ns.len) == 0) { - struct lws_genhash_ctx hash_ctx; - uint8_t hash[32]; - - if (!lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) { - if (!lws_genhash_update(&hash_ctx, (uint8_t *)res->ns.name, (size_t)res->ns.len)) { - if (!lws_genhash_destroy(&hash_ctx, hash)) { - char hex[128]; - int hl = 0; - for (int i = 0; i < 32; i++) { - hl += lws_snprintf(hex + hl, sizeof(hex) - (size_t)hl, "%02X", hash[i]); - } - lws_snprintf(dane_out, dane_out_len, "3 1 1 %s", hex); - } - } else { - lws_genhash_destroy(&hash_ctx, NULL); + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d", vhd->base_dir, a->domain); + d = opendir(d_path); + if (!d) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"domain\":\"%s\",\"tls\":[]}\n", a->req, a->domain); + } else { + int first = 1; + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"domain\":\"%s\",\"tls\":[", a->req, a->domain); + while ((de = readdir(d))) { + if (de->d_name[0] == '.') continue; + if (strstr(de->d_name, ".port")) { + char p_path[1024], sub[256]; + lws_strncpy(sub, de->d_name, sizeof(sub)); + char *ext = strstr(sub, ".port"); + if (ext) *ext = '\0'; + lws_snprintf(p_path, sizeof(p_path), "%s/%s", d_path, de->d_name); + int fd = open(p_path, O_RDONLY); + if (fd >= 0) { + char buf[64]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n > 0) { + buf[n] = '\0'; + if (!first) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"fqdn\":\"%s\",\"port\":%d}", sub, atoi(buf)); + first = 0; } + close(fd); } } - free(res); } + closedir(d); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "]}\n"); } - lws_x509_destroy(&cert); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } -#endif static void -handle_req_get_acme_config(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_create_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; + char p1[1024]; + char buf[2048]; + int n, fd; - lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", vhd->base_dir); - int fd = open(d_path, O_RDONLY); + lws_snprintf(p1, sizeof(p1), "%s/domains/%s", vhd->base_dir, a->domain); + if (mkdir(p1, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create domain dir\n", __func__); + + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d", vhd->base_dir, a->domain); + if (mkdir(d_path, 0700) < 0 && errno != EEXIST) + lwsl_notice("%s: Failed to create conf.d dir\n", __func__); + + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d/%s.json", vhd->base_dir, a->domain, a->subdomain); + fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0600); if (fd >= 0) { - char buf[4096]; - ssize_t n = read(fd, buf, sizeof(buf) - 1); - if (n > 0) { - buf[n] = '\0'; - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":%s}\n", buf); + n = lws_snprintf(buf, sizeof(buf), + "{\n \"common-name\": \"%s\",\n \"challenge-type\": \"dns-01\",\n" + " \"email\": \"%s\",\n \"acme\": {\n" + " \"organization\": \"%s\",\n" + " \"directory-url\": \"%s\"\n }\n}\n", + a->subdomain, + a->email[0] ? a->email : "", + a->organization[0] ? a->organization : "", + a->directory_url[0] ? a->directory_url : "https://acme-v02.api.letsencrypt.org/directory"); + + if (write(fd, buf, (size_t)n) == (ssize_t)n) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":{}}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Write failed\"}\n", a->req); } close(fd); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":{}}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Could not create TLS conf\"}\n", a->req); } root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_set_acme_config(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_delete_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; - char buf[4096]; - int n, fd; - lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", vhd->base_dir); - fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); - if (fd >= 0) { - n = lws_snprintf(buf, sizeof(buf), - "{\n \"enabled\": %s,\n \"production\": %s,\n \"email\": \"%s\",\n" - " \"organization\": \"%s\",\n \"country\": \"%s\",\n \"state\": \"%s\",\n" - " \"locality\": \"%s\"\n}\n", - a->enabled ? "true" : "false", - a->production ? "true" : "false", - a->email, a->organization, a->country, a->state, a->locality); + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d/%s.json", vhd->base_dir, a->domain, a->subdomain); + unlink(d_path); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); +} - if (write(fd, buf, (size_t)n) == (ssize_t)n) { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"ok\"}\n"); +static void +handle_req_save_acme_file(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a, const char *dir_suffix) +{ + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + char d_path[1024]; + + if (!a->zone_buf || !a->domain[0] || !a->subdomain[0]) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing payload, domain, or filename\"}\n", a->req); + goto done; + } + + if (strchr(a->domain, '/') || strstr(a->domain, "..") || strchr(a->domain, '\\') || + strchr(a->subdomain, '/') || strstr(a->subdomain, "..") || strchr(a->subdomain, '\\')) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Path traversal\"}\n", a->req); + goto done; + } + + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/%s/%s", vhd->base_dir, a->domain, dir_suffix, a->subdomain); + + int fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0600); + if (fd >= 0) { + if (write(fd, a->zone_buf, (size_t)a->zone_len) == (ssize_t)a->zone_len) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"error\",\"msg\":\"Write failed\"}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Partial write failure\"}\n", a->req); } close(fd); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"error\",\"msg\":\"Could not open config\"}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Could not open file for writing\"}\n", a->req); } +done: root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_get_acme_log(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_save_auth_key(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + handle_req_save_acme_file(vhd, root_pss, a, ""); +} + +static void +handle_req_save_cert(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + handle_req_save_acme_file(vhd, root_pss, a, "certs/crt"); +} + +static void +handle_req_save_key(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + handle_req_save_acme_file(vhd, root_pss, a, "certs/key"); +} + +static void +handle_req_get_all_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; + DIR *d, *d2; + struct dirent *de, *de2; + int first_dom = 1; - lws_snprintf(d_path, sizeof(d_path), "%s/acme.log", vhd->base_dir); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"all_tls\":[", a->req); + + lws_snprintf(d_path, sizeof(d_path), "%s/domains", vhd->base_dir); + d = opendir(d_path); + if (d) { + while ((de = readdir(d))) { + if (de->d_name[0] == '.') continue; + char conf_path[1024]; + lws_snprintf(conf_path, sizeof(conf_path), "%s/domains/%s/conf.d", vhd->base_dir, de->d_name); + d2 = opendir(conf_path); + if (d2) { + if (!first_dom) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"domain\":\"%s\",\"tls\":[", de->d_name); + int first_tls = 1; + while ((de2 = readdir(d2))) { + if (de2->d_name[0] == '.') continue; + if (strstr(de2->d_name, ".port")) { + char p_path[1024], sub[256]; + lws_strncpy(sub, de2->d_name, sizeof(sub)); + char *ext = strstr(sub, ".port"); + if (ext) *ext = '\0'; + lws_snprintf(p_path, sizeof(p_path), "%s/%s", conf_path, de2->d_name); + int fd = open(p_path, O_RDONLY); + if (fd >= 0) { + char buf[64]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n > 0) { + buf[n] = '\0'; + if (!first_tls) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"fqdn\":\"%s\",\"port\":%d}", sub, atoi(buf)); + first_tls = 0; + } + close(fd); + } + } + } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "]}"); + first_dom = 0; + closedir(d2); + } + } + closedir(d); + } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "]}\n"); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); +} + +static void +handle_req_get_acme_config(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + char d_path[1024]; + + lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", vhd->base_dir); int fd = open(d_path, O_RDONLY); if (fd >= 0) { char buf[4096]; - lws_filepos_t size = (lws_filepos_t)lseek(fd, 0, SEEK_END); - lws_filepos_t start = 0; - if (size > 4000) start = size - 4000; - if (lseek(fd, (off_t)start, SEEK_SET) < 0) - lwsl_err("%s: lseek failed\n", __func__); ssize_t n = read(fd, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = '\0'; - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\""); - for (ssize_t i = 0; i < n; i++) { - if (buf[i] == '\n') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\n"); - else if (buf[i] == '"') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\\""); - else if (buf[i] == '\\') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\\\"); - else if (buf[i] >= 32 && buf[i] <= 126) *tx++ = buf[i]; - } - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\"}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":%s}\n", buf); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\"\"}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":{}}\n"); } close(fd); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\"No log found.\"}\n"); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_config\",\"status\":\"ok\",\"config\":{}}\n"); } root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_save_acme_file(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a, const char *dir_suffix, int mode) +handle_req_set_acme_config(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; char d_path[1024]; + char buf[4096]; + int n, fd; - if (!a->zone_buf || !a->domain[0] || !a->subdomain[0]) { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing payload, domain, or filename\"}\n", a->req); - goto done; - } - - if (strchr(a->domain, '/') || strstr(a->domain, "..") || strchr(a->domain, '\\') || - strchr(a->subdomain, '/') || strstr(a->subdomain, "..") || strchr(a->subdomain, '\\')) { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Path traversal\"}\n", a->req); - goto done; - } - - char dir_path[1024]; - lws_snprintf(dir_path, sizeof(dir_path), "%s/domains/%s/%s", vhd->base_dir, a->domain, dir_suffix); - lws_snprintf(d_path, sizeof(d_path), "%s/%s", dir_path, a->subdomain); - - char p[1024]; - lws_strncpy(p, dir_path, sizeof(p)); - char *q = strchr(p + 1, '/'); - while (q) { - *q = '\0'; - if (mkdir(p, 0750) < 0 && errno != EEXIST) - lwsl_err("%s: Failed to create directory %s\n", __func__, p); - *q = '/'; - q = strchr(q + 1, '/'); - } - if (mkdir(p, 0750) < 0 && errno != EEXIST) - lwsl_err("%s: Failed to create directory %s\n", __func__, p); - - int fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, mode); + lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", vhd->base_dir); + fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd >= 0) { - if (write(fd, a->zone_buf, (size_t)a->zone_len) == (ssize_t)a->zone_len) { - /* Permissions */ -#if !defined(WIN32) - struct group *gr = getgrnam("lwsws"); - if (gr) { - if (fchown(fd, (uid_t)-1, gr->gr_gid) < 0) { - lwsl_err("%s: Failed to chown file %s to lwsws group\n", __func__, d_path); - } - if (chown(dir_path, (uid_t)-1, gr->gr_gid) < 0) { - lwsl_err("%s: Failed to chown dir %s to lwsws group\n", __func__, dir_path); - } - } - if (fchmod(fd, (mode_t)mode) < 0) - lwsl_err("%s: Failed to fchmod file %s\n", __func__, d_path); - if (chmod(dir_path, (mode_t)0750) < 0) - lwsl_err("%s: Failed to chmod dir %s\n", __func__, dir_path); -#endif - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); - - /* Update symlinks for .crt or .key if timestamped file */ - const char *ext = strrchr(a->subdomain, '.'); - char base[256]; - lws_strncpy(base, a->subdomain, sizeof(base)); - char *dash = strrchr(base, '-'); - if (ext && dash && (!strcmp(ext, ".crt") || !strcmp(ext, ".key"))) { - *dash = '\0'; - char latest_link[1024], previous_link[1024]; - lws_snprintf(latest_link, sizeof(latest_link), "%s/%s-latest%s", dir_path, base, ext); - lws_snprintf(previous_link, sizeof(previous_link), "%s/%s-previous%s", dir_path, base, ext); - -#if !defined(WIN32) -#if !defined(__COVERITY__) - /* - * Hide readlink from Coverity since it incorrectly flags TOCTOU - * when we later unlink latest_link. - */ - char target[1024]; - ssize_t link_len = readlink(latest_link, target, sizeof(target) - 1); - if (link_len > 0) { - target[link_len] = '\0'; - unlink(previous_link); - symlink(target, previous_link); - if (gr && lchown(previous_link, (uid_t)-1, gr->gr_gid) < 0) - lwsl_err("%s: lchown failed on %s\n", __func__, previous_link); - } -#endif - - unlink(latest_link); - symlink(a->subdomain, latest_link); - if (gr && lchown(latest_link, (uid_t)-1, gr->gr_gid) < 0) - lwsl_err("%s: lchown failed on %s\n", __func__, latest_link); -#endif - } + if (vhd->proxy_uid != (uid_t)-1 || vhd->proxy_gid != (gid_t)-1) + fchown(fd, vhd->proxy_uid, vhd->proxy_gid); + n = lws_snprintf(buf, sizeof(buf), + "{\n \"enabled\": %s,\n \"production\": %s,\n \"email\": \"%s\",\n" + " \"organization\": \"%s\",\n \"country\": \"%s\",\n \"state\": \"%s\",\n" + " \"locality\": \"%s\",\n \"profile\": \"%s\"\n}\n", + a->enabled ? "true" : "false", + a->production ? "true" : "false", + a->email, a->organization, a->country, a->state, a->locality, a->profile); + if (write(fd, buf, (size_t)n) == (ssize_t)n) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"ok\"}\n"); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Partial write failure\"}\n", a->req); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"error\",\"msg\":\"Write failed\"}\n"); } close(fd); } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Could not open file for writing\"}\n", a->req); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_acme_config\",\"status\":\"error\",\"msg\":\"Could not open config\"}\n"); } -done: root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_save_auth_key(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_set_domain_acme(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - handle_req_save_acme_file(vhd, root_pss, a, "", 0600); -} + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + char d_path[1024]; -static void -handle_req_save_cert(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) -{ - handle_req_save_acme_file(vhd, root_pss, a, vhd->acme_production ? "certs/production/crt" : "certs/staging/crt", 0640); + lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/acme_disabled", vhd->base_dir, a->domain); + if (a->enabled) { + unlink(d_path); + } else { + int fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (fd >= 0) { + if (vhd->proxy_uid != (uid_t)-1 || vhd->proxy_gid != (gid_t)-1) + fchown(fd, vhd->proxy_uid, vhd->proxy_gid); + close(fd); + } + } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"set_domain_acme\",\"status\":\"ok\"}\n"); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -force_external_dns(struct lws_context *cx, const char *external_ip) +handle_req_get_acme_log(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - lws_sockaddr46 sa46; - int index = 0; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + char d_path[1024]; - /* Extract exactly what the library natively discovered, and systematically kill it */ - while (!lws_plat_asyncdns_get_server(cx, index++, &sa46)) { - lws_async_dns_server_remove(cx, &sa46); + lws_snprintf(d_path, sizeof(d_path), "%s/acme.log", vhd->base_dir); + int fd = open(d_path, O_RDONLY); + if (fd >= 0) { + char buf[4096]; + lws_filepos_t size = (lws_filepos_t)lseek(fd, 0, SEEK_END); + lws_filepos_t start = 0; + if (size > 4000) start = size - 4000; + if (lseek(fd, (off_t)start, SEEK_SET) >= 0) { + ssize_t n = read(fd, buf, sizeof(buf) - 1); + if (n > 0) { + buf[n] = '\0'; + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\""); + for (ssize_t i = 0; i < n; i++) { + if (buf[i] == '\n') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\n"); + else if (buf[i] == '"') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\\""); + else if (buf[i] == '\\') tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\\\\"); + else if (buf[i] >= 32 && buf[i] <= 126) *tx++ = buf[i]; + } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\"}\n"); + } else { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\"\"}\n"); + } + } + close(fd); + } else { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"get_acme_log\",\"status\":\"ok\",\"log\":\"No log found.\"}\n"); } - - if (lws_sa46_parse_numeric_address(external_ip, &sa46) < 0) - return; - sa46_sockport(&sa46, htons(53)); - lws_async_dns_server_add(cx, &sa46); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_save_key(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_trigger_resign(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - handle_req_save_acme_file(vhd, root_pss, a, vhd->acme_production ? "certs/production/key" : "certs/staging/key", 0600); + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536 - 1; + + lwsl_notice("%s: ACME client triggered immediate re-sign for all zones\n", __func__); + lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 100 * LWS_US_PER_MS); + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"trigger_resign\",\"status\":\"ok\"}\n"); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void handle_req_update_whois(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; - - lwsl_notice("[INSTRUMENT] handle_req_update_whois START for domain: '%s', zone_buf present: %d\n", a->domain, !!a->zone_buf); - + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; if (a->domain[0] && a->zone_buf) { char path[1024]; lws_snprintf(path, sizeof(path), "%s/domains/%s/whois.json", vhd->base_dir, a->domain); int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd >= 0) { + if (vhd->proxy_uid != (uid_t)-1 || vhd->proxy_gid != (gid_t)-1) + fchown(fd, vhd->proxy_uid, vhd->proxy_gid); char decoded[8192]; int n = lws_b64_decode_string(a->zone_buf, decoded, sizeof(decoded)); - lwsl_notice("[INSTRUMENT] lws_b64_decode_string returned %d for %s\n", n, a->domain); - if (n > 0) { - if (write(fd, decoded, (size_t)n) < 0) { - lwsl_err("[INSTRUMENT] %s: Failed writing to %s (errno: %d)\n", __func__, path, errno); - } else { - lwsl_info("[INSTRUMENT] %s: Successfully synced WHOIS via UDS IPC for %s\n", __func__, a->domain); - } - } else { - lwsl_err("[INSTRUMENT] %s: Failed B64 decode on whois zone payload size=%d\n", __func__, (int)a->zone_len); - } + if (n > 0) write(fd, decoded, (size_t)n); close(fd); - } else { - lwsl_err("[INSTRUMENT] %s: Failed to open %s for writing! errno: %d\n", __func__, path, errno); } - } else { - lwsl_err("[INSTRUMENT] %s: Failed prerequisites. domain: '%s', zone_buf present: %d\n", __func__, a->domain, !!a->zone_buf); } - - /* Empty response is fine, IPC fire-and-forget */ root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void handle_req_regen_keys(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; char *tx_end = tx + 65536 - 1; if (vhd->ops && vhd->ops->keygen) { struct lws_dht_dnssec_keygen_args kargs; memset(&kargs, 0, sizeof(kargs)); - char wd[1024]; lws_snprintf(wd, sizeof(wd), "%s/domains/%s", vhd->base_dir, a->domain); - kargs.domain = a->domain; kargs.workdir = wd; - if (!strcmp(a->key_type, "ES256")) { - kargs.type = "EC"; kargs.curve = "P-256"; kargs.bits = 256; - } else if (!strcmp(a->key_type, "ES384")) { - kargs.type = "EC"; kargs.curve = "P-384"; kargs.bits = 384; - } else if (!strcmp(a->key_type, "R1024")) { - kargs.type = "RSA"; kargs.bits = 1024; - } else if (!strcmp(a->key_type, "R2048")) { - kargs.type = "RSA"; kargs.bits = 2048; - } else { - kargs.type = "EC"; kargs.curve = "P-256"; kargs.bits = 256; - } - - lwsl_notice("%s: Regenerating keys for %s using %s\n", __func__, a->domain, kargs.type); + if (!strcmp(a->key_type, "ES256")) { kargs.type = "EC"; kargs.curve = "P-256"; kargs.bits = 256; } + else if (!strcmp(a->key_type, "ES384")) { kargs.type = "EC"; kargs.curve = "P-384"; kargs.bits = 384; } + else if (!strcmp(a->key_type, "R1024")) { kargs.type = "RSA"; kargs.bits = 1024; } + else if (!strcmp(a->key_type, "R2048")) { kargs.type = "RSA"; kargs.bits = 2048; } + else { kargs.type = "EC"; kargs.curve = "P-256"; kargs.bits = 256; } if (!vhd->ops->keygen(vhd->context, &kargs)) { - /* Force resign by deleting the signed zone */ char signed_path[1024]; lws_snprintf(signed_path, sizeof(signed_path), "%s/%s.zone.signed", wd, a->domain); unlink(signed_path); - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); } else { tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Key generation failed\"}\n", a->req); @@ -2139,93 +1439,367 @@ handle_req_regen_keys(struct vhd *vhd, struct pss *root_pss, struct monitor_req_ root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } +struct lws * handle_req_get_acme_profiles(struct vhd *vhd, struct pss *root_pss, const char *directory_url) +{ + struct lws_client_connect_info i; + memset(&i, 0, sizeof(i)); + i.context = vhd->context; + struct lws_vhost *vh = lws_get_vhost_by_name(vhd->context, "root-monitor-dummy"); + i.vhost = vh ? vh : vhd->vhost; + + const char *url = (vhd->acme_production) ? + "https://acme-v02.api.letsencrypt.org/directory" : + "https://acme-staging-v02.api.letsencrypt.org/directory"; + + i.address = vhd->acme_production ? "acme-v02.api.letsencrypt.org" : "acme-staging-v02.api.letsencrypt.org"; + i.port = 443; + i.ssl_connection = LCCSCF_USE_SSL; + i.alpn = "http/1.1"; + i.method = "GET"; + i.path = "/directory"; + i.host = i.address; + i.origin = i.address; + i.protocol = "lws-dht-dnssec-monitor"; + + struct acme_profiles_fetch_info *afi = malloc(sizeof(*afi)); + if (afi) { + memset(afi, 0, sizeof(*afi)); + afi->magic = ACME_PROFILES_MAGIC; + afi->root_pss = root_pss; + i.opaque_user_data = afi; + } + + lwsl_notice("%s: Fetching ACME directory from %s\n", __func__, url); + struct lws *wsi = lws_client_connect_via_info(&i); + if (!wsi && afi) { + free(afi); + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"get_acme_profiles\",\"status\":\"error\",\"msg\":\"Failed to connect to ACME directory\"}\n"); + lws_callback_on_writable_all_protocol(vhd->context, lws_get_protocol(root_pss->wsi)); + } + return wsi; +} + +static void handle_get_acme_profiles_wrapper(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + handle_req_get_acme_profiles(vhd, root_pss, NULL); +} + static void -handle_req_create_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_get_dist_server_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; - char *tx_end = (char *)&root_pss->tx[LWS_PRE + 65536 - 1]; - char d_path[1024]; - char buf[64]; - int n, fd; - - lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d", vhd->base_dir, a->domain); - if (mkdir(d_path, 0755) < 0 && errno != EEXIST) - lwsl_err("%s: Failed to create directory %s\n", __func__, d_path); + char *tx_end = tx + 65536 - 1; + char path[1024]; + char domain[256] = ""; + int fd; - lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d/%s.port", vhd->base_dir, a->domain, a->subdomain); - fd = open(d_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + lws_snprintf(path, sizeof(path), "%s/dist_server_domain.txt", vhd->base_dir); + fd = open(path, O_RDONLY); if (fd >= 0) { - n = lws_snprintf(buf, sizeof(buf), "%d\n", a->port); - if (write(fd, buf, (size_t)n) == (ssize_t)n) { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); - } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Write failed\"}\n", a->req); + ssize_t n = read(fd, domain, sizeof(domain) - 1); + if (n > 0) { + domain[n] = '\0'; + char *nl = strchr(domain, '\n'); + if (nl) *nl = '\0'; } close(fd); - } else { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Could not create TLS conf\"}\n", a->req); } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"domain\":\"%s\"}\n", a->req, domain); root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_delete_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_set_dist_server_domain(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; - char *tx_end = (char *)&root_pss->tx[LWS_PRE + 65536 - 1]; - char d_path[1024]; + char *tx_end = tx + 65536 - 1; + char path[1024]; + int fd; - lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d/%s.port", vhd->base_dir, a->domain, a->subdomain); - unlink(d_path); + lws_snprintf(path, sizeof(path), "%s/dist_server_domain.txt", vhd->base_dir); + + if (!a->domain[0]) { + unlink(path); + } else { + fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + if (fd >= 0) { + if (write(fd, a->domain, strlen(a->domain)) < 0) { + lwsl_err("%s: Failed writing dist server domain\n", __func__); + } + close(fd); + } else { + lwsl_err("%s: Failed to open %s for write (errno=%d)\n", __func__, path, errno); + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Failed to write configuration\"}\n", a->req); + goto done; + } + } tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\"}\n", a->req); +done: root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } static void -handle_req_get_tls(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +handle_req_provisioning_bundle(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) { char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; - char *tx_end = (char *)&root_pss->tx[LWS_PRE + 65536 - 1]; - char d_path[1024]; - DIR *d; - struct dirent *de; + char *tx_end = tx + 65536; + char path[512], *ca = NULL, *crt = NULL, *key = NULL; + struct stat st; + int fd; - lws_snprintf(d_path, sizeof(d_path), "%s/domains/%s/conf.d", vhd->base_dir, a->domain); - d = opendir(d_path); - if (!d) { - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"tls\":[]}\n", a->req); - } else { - int first = 1; - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"tls\":[", a->req); - while ((de = readdir(d))) { - if (de->d_name[0] == '.') continue; - if (strstr(de->d_name, ".port")) { - char p_path[1024]; - char sub[256]; - lws_strncpy(sub, de->d_name, sizeof(sub)); - char *ext = strstr(sub, ".port"); - if (ext) *ext = '\0'; + if (!a->domain[0] || !a->subdomain[0]) { + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing domain or subdomain\"}\n", a->req); + return; + } - lws_snprintf(p_path, sizeof(p_path), "%s/domains/%s/conf.d/%s", vhd->base_dir, a->domain, de->d_name); - int fd = open(p_path, O_RDONLY); + generate_client_cert(vhd, a->domain, a->subdomain); + + lws_snprintf(path, sizeof(path), "%s/pki/distribution-ca.crt", vhd->base_dir); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + ca = malloc((size_t)st.st_size + 1); + if (ca && read(fd, ca, (size_t)st.st_size) == (ssize_t)st.st_size) ca[st.st_size] = '\0'; + else { free(ca); ca = NULL; } + } + close(fd); + } + + lws_snprintf(path, sizeof(path), "%s/domains/%s/dist-client/distribution-client-%s.crt", vhd->base_dir, a->domain, a->subdomain); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + crt = malloc((size_t)st.st_size + 1); + if (crt && read(fd, crt, (size_t)st.st_size) == (ssize_t)st.st_size) crt[st.st_size] = '\0'; + else { free(crt); crt = NULL; } + } + close(fd); + } + + lws_snprintf(path, sizeof(path), "%s/domains/%s/dist-client/distribution-client-%s.key", vhd->base_dir, a->domain, a->subdomain); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + key = malloc((size_t)st.st_size + 1); + if (key && read(fd, key, (size_t)st.st_size) == (ssize_t)st.st_size) key[st.st_size] = '\0'; + else { free(key); key = NULL; } + } + close(fd); + } + + if (!ca || !crt || !key) { + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Certificate files not found on server\"}\n", a->req); + goto bail; + } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"subdomain\":\"%s\",\"ca\":\"", a->req, a->subdomain); + char *p = ca; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\",\"cert\":\""); + p = crt; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\",\"key\":\""); + p = key; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\"}\n"); + +bail: + if (ca) free(ca); + if (crt) free(crt); + if (key) free(key); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); +} + +static void +handle_req_download_dist_ca(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536; + char path[512], *ca = NULL; + struct stat st; + int fd; + + lws_snprintf(path, sizeof(path), "%s/pki/distribution-ca.crt", vhd->base_dir); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + ca = malloc((size_t)st.st_size + 1); + if (ca && read(fd, ca, (size_t)st.st_size) == (ssize_t)st.st_size) ca[st.st_size] = '\0'; + else { free(ca); ca = NULL; } + } + close(fd); + } + + if (!ca) { + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"CA not found\"}\n", a->req); + return; + } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"ca\":\"", a->req); + char *p = ca; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\"}\n"); + + free(ca); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); +} + +static void +handle_req_download_dist_server(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; + char *tx_end = tx + 65536; + char path[512], *ca = NULL, *crt = NULL, *key = NULL; + struct stat st; + int fd; + + if (!a->domain[0]) { + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing domain parameter\"}\n", a->req); + return; + } + + generate_dist_server_cert(vhd, a->domain); + + lws_snprintf(path, sizeof(path), "%s/pki/distribution-ca.crt", vhd->base_dir); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + ca = malloc((size_t)st.st_size + 1); + if (ca && read(fd, ca, (size_t)st.st_size) == (ssize_t)st.st_size) ca[st.st_size] = '\0'; + else { free(ca); ca = NULL; } + } + close(fd); + } + + lws_snprintf(path, sizeof(path), "%s/pki/distribution-server-%s.crt", vhd->base_dir, a->domain); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + crt = malloc((size_t)st.st_size + 1); + if (crt && read(fd, crt, (size_t)st.st_size) == (ssize_t)st.st_size) crt[st.st_size] = '\0'; + else { free(crt); crt = NULL; } + } + close(fd); + } + + lws_snprintf(path, sizeof(path), "%s/pki/distribution-server-%s.key", vhd->base_dir, a->domain); + fd = open(path, O_RDONLY); + if (fd >= 0) { + if (fstat(fd, &st) == 0) { + key = malloc((size_t)st.st_size + 1); + if (key && read(fd, key, (size_t)st.st_size) == (ssize_t)st.st_size) key[st.st_size] = '\0'; + else { free(key); key = NULL; } + } + close(fd); + } + + if (!ca || !crt || !key) { + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Server cert/key not found\"}\n", a->req); + goto bail; + } + + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"%s\",\"status\":\"ok\",\"ca\":\"", a->req); + char *p = ca; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\",\"cert\":\""); + p = crt; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\",\"key\":\""); + p = key; while (p && *p) { if (*p == '\n') { *tx++ = '\\'; *tx++ = 'n'; } else if (*p != '\r') *tx++ = *p; p++; } + tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "\",\"domain\":\"%s\"}\n", a->domain); + +bail: + if (ca) free(ca); + if (crt) free(crt); + if (key) free(key); + root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); +} + +static void +handle_req_check_cert(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a) +{ + struct lws_client_connect_info i; + memset(&i, 0, sizeof(i)); + i.context = vhd->context; + struct lws_vhost *vh = lws_get_vhost_by_name(vhd->context, "root-monitor-dummy"); + i.vhost = vh ? vh : vhd->vhost; + i.address = a->subdomain; + i.port = a->port; + i.ssl_connection = LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; + int starttls = (a->port == 25 || a->port == 587); + if (!starttls) i.ssl_connection |= LCCSCF_USE_SSL; + i.alpn = "http/1.1"; i.method = "RAW"; i.path = "/"; i.host = i.address; i.origin = i.address; i.protocol = "lws-dht-dnssec-monitor"; + struct cert_check_info *cci = malloc(sizeof(*cci)); + if (cci) { + memset(cci, 0, sizeof(*cci)); + cci->magic = CERT_CHECK_MAGIC; + lws_strncpy(cci->fqdn, a->subdomain, sizeof(cci->fqdn)); + lws_strncpy(cci->domain, a->domain, sizeof(cci->domain)); + cci->port = a->port; cci->starttls_state = starttls ? 1 : 0; + i.opaque_user_data = cci; + } + if (!cci || !lws_client_connect_via_info(&i)) { + if (cci) free(cci); + struct cert_check_result *cr = malloc(sizeof(*cr)); + if (cr) { + memset(cr, 0, sizeof(*cr)); + lws_strncpy(cr->fqdn, a->subdomain, sizeof(cr->fqdn)); + lws_strncpy(cr->msg, "Connection failed", sizeof(cr->msg)); + lws_strncpy(cr->local_msg, "Not Found", sizeof(cr->local_msg)); + lws_strncpy(cr->issuer, "Unknown", sizeof(cr->issuer)); + + if (a->domain[0]) { + char path[1024]; + lws_snprintf(path, sizeof(path), "%s/domains/%s/certs/%s/crt/%s-latest.crt", vhd->base_dir, a->domain, vhd->acme_production ? "production" : "staging", a->subdomain); + lwsl_notice("%s: Checking local cert at %s\n", __func__, path); + int fd = open(path, O_RDONLY); if (fd >= 0) { - char buf[64]; - ssize_t n = read(fd, buf, sizeof(buf) - 1); - if (n > 0) { - buf[n] = '\0'; - int port = atoi(buf); - if (!first) tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), ","); - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"fqdn\":\"%s\",\"port\":%d}", sub, port); - first = 0; + struct stat st; + if (!fstat(fd, &st) && st.st_size > 0) { + uint8_t *pem = malloc((size_t)st.st_size + 1); + if (pem) { + if (read(fd, pem, (size_t)st.st_size) == (ssize_t)st.st_size) { + pem[st.st_size] = '\0'; + struct lws_x509_cert *x509 = NULL; + if (!lws_x509_create(&x509)) { + if (!lws_x509_parse_from_pem(x509, pem, (size_t)st.st_size + 1)) { + union lws_tls_cert_info_results lci; + if (!lws_x509_info(x509, LWS_TLS_CERT_INFO_ISSUER_NAME, &lci, 0)) + lws_strncpy(cr->issuer, lci.ns.name, sizeof(cr->issuer)); + if (!lws_x509_info(x509, LWS_TLS_CERT_INFO_VALIDITY_TO, &lci, 0)) { + time_t now; time(&now); + if (now > lci.time) lws_snprintf(cr->local_msg, sizeof(cr->local_msg), "Expired"); + else lws_snprintf(cr->local_msg, sizeof(cr->local_msg), "%d days", (int)((lci.time - now) / (24 * 3600))); + } + } else { + lwsl_err("%s: Failed to parse PEM at %s\n", __func__, path); + } + lws_x509_destroy(&x509); + } + } + free(pem); + } } close(fd); + } else { + lwsl_err("%s: Failed to open %s: %d\n", __func__, path, errno); } } + + cr->port = a->port; cr->status_err = 1; + + char json[1024]; + int n = lws_snprintf(json, sizeof(json), "{\"req\":\"cert_status\",\"subdomain\":\"%s\",\"port\":%d,\"status\":\"error\",\"msg\":\"%s\",\"local_msg\":\"%s\",\"issuer\":\"%s\"}\n", + cr->fqdn, cr->port, cr->msg, cr->local_msg, cr->issuer); + + struct vhd *target_vhd = global_root_vhd ? global_root_vhd : vhd; + lws_start_foreach_dll(struct lws_dll2 *, p, target_vhd->clients.head) { + struct pss *wpss = lws_container_of(p, struct pss, list); + if (wpss->tx_len + (size_t)n < 65536 - LWS_PRE) { + memcpy(&wpss->tx[LWS_PRE + wpss->tx_len], json, (size_t)n); + wpss->tx_len += (size_t)n; + lws_callback_on_writable(wpss->wsi); + } + } lws_end_foreach_dll(p); + free(cr); } - closedir(d); - tx += lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "]}\n"); } - root_pss->tx_len = lws_ptr_diff_size_t(tx, (char *)&root_pss->tx[LWS_PRE]); } typedef void (*monitor_req_handler_t)(struct vhd *vhd, struct pss *root_pss, struct monitor_req_args *a); @@ -2240,19 +1814,29 @@ static const struct monitor_req_map { { "delete_domain", handle_req_delete_domain }, { "get_zone", handle_req_get_zone }, { "update_zone", handle_req_update_zone }, - { "get_acme_config", handle_req_get_acme_config }, - { "set_acme_config", handle_req_set_acme_config }, - { "get_acme_log", handle_req_get_acme_log }, - { "update_whois", handle_req_update_whois }, + { "get_tls", handle_req_get_tls }, + { "get_all_tls", handle_req_get_all_tls }, + { "create_tls", handle_req_create_tls }, + { "delete_tls", handle_req_delete_tls }, { "save_auth_key", handle_req_save_auth_key }, { "save_cert", handle_req_save_cert }, { "save_key", handle_req_save_key }, { "get_ipv6_suffix", handle_req_get_ipv6_suffix }, { "set_ipv6_suffix", handle_req_set_ipv6_suffix }, + { "get_acme_config", handle_req_get_acme_config }, + { "set_acme_config", handle_req_set_acme_config }, + { "set_domain_acme", handle_req_set_domain_acme }, + { "get_acme_profiles", handle_get_acme_profiles_wrapper }, + { "get_acme_log", handle_req_get_acme_log }, + { "trigger_resign", handle_req_trigger_resign }, + { "update_whois", handle_req_update_whois }, { "regen_keys", handle_req_regen_keys }, - { "get_tls", handle_req_get_tls }, - { "create_tls", handle_req_create_tls }, - { "delete_tls", handle_req_delete_tls } + { "provisioning_bundle", handle_req_provisioning_bundle }, + { "download_dist_ca", handle_req_download_dist_ca }, + { "download_dist_server", handle_req_download_dist_server }, + { "get_dist_server_domain", handle_req_get_dist_server_domain }, + { "set_dist_server_domain", handle_req_set_dist_server_domain }, + { "check_cert", handle_req_check_cert } }; static void @@ -2260,7 +1844,7 @@ handle_monitor_request(struct vhd *vhd, struct pss *root_pss, const char *in, si { struct monitor_req_args a; struct lejp_ctx jctx; - char *tx = (char *)&root_pss->tx[LWS_PRE]; + char *tx = (char *)&root_pss->tx[LWS_PRE + root_pss->tx_len]; const size_t req_map_size = LWS_ARRAY_SIZE(req_map); memset(&a, 0, sizeof(a)); @@ -2268,23 +1852,21 @@ handle_monitor_request(struct vhd *vhd, struct pss *root_pss, const char *in, si int m = lejp_parse(&jctx, (uint8_t *)in, (int)len); lejp_destruct(&jctx); - lwsl_notice("[INSTRUMENT] handle_monitor_request: executed lejp_parse. len: %d, rc: %d. String: '%.*s'\n", (int)len, m, (int)len, in); + // lwsl_debug("[INSTRUMENT] handle_monitor_request: executed lejp_parse. len: %d, rc: %d. String: '%.*s'\n", (int)len, m, (int)len, in); if (m < 0 && m != LEJP_REJECT_UNKNOWN) { - lwsl_notice("[INSTRUMENT] handle_monitor_request: JSON parser failed! Error %d\n", m); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"JSON parse failed: %d\"}\n", a.req[0] ? a.req : "unknown", m); + lwsl_notice("[INSTRUMENT] handle_monitor_request: JSON parser failed! Error %d, len %d, in:\n%.*s\n", m, (int)len, (int)len, in); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"JSON parse failed: %d\"}\n", a.req[0] ? a.req : "unknown", m); goto done; } if (!a.req[0]) { lwsl_notice("[INSTRUMENT] handle_monitor_request: Missing 'req' parameter in JSON payload!\n"); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"status\":\"error\",\"msg\":\"Missing req\"}\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"status\":\"error\",\"msg\":\"Missing req\"}\n"); goto done; } - lwsl_notice("[INSTRUMENT] handle_monitor_request: Routed valid requested endpoint: '%s'\n", a.req); - - + lwsl_debug("[INSTRUMENT] handle_monitor_request: Routed valid requested endpoint: '%s'\n", a.req); if (vhd->auth_jwk.kty == LWS_GENCRYPTO_KTY_OCT) { char jwt_out[2048]; @@ -2294,49 +1876,61 @@ handle_monitor_request(struct vhd *vhd, struct pss *root_pss, const char *in, si if (!a.jwt[0]) { lwsl_notice("[INSTRUMENT] Missing JWT preamble token\n"); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); goto done; } if (lws_jwt_signed_validate(vhd->context, &vhd->auth_jwk, "HS256", a.jwt, strlen(a.jwt), jwt_temp, sizeof(jwt_temp), jwt_out, &jwt_out_len)) { lwsl_notice("[INSTRUMENT] Invalid/Forged JWT preamble token\n"); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); goto done; } if (lws_jwt_token_sanity(jwt_out, jwt_out_len, "acme-ipc", "dnssec-monitor", NULL, NULL, 0, &exp_time)) { lwsl_notice("[INSTRUMENT] Expired/Invalid JWT claims\n"); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); goto done; } } else { - lwsl_notice("[INSTRUMENT] Warning: UDS monitor secret not bootstrapped, accepting local IPC request without JWT\n"); + lwsl_notice("[INSTRUMENT] Warning: UDS monitor secret not bootstrapped, rejecting request!\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"status\":\"error\",\"msg\":\"Authentication Failed\"}\n"); + goto done; } /* Prevent path traversal attacks */ if (strchr(a.domain, '/') || strstr(a.domain, "..") || strchr(a.subdomain, '/') || strstr(a.subdomain, "..")) { - lwsl_notice("[INSTRUMENT] handle_monitor_request: Path traversal parameters detected\n"); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Invalid chars in domain\"}\n", a.req); + lwsl_debug("[INSTRUMENT] handle_monitor_request: Path traversal parameters detected\n"); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Invalid chars in domain\"}\n", a.req); goto done; } for (size_t i = 0; i < req_map_size; i++) { if (!strcmp(a.req, req_map[i].name)) { /* Enforce domain param if required by the handler */ - if (i > 0 && !a.domain[0] && strcmp(req_map[i].name, "status") && strcmp(req_map[i].name, "get_domains") && strcmp(req_map[i].name, "get_ipv6_suffix") && strcmp(req_map[i].name, "set_ipv6_suffix") && strcmp(req_map[i].name, "get_acme_config") && strcmp(req_map[i].name, "set_acme_config") && strcmp(req_map[i].name, "get_acme_log")) { + if (i > 0 && !a.domain[0] && + strcmp(req_map[i].name, "status") && + strcmp(req_map[i].name, "get_domains") && + strcmp(req_map[i].name, "get_ipv6_suffix") && + strcmp(req_map[i].name, "set_ipv6_suffix") && + strcmp(req_map[i].name, "get_all_tls") && + strcmp(req_map[i].name, "get_acme_config") && + strcmp(req_map[i].name, "get_acme_profiles") && + strcmp(req_map[i].name, "get_acme_log") && + strcmp(req_map[i].name, "get_dist_server_domain") && + strcmp(req_map[i].name, "set_dist_server_domain")) { lwsl_notice("[INSTRUMENT] handle_monitor_request: Missing required 'domain' param for %s\n", a.req); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing arguments\"}\n", a.req); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"%s\",\"status\":\"error\",\"msg\":\"Missing arguments\"}\n", a.req); goto done; } - lwsl_notice("[INSTRUMENT] handle_monitor_request: Calling map callback...\n"); + lwsl_debug("[INSTRUMENT] handle_monitor_request: Calling map callback...\n"); req_map[i].cb(vhd, root_pss, &a); - lwsl_notice("[INSTRUMENT] handle_monitor_request: Callback generated response size %d\n", (int)root_pss->tx_len); + lwsl_debug("[INSTRUMENT] handle_monitor_request: Callback generated response size %d\n", (int)root_pss->tx_len); goto done; } } lwsl_notice("[INSTRUMENT] handle_monitor_request: Unknown request parameter '%s'\n", a.req); - root_pss->tx_len = (size_t)lws_snprintf(tx, 65536, "{\"req\":\"unknown\",\"status\":\"error\",\"msg\":\"Unknown req %s\"}\n", a.req); + root_pss->tx_len += (size_t)lws_snprintf(tx, 65536 - root_pss->tx_len, "{\"req\":\"unknown\",\"status\":\"error\",\"msg\":\"Unknown req %s\"}\n", a.req); done: if (a.zone_buf) free(a.zone_buf); @@ -2367,6 +1961,7 @@ connect_retry_cb(lws_sorted_usec_list_t *sul) i.port = 0; i.host = "localhost"; i.origin = "localhost"; + i.method = "RAW"; i.local_protocol_name = "lws-dht-dnssec-monitor"; i.opaque_user_data = pss; i.pwsi = &pss->cwsi; @@ -2383,19 +1978,11 @@ connect_retry_cb(lws_sorted_usec_list_t *sul) } } -static void -extract_and_queue_cert_result(struct lws *wsi, struct vhd *vhd, struct cert_check_info *cci, const struct lws_protocols *protocol) +static void extract_and_queue_cert_result(struct lws *wsi, struct vhd *vhd, struct cert_check_info *cci, const struct lws_protocols *protocol) { union lws_tls_cert_info_results ci; char msg[128]; - char issuer[128] = "Unknown"; - char local_msg[128] = "Not Found"; int err = 0; - - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME, &ci, 0)) { - lws_strncpy(issuer, ci.ns.name, sizeof(issuer)); - } - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO, &ci, 0)) { time_t now; time(&now); @@ -2410,163 +1997,41 @@ extract_and_queue_cert_result(struct lws *wsi, struct vhd *vhd, struct cert_chec err = 1; } - int has_local_cert = 0; - /* Read local cert expiry */ - if (cci->domain[0]) { - char path[1024]; - lws_snprintf(path, sizeof(path), "%s/domains/%s/certs/%s/crt/%s-latest.crt", vhd->base_dir, cci->domain, vhd->acme_production ? "production" : "staging", cci->fqdn); - int fd = open(path, O_RDONLY); - if (fd >= 0) { - has_local_cert = 1; - struct stat st; - if (!fstat(fd, &st) && st.st_size > 0) { - uint8_t *pem = malloc((size_t)st.st_size + 1); - if (pem) { - if (read(fd, pem, (size_t)st.st_size) == st.st_size) { - pem[st.st_size] = '\0'; - struct lws_x509_cert *x509 = NULL; - if (!lws_x509_create(&x509)) { - if (!lws_x509_parse_from_pem(x509, pem, (size_t)st.st_size + 1)) { - union lws_tls_cert_info_results lci; - if (!lws_x509_info(x509, LWS_TLS_CERT_INFO_VALIDITY_TO, &lci, 0)) { - time_t now; - time(&now); - if (now > lci.time) { - lws_snprintf(local_msg, sizeof(local_msg), "Expired"); - } else { - int days = (int)((lci.time - now) / (24 * 3600)); - lws_snprintf(local_msg, sizeof(local_msg), "%d days", days); - } - } - } - lws_x509_destroy(&x509); - } - } - free(pem); - } - } - close(fd); - } - } - - if (cci->is_automated) { - int needs_acme = 0; - if (err) { - lwsl_notice("%s: AUTOMATED PROBE %s:%d FAILED: %s (Triggering ACME)\n", __func__, cci->fqdn, cci->port, msg); - needs_acme = 1; - } else { - union lws_tls_cert_info_results ci_from; - if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_FROM, &ci_from, 0)) { - time_t now; - time(&now); - time_t total = ci.time - ci_from.time; - time_t remaining = ci.time - now; - if (total > 0 && remaining < (total / 5)) { - lwsl_notice("%s: AUTOMATED PROBE %s:%d SUCCESS: Cert served expires in %s (Triggering ACME - <20%% validity left)\n", __func__, cci->fqdn, cci->port, msg); - needs_acme = 1; - } else if (!has_local_cert && !vhd->acme_production) { - lwsl_notice("%s: AUTOMATED PROBE %s:%d SUCCESS: Cert served expires in %s (Triggering ACME - no matching local staging cert)\n", __func__, cci->fqdn, cci->port, msg); - needs_acme = 1; - } else { - lwsl_notice("%s: AUTOMATED PROBE %s:%d SUCCESS: Cert served expires in %s (ACME not needed)\n", __func__, cci->fqdn, cci->port, msg); - } - } - } - - if (needs_acme && vhd->acme_enabled) { - char conf_dir[1024]; - lws_snprintf(conf_dir, sizeof(conf_dir), "%s/domains/%s/conf.d", vhd->base_dir, cci->fqdn); - if (mkdir(conf_dir, 0755) < 0 && errno != EEXIST) - lwsl_notice("%s: Failed to create conf.d dir\n", __func__); - - /* Stop writing redundant per-subdomain JSONs. Rely on global acme_config.json */ - - char vh_name[256]; - lws_snprintf(vh_name, sizeof(vh_name), "acme_%s", cci->fqdn); - if (!lws_get_vhost_by_name(vhd->context, vh_name)) { - struct lws_context_creation_info info; - struct acme_pvo_alloc *pa = malloc(sizeof(*pa)); - - if (!pa) { - lwsl_err("%s: OOM allocating ACME PVOs\n", __func__); - return; - } - memset(pa, 0, sizeof(*pa)); - lws_strncpy(pa->root_domain, cci->fqdn, sizeof(pa->root_domain)); - lws_strncpy(pa->common_name, cci->fqdn, sizeof(pa->common_name)); - - memset(&info, 0, sizeof(info)); - info.port = CONTEXT_PORT_NO_LISTEN_SERVER; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.vhost_name = vh_name; - - pa->pvo_core.name = "lws-acme-client-core"; - pa->pvo_core.next = &pa->pvo_acme; - - pa->pvo_acme.name = "lws-acme-client-dns"; - pa->pvo_acme.options = &pa->pvo1; - info.pvo = &pa->pvo_core; - - pa->pvo1.name = "root-domain"; - pa->pvo1.value = pa->root_domain; - pa->pvo1.next = &pa->pvo2; - - pa->pvo2.name = "common-name"; - pa->pvo2.value = pa->common_name; - pa->pvo2.next = &pa->pvo3; - - pa->pvo3.name = "email"; - pa->pvo3.value = vhd->acme_email[0] ? vhd->acme_email : "admin@domain.com"; - pa->pvo3.next = &pa->pvo4; - - pa->pvo4.name = "directory-url"; - pa->pvo4.value = vhd->acme_production ? "https://acme-v02.api.letsencrypt.org/directory" : "https://acme-staging-v02.api.letsencrypt.org/directory"; - - info.finalize = acme_vhost_finalize; - info.finalize_arg = pa; - - if (lws_create_vhost(vhd->context, &info)) { - lwsl_notice("%s: ACME vhost %s spawned natively\n", __func__, vh_name); - } else { - lwsl_err("%s: Failed to spawn ACME vhost %s\n", __func__, vh_name); - free(pa); - } - } - } - return; - } - struct cert_check_result *cr = malloc(sizeof(*cr)); if (cr) { memset(cr, 0, sizeof(*cr)); lws_strncpy(cr->fqdn, cci->fqdn, sizeof(cr->fqdn)); - cr->port = cci->port; char *colon = strchr(cr->fqdn, ':'); if (colon) *colon = '\0'; + cr->port = cci->port; lws_strncpy(cr->msg, msg, sizeof(cr->msg)); - lws_strncpy(cr->local_msg, local_msg, sizeof(cr->local_msg)); - lws_strncpy(cr->issuer, issuer, sizeof(cr->issuer)); cr->status_err = err; - lws_dll2_add_tail(&cr->list, &vhd->completed_checks); - lws_callback_on_writable_all_protocol(vhd->context, protocol); - } -} -static void -root_monitor_stdin_check_cb(struct lws_sorted_usec_list *sul) -{ - struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer); - struct pollfd pfd; - pfd.fd = 0; /* stdin */ - pfd.events = POLLIN; - if (poll(&pfd, 1, 0) == 1) { - char buf[1]; - if (read(0, buf, 1) <= 0) { - lwsl_notice("Parent stdin pipe broken. Parent died. Exiting!\n"); - exit(0); + if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME, &ci, 0)) { + lws_strncpy(cr->issuer, ci.ns.name, sizeof(cr->issuer)); + for (int i = 0; cr->issuer[i]; i++) { + if (cr->issuer[i] == '\n' || cr->issuer[i] == '\r') cr->issuer[i] = ' '; + if (cr->issuer[i] == '"') cr->issuer[i] = '\''; + if (cr->issuer[i] == '\\') cr->issuer[i] = '/'; + } + } else { + lws_strncpy(cr->issuer, "Unknown", sizeof(cr->issuer)); } + char json[1024]; + int n = lws_snprintf(json, sizeof(json), "{\"req\":\"cert_status\",\"subdomain\":\"%s\",\"port\":%d,\"status\":\"%s\",\"msg\":\"%s\",\"local_msg\":\"%s\",\"issuer\":\"%s\"}\n", + cr->fqdn, cr->port, cr->status_err ? "error" : "ok", cr->msg, cr->local_msg, cr->issuer); + + struct vhd *target_vhd = global_root_vhd ? global_root_vhd : vhd; + lws_start_foreach_dll(struct lws_dll2 *, p, target_vhd->clients.head) { + struct pss *wpss = lws_container_of(p, struct pss, list); + if (wpss->tx_len + (size_t)n < 65536 - LWS_PRE) { + memcpy(&wpss->tx[LWS_PRE + wpss->tx_len], json, (size_t)n); + wpss->tx_len += (size_t)n; + lws_callback_on_writable(wpss->wsi); + } + } lws_end_foreach_dll(p); + free(cr); } - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, root_monitor_stdin_check_cb, 2 * LWS_US_PER_SEC); } static int @@ -2588,22 +2053,17 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_PROTOCOL_INIT: { struct lws_context *cx = lws_get_context(wsi); + const char *p = lws_cmdline_option_cx(cx, "--lws-dht-dnssec-monitor-root"); - /* Prevent infinite recursion if global options spawn us on ACME vhosts */ - if (!strncmp(lws_get_vhost_name(lws_get_vhost(wsi)), "acme_", 5)) { - lwsl_notice("%s: Skipping root monitor initialization for ACME vhost\n", __func__); + if (!p && !in) return 0; - } - const char *p = lws_cmdline_option_cx(cx, "--lws-dht-dnssec-monitor-root"); + lwsl_vhost_notice(vhost, "dnssec_monitor: PROTOCOL_INIT called!\n"); + /* Root monitor spawned proxy branch */ if (p) { /* Yes, we are the root spawned UDS process! */ - if (global_root_vhd) { - /* Already initialized the root monitor singleton */ - return 0; - } lwsl_notice("%s: Started as UDS root monitor\n", __func__); /* Privileges are seamlessly restricted via native LWS framework policies securely dropping after UDS setup */ @@ -2620,9 +2080,13 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, memset(&info, 0, sizeof(info)); info.vhost_name = "dnssec_monitor_uds"; info.port = 0; /* raw socket UDS */ - info.options = LWS_SERVER_OPTION_UNIX_SOCK | LWS_SERVER_OPTION_ONLY_RAW | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options = LWS_SERVER_OPTION_UNIX_SOCK | LWS_SERVER_OPTION_ONLY_RAW; info.iface = uds_path; + const char *uds_perms = lws_cmdline_option_cx(cx, "--uds-perms"); + if (uds_perms) + info.unix_socket_perms = uds_perms; + /* We only want this protocol to run on the UDS */ static const struct lws_protocols *pprotocols[] = { &lws_dht_dnssec_monitor_protocols[1], @@ -2641,9 +2105,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, lwsl_err("%s: Failed to create UDS vhost on %s\n", __func__, uds_path); return -1; } - lws_init_vhost_client_ssl(&info, vh); lwsl_notice("%s: Created UDS vhost on %s\n", __func__, uds_path); - chmod(uds_path, 0666); } static int timer_armed = 0; @@ -2651,22 +2113,11 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, vhd = lws_protocol_vh_priv_zalloc(vhost, protocol, sizeof(*vhd)); if (vhd) { lwsl_notice("%s: Successfully allocated vhd on %s\n", __func__, lws_get_vhost_name(vhost)); - - struct lws_context_creation_info client_info; - memset(&client_info, 0, sizeof(client_info)); - client_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - lws_init_vhost_client_ssl(&client_info, vhost); - vhd->context = cx; vhd->vhost = vhost; + vhd->root_process_active = 1; - /* Force telemetry to use global public resolver to bypass local split-horizon DNS */ - force_external_dns(cx, "8.8.8.8"); - - const char *base_dir_arg = lws_cmdline_option_cx(cx, "--base-dir"); - if (base_dir_arg) { - vhd->base_dir = strdup(base_dir_arg); - } else { + { lws_system_policy_t *policy; if (lws_system_parse_policy(cx, "/etc/lwsws/policy", &policy)) { lwsl_vhost_notice(vh, "dnssec_monitor: couldn't parse policy."); @@ -2682,24 +2133,17 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, const char *auth_token = lws_cmdline_option_cx(cx, "--auth-token"); char buf[256]; if (!auth_token) { - int n = 0; - int retries = 5; /* 500ms max wait */ - struct pollfd pfd; - pfd.fd = 0; - pfd.events = POLLIN; + int n, retries = 50; while (retries-- > 0) { - if (poll(&pfd, 1, 100) > 0) { - n = (int)read(0, buf, sizeof(buf) - 1); - break; - } + n = (int)read(0, buf, sizeof(buf) - 1); + if (n > 0 || (n < 0 && errno != EAGAIN)) break; + usleep(100000); } if (n > 0) { buf[n] = '\0'; char *p = strchr(buf, '\n'); if (p) *p = '\0'; p = strchr(buf, '\r'); if (p) *p = '\0'; auth_token = buf; - } else { - lwsl_warn("%s: No auth-token provided via command line or stdin! Connections may be rejected.\n", __func__); } } @@ -2723,24 +2167,22 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, vhd->ops = (const struct lws_dht_dnssec_ops *)prot->user; /* Assign functional cross-vhost global routing directly for UDS channels */ - lwsl_notice("%s: Assigned global_root_vhd to UDS vhost (fallback active)\n", __func__); global_root_vhd = vhd; - timer_armed = 1; - - lws_sul_schedule(cx, 0, &vhd->sul_timer, root_monitor_stdin_check_cb, 2 * LWS_US_PER_SEC); if (vhd->ops) { char scan_path[1024]; lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - + /* Guarantee absolute discovery independently of Unix kernel notify boundaries */ + /* Deferred to cleanly drop execution permissions naturally inside the loop */ + lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, dnssec_monitor_timer_cb, 1 * LWS_US_PER_SEC); + timer_armed = 1; #if defined(LWS_WITH_DIR) vhd->dn = lws_dir_notify_create(cx, scan_path, dir_notify_cb, vhd); if (!vhd->dn) lwsl_err("%s: Failed to attach lws_dir_notify to %s\n", __func__, scan_path); #endif - lws_sul_schedule(cx, 0, &vhd->sul_timer_scan, root_dnssec_scan_timer_cb, 5 * LWS_US_PER_SEC); } else { lwsl_err("%s: Skipped scheduling timer on %s because vhd->ops is NULL!\n", __func__, lws_get_vhost_name(vhost)); /* It will organically retry when the next vhost runs PROTOCOL_INIT */ @@ -2757,10 +2199,6 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, if (lws_protocol_vh_priv_get(vhost, protocol)) return 0; - /* Do not spawn root monitor if no pvos restrict it */ - if (!in) - return 0; - vhd = lws_protocol_vh_priv_zalloc(vhost, protocol, sizeof(*vhd)); if (!vhd) return -1; @@ -2775,7 +2213,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, if ((pvo = lws_pvo_search(in, "base-dir"))) vhd->base_dir = strdup(pvo->value); else - vhd->base_dir = strdup("/var/dnssec"); + vhd->base_dir = strdup("/etc/dnssec"); if ((pvo = lws_pvo_search(in, "uds-path"))) vhd->uds_path = pvo->value; @@ -2821,38 +2259,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, vhd->smd_peer = lws_smd_register(vhd->context, vhd, 0, LWSSMDCL_NETWORK, smd_cb_network); - /* Load global ACME config into parent so the UI shows correct status */ - char acme_path[1024]; - lws_snprintf(acme_path, sizeof(acme_path), "%s/acme_config.json", vhd->base_dir); - int afd = open(acme_path, O_RDONLY); - if (afd >= 0) { - char abuf[4096]; - ssize_t an = read(afd, abuf, sizeof(abuf) - 1); - if (an > 0) { - abuf[an] = '\0'; - struct monitor_req_args a; - memset(&a, 0, sizeof(a)); - struct lejp_ctx jctx; - lejp_construct(&jctx, monitor_req_cb, &a, monitor_req_paths, LWS_ARRAY_SIZE(monitor_req_paths)); - lejp_parse(&jctx, (uint8_t *)abuf, (int)an); - lejp_destruct(&jctx); - - vhd->acme_enabled = a.enabled; - vhd->acme_production = a.production; - lws_strncpy(vhd->acme_email, a.email, sizeof(vhd->acme_email)); - lws_strncpy(vhd->acme_organization, a.organization, sizeof(vhd->acme_organization)); - lws_strncpy(vhd->acme_country, a.country, sizeof(vhd->acme_country)); - lws_strncpy(vhd->acme_state, a.state, sizeof(vhd->acme_state)); - lws_strncpy(vhd->acme_locality, a.locality, sizeof(vhd->acme_locality)); - } - close(afd); - } - - /* Force external DNS to bypass potentially broken local resolvers during probes */ - force_external_dns(vhd->context, "8.8.8.8"); - - lwsl_notice("%s: initialized monitor proxy (base-dir: %s, uds-path: %s, acme: %d)\n", - __func__, vhd->base_dir, vhd->uds_path, vhd->acme_enabled); + lwsl_notice("%s: initialized monitor proxy (base-dir: %s, uds-path: %s)\n", __func__, vhd->base_dir, vhd->uds_path); /* Spawn the root monitor process */ struct lws_spawn_piped_info spawn_info; @@ -2862,6 +2269,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, char arg_uds[1024]; char arg_uid[128]; char arg_gid[128]; + char arg_proxy_perms[128]; int n = 0; /* Rely on the original host application executable context path instead of * guessing paths. `argv[0]` guarantees relative/absolute execution fidelity. */ @@ -2901,24 +2309,41 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, exec_array[n++] = debug_lvl; } - char arg_basedir[1024]; - if (vhd->base_dir) { - lws_snprintf(arg_basedir, sizeof(arg_basedir), "--base-dir=%s", vhd->base_dir); - exec_array[n++] = arg_basedir; - } + + /* no --base-dir needed since the root spawnee will look up the policy itself! */ if (vhd->uds_path) { lws_snprintf(arg_uds, sizeof(arg_uds), "--uds-path=%s", vhd->uds_path); exec_array[n++] = arg_uds; } + uid_t target_uid = 0; if (uid) { - lws_snprintf(arg_uid, sizeof(arg_uid), "--uid=%s", uid); + if (!lws_plat_user_to_uid(uid, &target_uid)) + lws_snprintf(arg_uid, sizeof(arg_uid), "--uid=%u", (unsigned int)target_uid); + else + lws_snprintf(arg_uid, sizeof(arg_uid), "--uid=%s", uid); exec_array[n++] = arg_uid; } + + gid_t target_gid = 0; if (gid) { - lws_snprintf(arg_gid, sizeof(arg_gid), "--gid=%s", gid); + if (!lws_plat_group_to_gid(gid, &target_gid)) + lws_snprintf(arg_gid, sizeof(arg_gid), "--gid=%u", (unsigned int)target_gid); + else + lws_snprintf(arg_gid, sizeof(arg_gid), "--gid=%s", gid); exec_array[n++] = arg_gid; } + uid_t eff_uid; + gid_t eff_gid; + lws_get_effective_uid_gid(vhd->context, &eff_uid, &eff_gid); + + if (uid && !lws_plat_user_to_uid(uid, &target_uid)) + lws_snprintf(arg_proxy_perms, sizeof(arg_proxy_perms), "--uds-perms=%u:%u", (unsigned int)target_uid, (unsigned int)eff_gid); + else + lws_snprintf(arg_proxy_perms, sizeof(arg_proxy_perms), "--uds-perms=%s:%u", uid ? uid : "lwsws-priv", (unsigned int)eff_gid); + + exec_array[n++] = arg_proxy_perms; + for (int i = 0; i < n; i++) lwsl_notice("%s: exec_array[%d]: '%s'\n", __func__, i, exec_array[i]); @@ -2936,8 +2361,14 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, vhd->auth_jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = malloc(64); memcpy(vhd->auth_jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, rand, 64); + lws_system_blob_t *b = lws_system_get_blob(vhd->context, LWS_SYSBLOB_TYPE_EXT_AUTH1, 0); + if (b) { + lws_system_blob_direct_set(b, (uint8_t *)vhd->auth_token, strlen(vhd->auth_token)); + } + /* Inject auth token over native stdin pipe instead of argv to prevent ps inspection */ + exec_array[n++] = NULL; spawn_info.exec_array = exec_array; @@ -2967,19 +2398,21 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, global_root_vhd = vhd; lwsl_notice("%s: Spawned root monitor process successfully and assigned global_root_vhd=%p (fallback active)\n", __func__, global_root_vhd); - /* - * Privilege Separation Policy: - * - The "root daemon" drops its privileges to run as the `lwsws-priv` user. - * - Only the `lwsws-priv` daemon can write to the base dir (e.g., /var/dnssec) - * and read secrets like cert keys. - * - The less-privileged network-facing side (here) asks the daemon to handle - * write operations securely. - * - We keep the privileged daemon isolated from external network content. - * Therefore, this unprivileged side leverages a timer to securely scan for - * completed .jws drops and natively handles the DHT network publication. - */ - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 1 * LWS_US_PER_SEC); - lws_sul_schedule(vhd->context, 0, &vhd->sul_timer_proxy_scan, proxy_dnssec_scan_timer_cb, 5 * LWS_US_PER_SEC); + /* Engage parent monitor to execute DHT publications off completed JWS child drops cleanly */ + vhd->proxy_uid = (uid_t)-1; + vhd->proxy_gid = (gid_t)-1; + const char *uid_opt = lws_cmdline_option_cx(vhd->context, "--uid"); + if (uid_opt && lws_plat_user_to_uid(uid_opt, &vhd->proxy_uid)) { + lwsl_err("%s: unknown user %s\n", __func__, uid_opt); + } + const char *gid_opt = lws_cmdline_option_cx(vhd->context, "--gid"); + if (gid_opt && lws_plat_group_to_gid(gid_opt, &vhd->proxy_gid)) { + lwsl_err("%s: unknown group %s\n", __func__, gid_opt); + } + + generate_dist_pki(vhd); + + lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 5 * LWS_US_PER_SEC); } else { /* Already globally spawned! Just map the auth context */ lws_strncpy(vhd->auth_token, global_root_vhd->auth_token, sizeof(vhd->auth_token)); @@ -2988,6 +2421,11 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, vhd->auth_jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = malloc(64); memcpy(vhd->auth_jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, global_root_vhd->auth_jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, 64); + lws_system_blob_t *b = lws_system_get_blob(vhd->context, LWS_SYSBLOB_TYPE_EXT_AUTH1, 0); + if (b) { + lws_system_blob_direct_set(b, (uint8_t *)vhd->auth_token, strlen(vhd->auth_token)); + } + vhd->root_process_active = 1; lwsl_notice("%s: Reusing globally spawned root monitor %p for vhost %s\n", __func__, global_root_vhd, lws_get_vhost_name(vhost)); } @@ -3000,9 +2438,6 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_PROTOCOL_DESTROY: if (!vhd) break; - if (vhd->vhost != lws_get_vhost(wsi)) - break; /* Borrowed global_root_vhd */ - if (vhd->smd_peer) { lws_smd_unregister(vhd->smd_peer); vhd->smd_peer = NULL; @@ -3043,6 +2478,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, if (vhd && vhd->root_process_active) { /* We are the unprivileged proxy, and a UI WebSocket just connected. * Establish onward Raw UDS connection */ + pss->magic = PSS_MAGIC; pss->wsi = wsi; pss->retry_count = 0; lws_dll2_add_tail(&pss->list, &vhd->ui_clients); @@ -3067,34 +2503,13 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_RECEIVE: - if (vhd && vhd->root_process_active) { - if (len < 1024 && strstr((const char *)in, "\"check_cert\"")) { - struct monitor_req_args a; - struct lejp_ctx jctx; - memset(&a, 0, sizeof(a)); - lejp_construct(&jctx, monitor_req_cb, &a, monitor_req_paths, LWS_ARRAY_SIZE(monitor_req_paths)); - lejp_parse(&jctx, (uint8_t *)in, (int)len); - lejp_destruct(&jctx); - - if (!strcmp(a.req, "check_cert")) { - handle_req_check_cert(vhd, pss, &a); - if (a.zone_buf) free(a.zone_buf); - return 0; - } - if (a.zone_buf) free(a.zone_buf); - } - - if (len < 1024 && strstr((const char *)in, "\"get_domains\"")) { - char scan_path[1024]; - lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir); - lws_dir(scan_path, vhd, scan_whois_cb); - } - + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_RECEIVE: Browser UI triggered WS message (len: %d). Proxy cwsi=%p, root_process_active=%d\n", (int)len, pss->cwsi, vhd ? vhd->root_process_active : -1); + if (vhd && vhd->root_process_active && pss->cwsi) { if (len > 65536) { lwsl_err("%s: WS UI request too large\n", __func__); return -1; } - char jwt_buf[1024] = ""; + char jwt_buf[1024]; size_t jwt_len = sizeof(jwt_buf); unsigned long long now = (unsigned long long)lws_now_secs(); char claims[256]; @@ -3103,32 +2518,31 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, lws_snprintf(claims, sizeof(claims), "{\"iss\":\"acme-ipc\",\"aud\":\"dnssec-monitor\",\"iat\":%llu,\"nbf\":%llu,\"exp\":%llu}", now, now - 60, now + 60); - if (vhd->auth_jwk.kty == LWS_GENCRYPTO_KTY_OCT && - !lws_jwt_sign_compact(vhd->context, &vhd->auth_jwk, "HS256", jwt_buf, &jwt_len, temp, sizeof(temp), "%s", claims)) { + if (!lws_jwt_sign_compact(vhd->context, &vhd->auth_jwk, "HS256", jwt_buf, &jwt_len, temp, sizeof(temp), "%s", claims)) { first_brace = memchr(in, '{', len); if (first_brace) { size_t offset = lws_ptr_diff_size_t(first_brace, in) + 1; size_t out_len = 0; - size_t existing_len = pss->tx_len; - if (existing_len + offset < 65536 - LWS_PRE) { - memcpy(&pss->tx[LWS_PRE + existing_len], in, offset); - out_len += offset; - - int n = lws_snprintf((char *)&pss->tx[LWS_PRE + existing_len + out_len], 65536 - LWS_PRE - existing_len - out_len, "\"jwt\":\"%s\",", jwt_buf); - out_len += (size_t)n; - - if (existing_len + out_len + len - offset + 1 < 65536 - LWS_PRE) { - memcpy(&pss->tx[LWS_PRE + existing_len + out_len], first_brace + 1, len - offset); - out_len += len - offset; - pss->tx[LWS_PRE + existing_len + out_len] = '\n'; - out_len += 1; - pss->tx_len += out_len; - if (pss->cwsi) - lws_callback_on_writable(pss->cwsi); /* Write proxy -> root */ - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RECEIVE: Appended proxy->root payload size %d with JWT, total %d\n", (int)out_len, (int)pss->tx_len); - } + + if (existing_len + offset + 512 + (len - offset) + 1 >= 65536 - LWS_PRE) { + lwsl_err("%s: WS UI request dropped, tx buffer full\n", __func__); + return -1; } + + memcpy(&pss->tx[LWS_PRE + existing_len], in, offset); + out_len += offset; + + int n = lws_snprintf((char *)&pss->tx[LWS_PRE + existing_len + out_len], 65536 - LWS_PRE - existing_len - out_len, "\"jwt\":\"%s\",", jwt_buf); + out_len += (size_t)n; + + memcpy(&pss->tx[LWS_PRE + existing_len + out_len], first_brace + 1, len - offset); + out_len += len - offset; + pss->tx[LWS_PRE + existing_len + out_len] = '\n'; + out_len++; + pss->tx_len += out_len; + lws_callback_on_writable(pss->cwsi); /* Write proxy -> root */ + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_RECEIVE: Enqueued proxy->root payload size %d with JWT (total: %d)\n", (int)out_len, (int)pss->tx_len); } else { goto fallback; } @@ -3138,36 +2552,17 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, memcpy(&pss->tx[LWS_PRE + pss->tx_len], in, len); pss->tx_len += len; pss->tx[LWS_PRE + pss->tx_len] = '\n'; - pss->tx_len += 1; - if (pss->cwsi) - lws_callback_on_writable(pss->cwsi); /* Write proxy -> root */ - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RECEIVE: Appended proxy->root payload size %d (no JWT), total %d\n", (int)len + 1, (int)pss->tx_len); + pss->tx_len++; + lws_callback_on_writable(pss->cwsi); /* Write proxy -> root */ + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_RECEIVE: Enqueued proxy->root payload size %d (no JWT)\n", (int)len); } } } else { - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RECEIVE: ABORTED! root_active=%d\n", vhd?vhd->root_process_active:0); + lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RECEIVE: ABORTED! root_active=%d, pss->cwsi=%p\n", vhd?vhd->root_process_active:0, pss->cwsi); } break; case LWS_CALLBACK_SERVER_WRITEABLE: - if (vhd && vhd->completed_checks.head) { - struct lws_dll2 *p = vhd->completed_checks.head; - struct cert_check_result *cr = lws_container_of(p, struct cert_check_result, list); - char *tx = (char *)&pss->tx[LWS_PRE]; - char *tx_end = tx + 65536 - 1; - int n = lws_snprintf(tx, lws_ptr_diff_size_t(tx_end, tx), "{\"req\":\"cert_status\",\"subdomain\":\"%s\",\"port\":%d,\"status\":\"%s\",\"msg\":\"%s\",\"local_msg\":\"%s\",\"issuer\":\"%s\"}\n", - cr->fqdn, cr->port, cr->status_err ? "error" : "ok", cr->msg, cr->local_msg, cr->issuer); - - if (lws_write(wsi, (unsigned char *)tx, (size_t)n, LWS_WRITE_TEXT) < 0) - return -1; - - lws_dll2_remove(&cr->list); - free(cr); - if (vhd->completed_checks.head) - lws_callback_on_writable(wsi); - return 0; - } - if (vhd && vhd->root_process_active) { if (pss->send_ext_ips) { pss->send_ext_ips = 0; @@ -3181,84 +2576,224 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, return 0; } if (pss->rx_len) { - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_SERVER_WRITEABLE: Translating %d bytes to final browser!\n", (int)pss->rx_len); - if (lws_write(wsi, &pss->rx[LWS_PRE], pss->rx_len, LWS_WRITE_TEXT) < 0) { + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_SERVER_WRITEABLE: Translating %d bytes to final browser!\n", (int)pss->rx_len); + int m = lws_write(wsi, &pss->rx[LWS_PRE], pss->rx_len, LWS_WRITE_TEXT); + if (m < 0) { lwsl_err("%s: Failed writing to WS UI\n", __func__); return -1; } - pss->rx_len = 0; + if (m < (int)pss->rx_len) { + memmove(&pss->rx[LWS_PRE], &pss->rx[LWS_PRE + m], pss->rx_len - (size_t)m); + pss->rx_len -= (size_t)m; + lws_callback_on_writable(wsi); + } else { + pss->rx_len = 0; + } } } break; - case LWS_CALLBACK_RAW_CONNECTED: + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { - struct cert_check_info *cci = (struct cert_check_info *)lws_get_opaque_user_data(wsi); - if (cci && cci->magic == CERT_CHECK_MAGIC && vhd) { - lwsl_notice("[INSTRUMENT] Probe %s RAW_CONNECTED successfully! (STARTTLS state: %d)\n", cci->fqdn, cci->starttls_state); - if (cci->starttls_state == 0 || cci->starttls_state == 4) { - extract_and_queue_cert_result(wsi, vhd, cci, protocol); - cci->magic = 0; - lws_dll2_remove(&cci->active_list); - free(cci); - lws_set_opaque_user_data(wsi, NULL); - return -1; // close immediately + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + if (magic && *magic == CERT_CHECK_MAGIC) { + struct cert_check_info *cci = (struct cert_check_info *)magic; + if (vhd) { + struct cert_check_result *cr = malloc(sizeof(*cr)); + if (cr) { + memset(cr, 0, sizeof(*cr)); + lws_strncpy(cr->fqdn, cci->fqdn, sizeof(cr->fqdn)); + char *colon = strchr(cr->fqdn, ':'); + if (colon) *colon = '\0'; + cr->port = cci->port; + char *err_str = in ? (char *)in : "Connection failed"; + lws_snprintf(cr->msg, sizeof(cr->msg), "Error: %s", err_str); + for (int i = 0; cr->msg[i]; i++) { + if (cr->msg[i] == '\n' || cr->msg[i] == '\r') cr->msg[i] = ' '; + if (cr->msg[i] == '"') cr->msg[i] = '\''; + if (cr->msg[i] == '\\') cr->msg[i] = '/'; + } + cr->status_err = 1; + + char json[1024]; + int n = lws_snprintf(json, sizeof(json), "{\"req\":\"cert_status\",\"subdomain\":\"%s\",\"port\":%d,\"status\":\"error\",\"msg\":\"%s\",\"local_msg\":\"%s\",\"issuer\":\"%s\"}\n", + cr->fqdn, cr->port, cr->msg, cr->local_msg, cr->issuer); + + struct vhd *target_vhd = global_root_vhd ? global_root_vhd : vhd; + lws_start_foreach_dll(struct lws_dll2 *, p, target_vhd->clients.head) { + struct pss *wpss = lws_container_of(p, struct pss, list); + if (wpss->tx_len + (size_t)n < 65536 - LWS_PRE) { + memcpy(&wpss->tx[LWS_PRE + wpss->tx_len], json, (size_t)n); + wpss->tx_len += (size_t)n; + lws_callback_on_writable(wpss->wsi); + } + } lws_end_foreach_dll(p); + free(cr); + } } - /* STARTTLS: skip extraction for now, we are cleartext. - * SMTP Banner will arrive in RX. */ + cci->magic = 0; + free(cci); + lws_set_opaque_user_data(wsi, NULL); + } else if (magic && *magic == PSS_MAGIC) { + struct pss *wpss = (struct pss *)magic; + wpss->cwsi = NULL; } } break; - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { - void *opaque = lws_get_opaque_user_data(wsi); - struct cert_check_info *cci = (struct cert_check_info *)opaque; - if (cci && cci->magic == CERT_CHECK_MAGIC && vhd) { - lwsl_notice("[INSTRUMENT] Probe %s CLIENT_CONNECTION_ERROR: %s\n", cci->fqdn, in ? (char *)in : "unknown"); - struct cert_check_result *cr = malloc(sizeof(*cr)); - if (cr) { - memset(cr, 0, sizeof(*cr)); - lws_strncpy(cr->fqdn, cci->fqdn, sizeof(cr->fqdn)); - char *err_str = in ? (char *)in : "Connection failed"; - lws_snprintf(cr->msg, sizeof(cr->msg), "Error: %s", err_str); - cr->status_err = 1; - lws_dll2_add_tail(&cr->list, &vhd->completed_checks); - lws_callback_on_writable_all_protocol(vhd->context, protocol); + struct acme_profiles_fetch_info *afi = (struct acme_profiles_fetch_info *)lws_get_opaque_user_data(wsi); + if (afi && afi->magic == ACME_PROFILES_MAGIC) { + lwsl_notice("%s: Connected to ACME directory\n", __func__); + } + } + break; + + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + { + struct acme_profiles_fetch_info *afi = (struct acme_profiles_fetch_info *)lws_get_opaque_user_data(wsi); + if (afi && afi->magic == ACME_PROFILES_MAGIC) { + char buffer[2048 + LWS_PRE]; + char *px = buffer + LWS_PRE; + int lenx = sizeof(buffer) - LWS_PRE; + + if (lws_http_client_read(wsi, &px, &lenx) < 0) + return -1; + } + } + return 0; + + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: + { + struct acme_profiles_fetch_info *afi = (struct acme_profiles_fetch_info *)lws_get_opaque_user_data(wsi); + if (afi && afi->magic == ACME_PROFILES_MAGIC) { + lwsl_notice("%s: Received %zu bytes for ACME directory\n", __func__, len); + + if (!afi->json) { + afi->json_alloc = 8192; + afi->json = malloc(afi->json_alloc); + } + if (afi->json) { + if (afi->json_len + len >= afi->json_alloc) { + afi->json_alloc *= 2; + char *nb = realloc(afi->json, afi->json_alloc); + if (nb) afi->json = nb; + } + if (afi->json_len + len < afi->json_alloc) { + memcpy(&afi->json[afi->json_len], in, len); + afi->json_len += len; + afi->json[afi->json_len] = '\0'; + } else { + lwsl_err("%s: ACME directory JSON too large!\n", __func__); + } } } } + return 0; + + case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: + { + struct acme_profiles_fetch_info *afi = (struct acme_profiles_fetch_info *)lws_get_opaque_user_data(wsi); + if (afi && afi->magic == ACME_PROFILES_MAGIC) { + lwsl_notice("%s: Completed ACME directory fetch (%zu bytes)\n", __func__, afi->json_len); + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, vhd->ui_clients.head) { + struct pss *wpss = lws_container_of(d, struct pss, list); + size_t existing_len = wpss->tx_len; + if (existing_len + afi->json_len + 128 < 65536 - LWS_PRE) { + int n = lws_snprintf((char *)&wpss->tx[LWS_PRE + existing_len], 65536 - LWS_PRE - existing_len, + "{\"req\":\"get_acme_profiles\",\"status\":\"ok\",\"profiles\":"); + existing_len += (size_t)n; + + char *profiles_start = afi->json ? strstr(afi->json, "\"profiles\"") : NULL; + if (profiles_start) { + profiles_start += 10; + while (*profiles_start && (*profiles_start == ' ' || *profiles_start == ':')) profiles_start++; + char *profiles_end = profiles_start; + int braces = 0; + while (*profiles_end) { + if (*profiles_end == '{') braces++; + else if (*profiles_end == '}') { + braces--; + if (braces == 0) { profiles_end++; break; } + } + profiles_end++; + } + if (braces == 0 && profiles_end > profiles_start) { + size_t plen = lws_ptr_diff_size_t(profiles_end, profiles_start); + memcpy(&wpss->tx[LWS_PRE + existing_len], profiles_start, plen); + existing_len += plen; + } else { + int k = lws_snprintf((char *)&wpss->tx[LWS_PRE + existing_len], 65536 - LWS_PRE - existing_len, "{}"); + existing_len += (size_t)k; + } + } else { + int k = lws_snprintf((char *)&wpss->tx[LWS_PRE + existing_len], 65536 - LWS_PRE - existing_len, "{}"); + existing_len += (size_t)k; + } + + int k = lws_snprintf((char *)&wpss->tx[LWS_PRE + existing_len], 65536 - LWS_PRE - existing_len, "}\n"); + wpss->tx_len = existing_len + (size_t)k; + if (wpss->cwsi) lws_callback_on_writable(wpss->cwsi); + if (wpss->wsi) lws_callback_on_writable(wpss->wsi); + } + } lws_end_foreach_dll_safe(d, d1); + + afi->magic = 0; + if (afi->json) free(afi->json); + free(afi); + lws_set_opaque_user_data(wsi, NULL); + } + } break; - case LWS_CALLBACK_RAW_CLOSE: + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: { - void *opaque = lws_get_opaque_user_data(wsi); - struct cert_check_info *cci = (struct cert_check_info *)opaque; - if (cci && cci->magic == CERT_CHECK_MAGIC) { - cci->magic = 0; - lws_dll2_remove(&cci->active_list); - free(cci); + struct acme_profiles_fetch_info *afi = (struct acme_profiles_fetch_info *)lws_get_opaque_user_data(wsi); + if (afi && afi->magic == ACME_PROFILES_MAGIC) { + lwsl_err("%s: ACME directory HTTP client connection closed before completion\n", __func__); + afi->magic = 0; + if (afi->json) free(afi->json); + free(afi); lws_set_opaque_user_data(wsi, NULL); - } else { - struct pss *wpss = (struct pss *)opaque; - if (wpss) { - wpss->cwsi = NULL; + } + } + break; + + case LWS_CALLBACK_RAW_CONNECTED: + { + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + if (magic && *magic == CERT_CHECK_MAGIC) { + struct cert_check_info *cci = (struct cert_check_info *)magic; + if (vhd) { + lwsl_notice("[INSTRUMENT] Probe %s RAW_CONNECTED successfully!\n", cci->fqdn); + if (cci->starttls_state == 0 || cci->starttls_state == 4) { + extract_and_queue_cert_result(wsi, vhd, cci, protocol); + cci->magic = 0; free(cci); lws_set_opaque_user_data(wsi, NULL); + return -1; + } } - lwsl_notice("%s: UDS connection closed\n", __func__); + /* Drop STARTTLS probe rx */ + return 0; } } break; case LWS_CALLBACK_RAW_ADOPT: { - struct pss *wpss = (struct pss *)lws_get_opaque_user_data(wsi); - if (wpss) { + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + if (magic && *magic == PSS_MAGIC) { + struct pss *wpss = (struct pss *)magic; lwsl_notice("%s: UDS proxy client connection established\n", __func__); wpss->cwsi = wsi; - if (wpss->tx_len) - lws_callback_on_writable(wsi); - } else { + } else if (!magic) { lwsl_notice("%s: UDS connection established to server\n", __func__); + if (vhd && vhd->root_process_active) { + struct pss *root_pss = (struct pss *)user; + root_pss->wsi = wsi; + lws_dll2_add_head(&root_pss->list, &vhd->clients); + } } } break; @@ -3267,106 +2802,90 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, { void *opaque = lws_get_opaque_user_data(wsi); struct cert_check_info *cci = (struct cert_check_info *)opaque; - if (cci && cci->magic == CERT_CHECK_MAGIC) { - lwsl_notice("[INSTRUMENT] Probe %s RAW_RX: '%.*s' (state %d, SSL %d)\n", cci->fqdn, (int)len, (const char *)in, cci->starttls_state, lws_is_ssl(wsi)); - - if (cci->starttls_state == 4) { - /* Handshake might be in progress or done. - * If lws_is_ssl is true, we can try to extract. */ - if (lws_is_ssl(wsi)) { - if (vhd) - extract_and_queue_cert_result(wsi, vhd, cci, protocol); - cci->magic = 0; - free(cci); - lws_set_opaque_user_data(wsi, NULL); - return -1; - } - return 0; + if (cci->starttls_state == 4 && lws_is_ssl(wsi)) { + if (vhd) extract_and_queue_cert_result(wsi, vhd, cci, protocol); + cci->magic = 0; free(cci); lws_set_opaque_user_data(wsi, NULL); return -1; } - if (cci->starttls_state == 1 && !strncmp((const char *)in, "220", 3)) { - cci->starttls_state = 2; - lws_callback_on_writable(wsi); - return 0; + cci->starttls_state = 2; lws_callback_on_writable(wsi); return 0; } if (cci->starttls_state == 2 && !strncmp((const char *)in, "250", 3)) { - /* EHLO can have multiple lines, look for last one. - * For simplicity, we just look for 250 space. */ int found_250_space = 0; for (size_t k = 0; k < len; k++) { - if ((k == 0 || ((const char *)in)[k-1] == '\n') && - len - k >= 4 && !strncmp((const char *)in + k, "250 ", 4)) { - found_250_space = 1; - break; + if ((k == 0 || ((const char *)in)[k-1] == '\n') && len - k >= 4 && !strncmp((const char *)in + k, "250 ", 4)) { + found_250_space = 1; break; } } - if (found_250_space) { - cci->starttls_state = 3; - lws_callback_on_writable(wsi); - } + if (found_250_space) { cci->starttls_state = 3; lws_callback_on_writable(wsi); } return 0; } if (cci->starttls_state == 3 && !strncmp((const char *)in, "220", 3)) { - lwsl_notice("[INSTRUMENT] Probe %s STARTTLS accepted, upgrading to TLS\n", cci->fqdn); cci->starttls_state = 4; - if (lws_tls_client_upgrade(wsi, LCCSCF_USE_SSL | - LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | - LCCSCF_ALLOW_SELFSIGNED | - LCCSCF_ALLOW_EXPIRED) < 0) { - lwsl_notice("[INSTRUMENT] Probe %s TLS upgrade failed\n", cci->fqdn); - return -1; - } - lws_callback_on_writable(wsi); - return 0; + if (lws_tls_client_upgrade(wsi, LCCSCF_USE_SSL | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_ALLOW_EXPIRED) < 0) return -1; + lws_callback_on_writable(wsi); return 0; } return 0; } + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_RAW_RX: UDS channel receiving %d bytes. Is Proxy? %d\n", (int)len, magic && *magic == PSS_MAGIC); - struct pss *wpss = (struct pss *)opaque; - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RAW_RX: UDS channel receiving %d bytes. Is Proxy? %d\n", (int)len, wpss != NULL); - - if (wpss) { + if (magic && *magic == CERT_CHECK_MAGIC) { + /* Drop STARTTLS probe rx */ + return 0; + } else if (magic && *magic == PSS_MAGIC) { + struct pss *wpss = (struct pss *)magic; /* 1: Proxy Unprivileged Client: root server just replied. */ - if (len > 65536) return -1; - memcpy(&wpss->rx[LWS_PRE], in, len); - wpss->rx_len = len; + if (wpss->rx_len + len > 65536 - LWS_PRE) return -1; + memcpy(&wpss->rx[LWS_PRE + wpss->rx_len], in, len); + wpss->rx_len += len; lws_callback_on_writable(wpss->wsi); /* trigger WS write */ - lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RAW_RX (PROXY): Saved response length %d and queued browser wsi ptr %p for writing\n", (int)len, wpss->wsi); + lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RAW_RX (PROXY): Appended response length %d and queued browser wsi ptr %p for writing\n", (int)len, wpss->wsi); } else { - /* 2: Root Server: UI proxy just gave us a request. */ - if (len > 65536 - 1) return -1; - - lwsl_notice("[ROOT-DAEMON] [INSTRUMENT] LWS_CALLBACK_RAW_RX: UDS Channel rx %d bytes\n", (int)len); + struct lws_vhost *vh = lws_get_vhost(wsi); + if (!vh || strcmp(lws_get_vhost_name(vh), "dnssec_monitor_uds") != 0) { + /* Drop anything not explicitly accepted on the UDS channel (e.g. child stream WSIs from probes) */ + return 0; + } - memcpy(&vhd->rx[LWS_PRE], in, len); - vhd->rx[LWS_PRE + len] = '\0'; - vhd->rx_len = len; + /* 2: Root Server: UI proxy just gave us a request. */ + if (vhd->rx_len + len > 65536 - 1) return -1; + memcpy(&vhd->rx[LWS_PRE + vhd->rx_len], in, len); + vhd->rx_len += len; + vhd->rx[LWS_PRE + vhd->rx_len] = '\0'; struct pss *root_pss = (struct pss *)user; - root_pss->tx_len = 0; // Prevent synchronous overwrite buildup mapping - - char *current = (char *)&vhd->rx[LWS_PRE]; - char *end = current + len; - - while (current < end) { - char *nl = strchr(current, '\n'); - if (!nl) nl = end; - - size_t chunk_len = lws_ptr_diff_size_t(nl, current); - if (chunk_len > 0) { - char save = *nl; + /* root_pss->tx_len = 0; REMOVED to prevent overwriting batched responses */ + + lwsl_debug("[INSTRUMENT] LWS_CALLBACK_RAW_RX (ROOT): Processing %d bytes buffer\n", (int)vhd->rx_len); + + char *p = (char *)&vhd->rx[LWS_PRE]; + char *start = p; + while (p && *p) { + char *nl = strchr(p, '\n'); + if (nl) { *nl = '\0'; - lwsl_notice("[ROOT-DAEMON] [INSTRUMENT] LWS_CALLBACK_RAW_RX (ROOT): Sending %d bytes to monitor request router\n", (int)chunk_len); - handle_monitor_request(vhd, root_pss, current, chunk_len); - if (save != '\0') *nl = save; + if (*p) { + handle_monitor_request(vhd, root_pss, p, strlen(p)); + } + p = nl + 1; + start = p; + } else { + break; + } + } + + if (start > (char *)&vhd->rx[LWS_PRE]) { + size_t unparsed = lws_ptr_diff_size_t((char *)&vhd->rx[LWS_PRE + vhd->rx_len], start); + if (unparsed > 0) { + memmove(&vhd->rx[LWS_PRE], start, unparsed); } - current = nl + 1; + vhd->rx_len = unparsed; + vhd->rx[LWS_PRE + vhd->rx_len] = '\0'; } - /* Tell server socket to reply */ if (root_pss->tx_len) { - lwsl_notice("[ROOT-DAEMON] [INSTRUMENT] LWS_CALLBACK_RAW_RX (ROOT): Triggering WS write\n"); + /* Tell server socket to reply */ lws_callback_on_writable(wsi); } } @@ -3377,55 +2896,78 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason, { void *opaque = lws_get_opaque_user_data(wsi); struct cert_check_info *cci = (struct cert_check_info *)opaque; - if (cci && cci->magic == CERT_CHECK_MAGIC) { if (cci->starttls_state == 4) { - lwsl_notice("[INSTRUMENT] Probe %s STARTTLS handshake finished, extracting cert\n", cci->fqdn); - if (vhd) - extract_and_queue_cert_result(wsi, vhd, cci, protocol); - cci->magic = 0; - free(cci); - lws_set_opaque_user_data(wsi, NULL); - return -1; - } - char buf[256]; - int n = 0; - if (cci->starttls_state == 2) { - n = lws_snprintf(buf, sizeof(buf), "EHLO %s\r\n", cci->fqdn); - } else if (cci->starttls_state == 3) { - n = lws_snprintf(buf, sizeof(buf), "STARTTLS\r\n"); - } - if (n > 0) { - lwsl_notice("[INSTRUMENT] Probe %s sending: %.*s", cci->fqdn, n, buf); - if (lws_write(wsi, (unsigned char *)buf, (size_t)n, LWS_WRITE_RAW) < 0) return -1; + if (vhd) extract_and_queue_cert_result(wsi, vhd, cci, protocol); + cci->magic = 0; free(cci); lws_set_opaque_user_data(wsi, NULL); return -1; } + char buf[256]; int n = 0; + if (cci->starttls_state == 2) n = lws_snprintf(buf, sizeof(buf), "EHLO %s\r\n", cci->fqdn); + else if (cci->starttls_state == 3) n = lws_snprintf(buf, sizeof(buf), "STARTTLS\r\n"); + if (n > 0 && lws_write(wsi, (unsigned char *)buf, (size_t)n, LWS_WRITE_RAW) < 0) return -1; return 0; } - - struct pss *wpss = (struct pss *)opaque; - - if (wpss) { - /* 1: Proxy Client sending request -> Root Server */ + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + if (magic && *magic == PSS_MAGIC) { + struct pss *wpss = (struct pss *)magic; + /* 1: Proxy Unprivileged Client: write queued UI data to Root Server */ if (wpss->tx_len) { lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RAW_WRITEABLE (PROXY): Driving %d bytes out over UDS IPC into Daemon\n", (int)wpss->tx_len); - if (lws_write(wsi, &wpss->tx[LWS_PRE], wpss->tx_len, LWS_WRITE_RAW) < 0) return -1; - wpss->tx_len = 0; + int m = lws_write(wsi, &wpss->tx[LWS_PRE], wpss->tx_len, LWS_WRITE_RAW); + if (m < 0) return -1; + if (m < (int)wpss->tx_len) { + memmove(&wpss->tx[LWS_PRE], &wpss->tx[LWS_PRE + m], wpss->tx_len - (size_t)m); + wpss->tx_len -= (size_t)m; + lws_callback_on_writable(wsi); + } else { + wpss->tx_len = 0; + } } } else { /* 2: Root Server sending response -> Proxy Client */ struct pss *root_pss = (struct pss *)user; + if (root_pss && root_pss->tx_len) { lwsl_notice("[INSTRUMENT] LWS_CALLBACK_RAW_WRITEABLE (ROOT): Dispatching %d byte JSON response natively to Proxy UDS caller\n", (int)root_pss->tx_len); - if (lws_write(wsi, &root_pss->tx[LWS_PRE], root_pss->tx_len, LWS_WRITE_RAW) < 0) return -1; - root_pss->tx_len = 0; + int m = lws_write(wsi, &root_pss->tx[LWS_PRE], root_pss->tx_len, LWS_WRITE_RAW); + if (m < 0) return -1; + if (m < (int)root_pss->tx_len) { + memmove(&root_pss->tx[LWS_PRE], &root_pss->tx[LWS_PRE + m], root_pss->tx_len - (size_t)m); + root_pss->tx_len -= (size_t)m; + lws_callback_on_writable(wsi); + } else { + root_pss->tx_len = 0; + } + } + } + } + break; + + case LWS_CALLBACK_RAW_CLOSE: + { + uint32_t *magic = (uint32_t *)lws_get_opaque_user_data(wsi); + if (magic && *magic == CERT_CHECK_MAGIC) { + struct cert_check_info *cci = (struct cert_check_info *)magic; + cci->magic = 0; + free(cci); + lws_set_opaque_user_data(wsi, NULL); + } else if (magic && *magic == PSS_MAGIC) { + struct pss *wpss = (struct pss *)magic; + wpss->cwsi = NULL; + } else if (!magic) { + if (vhd && vhd->root_process_active) { + struct pss *root_pss = (struct pss *)user; + lws_dll2_remove(&root_pss->list); } } + lwsl_notice("%s: UDS connection closed\n", __func__); } break; default: break; } + return 0; } @@ -3435,10 +2977,6 @@ callback_monitor_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, { uint8_t buf[2048]; int ilen; - struct lws_vhost *vhost = lws_get_vhost(wsi); - const struct lws_protocols *protocol = lws_get_protocol(wsi); - struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get(vhost, protocol); - if (!vhd && global_root_vhd) vhd = global_root_vhd; switch (reason) { case LWS_CALLBACK_RAW_CLOSE_FILE: @@ -3457,7 +2995,7 @@ callback_monitor_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, while (b && *b) { char *nl = strchr(b, '\n'); if (nl) *nl++ = '\0'; - lwsl_notice("[ROOT-DAEMON] %s\n", b); + lwsl_notice("[PRIV-DAEMON] %s\n", b); b = nl; } return 0; diff --git a/plugins/protocol_lws_dht_object_store/protocol_lws_dht_object_store.c b/plugins/protocol_lws_dht_object_store/protocol_lws_dht_object_store.c index fb9b9c7c30..db300af407 100644 --- a/plugins/protocol_lws_dht_object_store/protocol_lws_dht_object_store.c +++ b/plugins/protocol_lws_dht_object_store/protocol_lws_dht_object_store.c @@ -235,7 +235,7 @@ verb_put_handler(struct lws_dht_ctx *ctx, struct vhd_dht_store *vhd, const struc if (!lws_hex_to_byte_array(frag->safe_hash, raw_hash, sizeof(raw_hash))) { lws_dht_hash_t *id = lws_dht_hash_create(LWS_DHT_HASH_TYPE_SHA1, 20, raw_hash); if (id) { - lws_dht_notify_subscribers(ctx, id, hash); + lws_dht_notify_subscribers(ctx, id, hash, NULL, 0); lws_dht_hash_destroy(&id); } } @@ -272,7 +272,7 @@ verb_get_handler(struct lws_dht_ctx *ctx, struct vhd_dht_store *vhd, const struc size_t blen = 1024 + 1024; int hlen; - lwsl_user("%s: GET %s offset %llu len %llu\n", __func__, msg->hash, msg->offset, msg->len); + // lwsl_user("%s: GET %s offset %llu len %llu\n", __func__, msg->hash, msg->offset, msg->len); lws_snprintf(path, sizeof(path), "%s/%s", vhd->storage_path, msg->hash); fd = open(path, O_RDONLY); @@ -606,6 +606,8 @@ callback_dht_object_store(struct lws* wsi, enum lws_callback_reasons reason, } case LWS_CALLBACK_PROTOCOL_INIT: { + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; const char *store_verbs[] = { "PUT", "GET", diff --git a/plugins/protocol_lws_dht_stats/protocol_lws_dht_stats.c b/plugins/protocol_lws_dht_stats/protocol_lws_dht_stats.c index 30fa5a6e51..2dbe7d9ebb 100644 --- a/plugins/protocol_lws_dht_stats/protocol_lws_dht_stats.c +++ b/plugins/protocol_lws_dht_stats/protocol_lws_dht_stats.c @@ -63,6 +63,8 @@ callback_lws_dht_stats(struct lws *wsi, enum lws_callback_reasons reason, void * switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_dht_store/protocol_lws_dht_store.c b/plugins/protocol_lws_dht_store/protocol_lws_dht_store.c index 5d0dd7c5ca..287e7f2f28 100644 --- a/plugins/protocol_lws_dht_store/protocol_lws_dht_store.c +++ b/plugins/protocol_lws_dht_store/protocol_lws_dht_store.c @@ -84,6 +84,8 @@ callback_lws_dht_store(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_extip/protocol_lws_extip.c b/plugins/protocol_lws_extip/protocol_lws_extip.c index 011bcced78..7033c882d8 100644 --- a/plugins/protocol_lws_extip/protocol_lws_extip.c +++ b/plugins/protocol_lws_extip/protocol_lws_extip.c @@ -147,6 +147,7 @@ extip_report_ip_offline(struct vhd_extip *vhd, int i) else zero.sa6.sin6_family = AF_INET6; + lwsl_notice("EXTIP_DEBUG: extip_report_ip_offline for IPv%c\n", i ? '6' : '4'); lws_extip_report(vhd->context, LWS_EXTIP_SRC_EXTIP, &zero, !i ? AF_INET : AF_INET6, 2, NULL, 0); } @@ -231,13 +232,13 @@ callback_extip(struct lws *wsi, enum lws_callback_reasons reason, void *user, vo switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; { const struct lws_protocol_vhost_options *pvo = (const struct lws_protocol_vhost_options *)in; if (!pvo) - return -1; - - lwsl_vhost_notice(lws_get_vhost(wsi), "%s: LWS_CALLBACK_PROTOCOL_INIT starting\n", __func__); + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd_extip)); if (!vhd) @@ -363,6 +364,7 @@ callback_extip(struct lws *wsi, enum lws_callback_reasons reason, void *user, vo buf[len] = '\0'; memset(&sa46, 0, sizeof(sa46)); lws_sa46_parse_numeric_address(buf + 1 + LENGTH_EXTIP_COOKIE, &sa46); + lwsl_notice("EXTIP_DEBUG: Client reporting online IP to lws_extip_report\n"); lws_extip_report(vhd->context, LWS_EXTIP_SRC_EXTIP, &sa46, sa46.sa4.sin_family == AF_INET ? AF_INET : AF_INET6, 1, NULL, 0); } diff --git a/plugins/protocol_lws_latency/protocol_lws_latency.c b/plugins/protocol_lws_latency/protocol_lws_latency.c index 1c7671292c..422ef03d06 100644 --- a/plugins/protocol_lws_latency/protocol_lws_latency.c +++ b/plugins/protocol_lws_latency/protocol_lws_latency.c @@ -54,6 +54,8 @@ callback_latency(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__latency)); diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 1c2e0ec556..88b5b96a1e 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -442,6 +442,8 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, switch ((int)reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), diff --git a/plugins/protocol_lws_mirror/protocol_lws_mirror.c b/plugins/protocol_lws_mirror/protocol_lws_mirror.c index 38b1e709ff..d86f596ceb 100644 --- a/plugins/protocol_lws_mirror/protocol_lws_mirror.c +++ b/plugins/protocol_lws_mirror/protocol_lws_mirror.c @@ -345,6 +345,8 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason, return 1; /* disallow compression */ case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */ + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!v) { lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), diff --git a/plugins/protocol_lws_oauth2_client/protocol_lws_oauth2_client.c b/plugins/protocol_lws_oauth2_client/protocol_lws_oauth2_client.c index 1f7eca875f..1a43e575bf 100644 --- a/plugins/protocol_lws_oauth2_client/protocol_lws_oauth2_client.c +++ b/plugins/protocol_lws_oauth2_client/protocol_lws_oauth2_client.c @@ -109,6 +109,8 @@ callback_lws_oauth2_client(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_oauth_preauth/README.md b/plugins/protocol_lws_oauth_preauth/README.md new file mode 100644 index 0000000000..bfce7ffbda --- /dev/null +++ b/plugins/protocol_lws_oauth_preauth/README.md @@ -0,0 +1,24 @@ +# lws-oauth-preauth + +This plugin provides a WebSocket-based waiting room for devices that have not yet been paired/authorized via RFC 8628 (OAuth 2.0 Device Authorization Grant). It acts as an intermediary signaling channel where pre-authenticated admin clients can securely identify and interact with unauthenticated devices before full pairing. + +## Features + +- Maintains a list of connected unauthenticated "devices" and authorized "listeners" (admins). +- Validates authorized admin listeners via JWT (JSON Web Tokens) provided via an HTTP cookie. +- Allows admins to discover unauthenticated devices currently waiting for pairing. +- Facilitates sending "identify" commands to specific devices, often used to trigger a physical indication (like a blinking LED) so the admin can verify physical possession of the device before authorizing it. + +## Configuration PVOs (Per-VHost Options) + +| Name | Meaning | Default | +|---|---|---| +| `cookie-name` | The name of the HTTP cookie that carries the JWT for admin validation. | `auth_session` | +| `jwt-jwk` | The JSON Web Key (JWK) string used to verify the JWT signature. Can be the literal JSON or a path to a file depending on LWS configuration. | N/A | + +## Operation + +- **Devices**: Devices connect to this protocol over WebSocket and send JSON payloads containing their identifying information (`name`, `serial`, `user_code`). +- **Listeners (Admins)**: Admins connect to the same protocol, providing their JWT cookie. If validation succeeds, they are registered as a listener. +- **Broadcast**: When a device connects or disconnects, the plugin broadcasts its state to all connected listeners. +- **Identification**: A listener can send `{"cmd": "identify", "serial": ""}`. The plugin will route this `identify` command down to the target device, allowing it to perform a physical identification action (e.g., blink an LED). diff --git a/plugins/protocol_lws_oauth_preauth/protocol_lws_oauth_preauth.c b/plugins/protocol_lws_oauth_preauth/protocol_lws_oauth_preauth.c index 0010236f2c..949c3c59ed 100644 --- a/plugins/protocol_lws_oauth_preauth/protocol_lws_oauth_preauth.c +++ b/plugins/protocol_lws_oauth_preauth/protocol_lws_oauth_preauth.c @@ -80,6 +80,8 @@ callback_lws_oauth_preauth(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd_oauth_preauth)); if (!vhd) diff --git a/plugins/protocol_lws_openmetrics_export/protocol_lws_openmetrics_export.c b/plugins/protocol_lws_openmetrics_export/protocol_lws_openmetrics_export.c index b03c903678..1f32cee13a 100644 --- a/plugins/protocol_lws_openmetrics_export/protocol_lws_openmetrics_export.c +++ b/plugins/protocol_lws_openmetrics_export/protocol_lws_openmetrics_export.c @@ -588,6 +588,8 @@ callback_lws_openmetrics_prox_agg(struct lws *wsi, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; @@ -766,6 +768,8 @@ callback_lws_openmetrics_prox_server(struct lws *wsi, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; /* * We get told what to do when we are bound to the vhost */ @@ -968,6 +972,8 @@ callback_lws_openmetrics_prox_client(struct lws *wsi, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_raw_proxy/protocol_lws_raw_proxy.c b/plugins/protocol_lws_raw_proxy/protocol_lws_raw_proxy.c index 6ac3fdc931..8e32d064de 100644 --- a/plugins/protocol_lws_raw_proxy/protocol_lws_raw_proxy.c +++ b/plugins/protocol_lws_raw_proxy/protocol_lws_raw_proxy.c @@ -189,6 +189,8 @@ callback_raw_proxy(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_raw_test/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test/protocol_lws_raw_test.c index 1b981c2efe..f5452a22a3 100644 --- a/plugins/protocol_lws_raw_test/protocol_lws_raw_test.c +++ b/plugins/protocol_lws_raw_test/protocol_lws_raw_test.c @@ -111,6 +111,8 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, void *user, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_rtc_camera/protocol_lws_rtc_camera.c b/plugins/protocol_lws_rtc_camera/protocol_lws_rtc_camera.c index a9b4af4ebd..a5acefc4d0 100644 --- a/plugins/protocol_lws_rtc_camera/protocol_lws_rtc_camera.c +++ b/plugins/protocol_lws_rtc_camera/protocol_lws_rtc_camera.c @@ -407,11 +407,14 @@ callback_rtc_camera(struct lws *wsi, enum lws_callback_reasons reason, } case LWS_CALLBACK_PROTOCOL_INIT: { + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; + if (!in) + return 0; + const struct lws_protocol_vhost_options *pvo; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data)); - - lwsl_err("LWS_CALLBACK_PROTOCOL_INIT for lws-rtc-camera\n"); if (!vhd) return -1; vhd->vhd = (struct vhd_webrtc *)lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_vhost_name_to_protocol(lws_get_vhost(wsi), "lws-webrtc")); diff --git a/plugins/protocol_lws_smtp_client/README.md b/plugins/protocol_lws_smtp_client/README.md new file mode 100644 index 0000000000..e7f5e84d55 --- /dev/null +++ b/plugins/protocol_lws_smtp_client/README.md @@ -0,0 +1,30 @@ +# lws-smtp-client + +This is a protocol plugin providing a simple SMTP client API. It allows other components and plugins to asynchronously queue and send emails via a local or remote SMTP server. + +## Features + +- Non-blocking, event-loop integrated SMTP client. +- Provides a C API (`lws_smtp_client_ops_t`) exposed via the protocol's user pointer, allowing other plugins/C code to trigger emails dynamically. +- Implements a basic SMTP state machine (Connecting, Greeting, Helo, Mail From, Rcpt To, Data, Body, Quit). + +## Usage via C API + +You can lookup the protocol and access its operations to send emails from within libwebsockets: + +```c +const struct lws_protocols *pp = lws_vhost_name_to_protocol(vh, "lws-smtp-client"); +if (pp) { + lws_smtp_client_ops_t *ops = (lws_smtp_client_ops_t *)pp->user; + + lws_smtp_email_t email; + email.from = "sender@example.com"; + email.to = "receiver@example.com"; + email.subject = "Test Email"; + email.body = "This is a test email sent via lws-smtp-client."; + + ops->send_email(cx, vh, &email); +} +``` + +The plugin internally queues the email and asynchronously negotiates the SMTP transaction. By default, it connects to an SMTP server at `127.0.0.1:25`. diff --git a/plugins/protocol_lws_smtp_client/protocol_lws_smtp_client.c b/plugins/protocol_lws_smtp_client/protocol_lws_smtp_client.c index acc78e4e72..f0a484d060 100644 --- a/plugins/protocol_lws_smtp_client/protocol_lws_smtp_client.c +++ b/plugins/protocol_lws_smtp_client/protocol_lws_smtp_client.c @@ -146,6 +146,8 @@ callback_smtp_client(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__smtp_client)); diff --git a/plugins/protocol_lws_sshd_demo/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo/protocol_lws_sshd_demo.c index baf57b606f..ccb03269a4 100644 --- a/plugins/protocol_lws_sshd_demo/protocol_lws_sshd_demo.c +++ b/plugins/protocol_lws_sshd_demo/protocol_lws_sshd_demo.c @@ -393,6 +393,8 @@ callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), diff --git a/plugins/protocol_lws_status/protocol_lws_status.c b/plugins/protocol_lws_status/protocol_lws_status.c index 9f83ad02bb..bb487d59c4 100644 --- a/plugins/protocol_lws_status/protocol_lws_status.c +++ b/plugins/protocol_lws_status/protocol_lws_status.c @@ -99,6 +99,8 @@ callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; if (!in) return 0; diff --git a/plugins/protocol_lws_webrtc/README.md b/plugins/protocol_lws_webrtc/README.md new file mode 100644 index 0000000000..bf7aedad86 --- /dev/null +++ b/plugins/protocol_lws_webrtc/README.md @@ -0,0 +1,24 @@ +# lws-webrtc + +This plugin implements a shared WebRTC signaling and media transport layer within libwebsockets. It handles the WebSocket signaling, DTLS (Datagram Transport Layer Security) negotiation, SRTP (Secure Real-time Transport Protocol) keying, and RTP packetization for both audio and video streams. + +## Features + +- **Signaling**: Parses and generates SDP (Session Description Protocol) offer/answer exchanges over a secure WebSocket connection. +- **ICE Handling**: Processes STUN (Session Traversal Utilities for NAT) binding requests to establish connectivity without full ICE-agent complexity (acts as an ICE-lite server). +- **Security**: Sets up DTLS handshakes and extracts SRTP keys to encrypt outgoing RTP media flows. +- **Media Delivery**: Provides a C API (`lws_webrtc_send_video`, `lws_webrtc_send_audio`) to inject raw H.264/AV1 NAL units and Opus frames into the WebRTC session. +- **Packetization**: Automatically fragments H.264/AV1 bitstreams and wraps them in RTP packets suitable for browsers. +- **Feedback**: Receives and processes RTCP (RTP Control Protocol) feedback, particularly NACKs and PLIs (Picture Loss Indications), to maintain stream health. + +## Usage + +This protocol is usually utilized as an underlying infrastructure plugin for higher-level media handlers (like `protocol_lws_rtc_camera` or `protocol_lws_webrtc_mixer`), which feed the raw media frames into `lws_webrtc`. + +It provides operations via `lws_vhost_name_to_protocol(vh, "lws-webrtc")->user`, exposing functions like: +- `lws_webrtc_send_video()` +- `lws_webrtc_send_audio()` +- `lws_webrtc_send_text()` +- Session iteration and telemetry functions. + +When a client connects to the `lws-webrtc` WebSocket endpoint, the server responds with an SDP offer containing the supported codecs (H.264, AV1, Opus) and negotiates the WebRTC UDP data path transparently. diff --git a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c index 005cac87b1..c2d8d65bef 100644 --- a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c +++ b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c @@ -607,16 +607,16 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) char audio_m[2048], video_m[2048], candidates[1024] = ""; size_t n_sdp; - pss->is_client = 1; + pss->is_client = 0; if (!vhd) return -1; - /* Initialize Client DTLS */ + /* Initialize Server DTLS */ if (!pss->handshake_started) { struct lws_gendtls_creation_info ci; memset(&ci, 0, sizeof(ci)); ci.context = vhd->context; - ci.mode = LWS_GENDTLS_MODE_CLIENT; + ci.mode = LWS_GENDTLS_MODE_SERVER; ci.mtu = 1100; ci.use_srtp = "SRTP_AES128_CM_SHA1_80"; if (lws_gendtls_create(&pss->dtls_ctx, &ci)) return -1; @@ -657,7 +657,7 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) "a=ice-ufrag:%s\\r\\n" "a=ice-pwd:%s\\r\\n" "a=fingerprint:sha-256 %s\\r\\n" - "a=setup:actpass\\r\\n" + "a=setup:passive\\r\\n" "a=mid:1\\r\\n" "a=sendonly\\r\\n" "a=msid:lws-stream lws-track-video\\r\\n" @@ -689,7 +689,7 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) "a=ice-ufrag:%s\\r\\n" "a=ice-pwd:%s\\r\\n" "a=fingerprint:sha-256 %s\\r\\n" - "a=setup:actpass\\r\\n" + "a=setup:passive\\r\\n" "a=mid:0\\r\\n" "a=sendonly\\r\\n" "a=msid:lws-stream lws-track-audio\\r\\n" @@ -723,7 +723,7 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) return 0; } -/* STUN Binding Request generator */ +/* STUN Binding Request generator for hole punching */ static int lws_webrtc_stun_req_pack(struct pss_webrtc *pss, uint8_t *buf, size_t len, uint8_t *tid) { @@ -761,13 +761,13 @@ lws_webrtc_stun_req_pack(struct pss_webrtc *pss, uint8_t *buf, size_t len, uint8 lws_ser_wu32be(p + 4, 1845494271); /* Type preference 110, Local pref 65535, Component 255 */ p += 8; - /* 3. ICE-CONTROLLING (0x802A) */ - lws_ser_wu16be(p, 0x802A); - lws_ser_wu16be(p + 2, 8); - lws_get_random(lws_get_context(pss->wsi_ws), p + 4, 8); - p += 12; + /* + * CRITICAL: We MUST NOT include ICE-CONTROLLING (0x802A) here! + * Since our server is ice-lite, sending ICE-CONTROLLING is a fatal protocol + * violation and modern Chrome/Firefox will aggressively abort the ICE connection. + */ - /* 4. MESSAGE-INTEGRITY (0x0008) */ + /* 3. MESSAGE-INTEGRITY (0x0008) */ /* Should use remote password */ if (pss->ice_pwd_remote[0]) { uint16_t msg_len = (uint16_t)(p - start - 20 + 24); /* Current len + attribute header + HMAC (20) */ @@ -786,7 +786,7 @@ lws_webrtc_stun_req_pack(struct pss_webrtc *pss, uint8_t *buf, size_t len, uint8 p += 24; } - /* 5. FINGERPRINT (0x8028) */ + /* 4. FINGERPRINT (0x8028) */ { uint16_t msg_len = (uint16_t)(p - start - 20 + 8); /* Current len + attribute header + CRC (4) */ lws_ser_wu16be(start + 2, msg_len); @@ -807,6 +807,8 @@ lws_webrtc_stun_req_pack(struct pss_webrtc *pss, uint8_t *buf, size_t len, uint8 } + + static int handle_candidate(struct pss_webrtc *pss, struct vhd_webrtc *vhd, const char *cand) { @@ -856,7 +858,7 @@ handle_candidate(struct pss_webrtc *pss, struct vhd_webrtc *vhd, const char *can sa46_sockport(&pss->media->peer_sa46, htons((uint16_t)port)); pss->media->has_peer_sa46 = 1; - /* Send STUN Binding Request to punch hole */ + /* Send STUN Binding Request to punch hole (spec compliant) */ uint8_t stun[2048]; uint8_t tid[12]; @@ -880,22 +882,6 @@ handle_candidate(struct pss_webrtc *pss, struct vhd_webrtc *vhd, const char *can webrtc_pss_err(pss, "STUN req pack failed: %d\n", n); } - /* Trigger DTLS Client Hello if we were waiting for peer sin */ - if (pss->is_client && pss->handshake_started && !pss->media->handshake_done) { - uint8_t dummy; - lws_gendtls_get_rx(&pss->dtls_ctx, &dummy, 1); - uint8_t out[2048]; - int _tx_len; - while ((_tx_len = lws_gendtls_get_tx(&pss->dtls_ctx, out, sizeof(out))) > 0) { - int fd = (int)(lws_intptr_t)lws_get_socket_fd(pss->media->wsi_udp); - if (fd >= 0) { - lwsl_notice("%s: Sending Initial DTLS ClientHello after ICE (%d bytes)\n", __func__, _tx_len); - if (sendto((lws_sockfd_type)(lws_intptr_t)fd, (const char *)out, (size_t)_tx_len, 0, (const struct sockaddr *)&pss->media->peer_sa46, pss->media->peer_sa46.sa4.sin_family == AF_INET6 ? (socklen_t)sizeof(pss->media->peer_sa46.sa6) : (socklen_t)sizeof(pss->media->peer_sa46.sa4)) < 0) { - webrtc_pss_err(pss, "DTLS ClientHello sendto failed: errno %d\n", errno); - } - } - } - } return 0; } @@ -1179,24 +1165,6 @@ handle_answer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, c free(sdp_clean); - /* Trigger DTLS Client Hello */ - lwsl_notice("%s: Checking DTLS Cond: started %d, done %d, peer %d\n", __func__, pss->handshake_started, pss->media ? pss->media->handshake_done : 0, pss->media ? pss->media->has_peer_sa46 : 0); - if (pss->media && pss->media->wsi_udp && pss->handshake_started && !pss->media->handshake_done && pss->media->has_peer_sa46) { - int fd = (int)(lws_intptr_t)lws_get_socket_fd(pss->media->wsi_udp); - if (fd >= 0) { - uint8_t dummy; - lws_gendtls_get_rx(&pss->dtls_ctx, &dummy, 1); - uint8_t out[2048]; - int _tx_len; - while ((_tx_len = lws_gendtls_get_tx(&pss->dtls_ctx, out, sizeof(out))) > 0) { - webrtc_pss_log(pss, "Sending Initial DTLS ClientHello (%d bytes)\n", _tx_len); - if (sendto((lws_sockfd_type)(lws_intptr_t)fd, (const char *)out, (size_t)_tx_len, 0, (const struct sockaddr *)&pss->media->peer_sa46, pss->media->peer_sa46.sa4.sin_family == AF_INET6 ? (socklen_t)sizeof(pss->media->peer_sa46.sa6) : (socklen_t)sizeof(pss->media->peer_sa46.sa4)) < 0) { - webrtc_pss_err(pss, "Failed to send Initial DTLS ClientHello: errno %d\n", errno); - } - } - } - } - return 0; } @@ -1489,7 +1457,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co struct lws_gendtls_creation_info ci; memset(&ci, 0, sizeof(ci)); ci.context = vhd->context; - ci.mode = LWS_GENDTLS_MODE_SERVER; + ci.mode = pss->is_client ? LWS_GENDTLS_MODE_CLIENT : LWS_GENDTLS_MODE_SERVER; ci.mtu = 1100; ci.use_srtp = "SRTP_AES128_CM_SHA1_80"; if (lws_gendtls_create(&pss->dtls_ctx, &ci)) return -1; @@ -1596,7 +1564,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co "a=ice-ufrag:%s\\r\\n" "a=ice-pwd:%s\\r\\n" "a=fingerprint:sha-256 %s\\r\\n" - "a=setup:passive\\r\\n" + "a=setup:%s\\r\\n" "a=mid:%s\\r\\n" "a=sendrecv\\r\\n" "a=msid:lws-stream lws-track-video\\r\\n" @@ -1609,6 +1577,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co "a=end-of-candidates\\r\\n", vhd->udp_port, pt_list[0] ? pt_list : "0", vhd->external_ip[0] ? vhd->external_ip : "127.0.0.1", pss->ice_ufrag, pss->ice_pwd, vhd->fingerprint, + pss->is_client ? "active" : "passive", mid_video, rtpmap_lines, pss->media->ssrc_video, pss->media->ssrc_video, candidates); @@ -1628,7 +1597,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co "a=ice-ufrag:%s\\r\\n" "a=ice-pwd:%s\\r\\n" "a=fingerprint:sha-256 %s\\r\\n" - "a=setup:passive\\r\\n" + "a=setup:%s\\r\\n" "a=mid:%s\\r\\n" "a=sendrecv\\r\\n" "a=msid:lws-stream lws-track-audio\\r\\n" @@ -1639,6 +1608,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co "%s" "a=end-of-candidates\\r\\n", vhd->udp_port, pss->media->pt_audio, vhd->external_ip[0] ? vhd->external_ip : "127.0.0.1", pss->ice_ufrag, pss->ice_pwd, vhd->fingerprint, + pss->is_client ? "active" : "passive", mid_audio, pss->media->pt_audio, fmtp_audio, pss->media->ssrc_audio, pss->media->ssrc_audio, candidates); @@ -1708,6 +1678,8 @@ lws_shared_webrtc_callback(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; /* VHD is managed by the application extension now */ if (!vhd->context) vhd->context = lws_get_context(wsi); @@ -1781,8 +1753,7 @@ lws_shared_webrtc_callback(struct lws *wsi, enum lws_callback_reasons reason, } vhd->wsi_udp = lws_create_adopt_udp(vhd->vhost, - vhd->external_ip[0] ? vhd->external_ip : NULL, - vhd->udp_port, LWS_CAUDP_BIND, + NULL, vhd->udp_port, LWS_CAUDP_BIND, "lws-webrtc-udp", NULL, NULL, NULL, NULL, NULL); if (!vhd->wsi_udp) { lwsl_err("%s: UDP socket creation failed\n", __func__); @@ -1819,8 +1790,6 @@ lws_shared_webrtc_callback(struct lws *wsi, enum lws_callback_reasons reason, #endif } pss->media->wsi_udp = vhd->wsi_udp; /* Critical: Session needs UDP handle */ - if (reason == LWS_CALLBACK_CLIENT_ESTABLISHED) - pss->is_client = 1; lws_dll2_clear(&pss->list); lws_dll2_add_tail(&pss->list, &vhd->sessions); pss->media->ssrc_video = (uint32_t)lws_now_usecs(); @@ -2063,18 +2032,45 @@ webrtc_handle_stun(struct lws *wsi, struct vhd_webrtc *vhd, struct pss_webrtc ** int n_stun = lws_stun_validate_and_reply(wsi, (uint8_t *)in, len, out, sizeof(out), pss->ice_pwd, sin); if (n_stun > 0) { if (udp_desc) { + if (pss->media) { + pss->media->peer_sa46 = udp_desc->sa46; + pss->media->has_peer_sa46 = 1; + } + int fd = (int)(lws_intptr_t)lws_get_socket_fd(wsi); if (fd >= 0) { socklen_t slen = udp_desc->sa46.sa4.sin_family == AF_INET6 ? (socklen_t)sizeof(udp_desc->sa46.sa6) : (socklen_t)sizeof(udp_desc->sa46.sa4); - // webrtc_pss_log(pss, "Sent STUN Response (%d bytes) successfully.\n", n_stun); + lwsl_user(">>> STUN RESPONSE (%d bytes) TO %s:%u <<<\n", n_stun, ads, ntohs(sin->sin_port)); + lwsl_hexdump_user(out, (size_t)n_stun); + webrtc_pss_log(pss, "Sent STUN Response (%d bytes) successfully.\n", n_stun); ssize_t sent = sendto((lws_sockfd_type)(lws_intptr_t)fd, (const char *)out, (size_t)n_stun, 0, (const struct sockaddr *)&udp_desc->sa46, slen); if (sent < 0) { webrtc_pss_err(pss, "STUN sendto failed: errno %d\n", errno); } else if (sent != n_stun) { webrtc_pss_err(pss, "STUN sendto partial %ld of %d\n", (long)sent, n_stun); } + + /* + * Trigger DTLS Client Hello now that we have proven connectivity + * via a successful STUN Request/Response cycle. + */ + if (pss->is_client && pss->handshake_started && pss->media && !pss->media->handshake_done) { + uint8_t dummy; + lws_gendtls_get_rx(&pss->dtls_ctx, &dummy, 1); + uint8_t out_dtls[2048]; + int _tx_len; + while ((_tx_len = lws_gendtls_get_tx(&pss->dtls_ctx, out_dtls, sizeof(out_dtls))) > 0) { + lwsl_notice("%s: Sending Initial DTLS ClientHello (%d bytes) to %s:%u\n", + __func__, _tx_len, ads, ntohs(sin->sin_port)); + if (sendto((lws_sockfd_type)(lws_intptr_t)fd, (const char *)out_dtls, (size_t)_tx_len, 0, + (const struct sockaddr *)&udp_desc->sa46, slen) < 0) { + webrtc_pss_err(pss, "DTLS ClientHello sendto failed: errno %d\n", errno); + } + } + } } } + } else { webrtc_pss_err(pss, "STUN validation failed (bad credentials or format)\n"); } @@ -2103,7 +2099,7 @@ webrtc_handle_dtls(struct lws *wsi, struct pss_webrtc *pss, const struct sockadd while ((_tx_len = lws_gendtls_get_tx(&pss->dtls_ctx, out, sizeof(out))) > 0) { lwsl_notice("%s: Sending DTLS Reply (%d bytes)\n", __func__, _tx_len); if (_fd >= 0) { - if (sendto((lws_sockfd_type)(lws_intptr_t)_fd, (const char *)out, (size_t)_tx_len, 0, (const struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (sendto((lws_sockfd_type)(lws_intptr_t)_fd, (const char *)out, (size_t)_tx_len, 0, (const struct sockaddr *)&pss->media->peer_sa46, pss->media->peer_sa46.sa4.sin_family == AF_INET6 ? (socklen_t)sizeof(pss->media->peer_sa46.sa6) : (socklen_t)sizeof(pss->media->peer_sa46.sa4)) < 0) { webrtc_pss_err(pss, "DTLS reply sendto failed: errno %d\n", errno); } } diff --git a/plugins/protocol_lws_webrtc_mixer/layout-speaker.c b/plugins/protocol_lws_webrtc_mixer/layout-speaker.c index bc154988b6..11a9d80919 100644 --- a/plugins/protocol_lws_webrtc_mixer/layout-speaker.c +++ b/plugins/protocol_lws_webrtc_mixer/layout-speaker.c @@ -155,6 +155,7 @@ lm_speaker_update(struct mixer_room *r, void *vctx) } } + /* Keep current speaker if energy isn't significantly higher than 0 and there is a current speaker. * Or just switch to max. We'll switch to the one with the highest energy. * If max_energy is 0, keep current speaker if still active, else pick join_time oldest/newest. */ @@ -182,6 +183,9 @@ lm_speaker_update(struct mixer_room *r, void *vctx) best_part = &ctx->parts[i]; continue; } + if (!best_part->s->video_muted && s->video_muted) { + continue; + } /* If both have same video mute state, prefer people who are not out_only */ if (best_part->s->video_muted == s->video_muted) { @@ -199,8 +203,15 @@ lm_speaker_update(struct mixer_room *r, void *vctx) } if (best_part) { + if (ctx->current_speaker != best_part->s) { + lwsl_notice("[INSTRUMENT] %s: Switching speaker from '%s' to '%s'\n", + __func__, ctx->current_speaker ? ((struct participant *)ctx->current_speaker->parent_p)->name : "none", + ((struct participant *)best_part->s->parent_p)->name); + } ctx->current_speaker = best_part->s; best_part->last_speaker_time = lws_now_usecs(); + } else { + lwsl_notice("[INSTRUMENT] %s: No best_part found!\n", __func__); } /* Allocate regions */ diff --git a/plugins/protocol_lws_webrtc_mixer/protocol_lws_webrtc_mixer.c b/plugins/protocol_lws_webrtc_mixer/protocol_lws_webrtc_mixer.c index 37bc1e64dd..832e3591f5 100644 --- a/plugins/protocol_lws_webrtc_mixer/protocol_lws_webrtc_mixer.c +++ b/plugins/protocol_lws_webrtc_mixer/protocol_lws_webrtc_mixer.c @@ -432,6 +432,9 @@ callback_mixer(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_PROTOCOL_INIT: if (!in) return 0; + if (lws_cmdline_option_cx(lws_get_context(wsi), "--lws-stub")) + return 0; + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd_mixer)); if (!vhd) return -1; @@ -620,15 +623,11 @@ callback_mixer(struct lws *wsi, enum lws_callback_reasons reason, if (is_capabilities) { /* Store the raw JSON blob of capabilities for this participant */ - lwsl_warn("%s: Processing capabilities for '%s'. Payload len %zu:\n", - __func__, p->name, len); - lwsl_hexdump_warn(in, len); - v = lws_json_simple_find((const char *)in, len, "\"controls\":", &al); if (v) { size_t kl; const char *kind = lws_json_simple_find((const char *)in, len, "\"kind\":", &kl); - int is_audio = (kind && !strncmp(kind, "\"audio\"", 7)); + int is_audio = (kind && (strstr(kind, "audio") != NULL)); char **target_cap = is_audio ? &p->capabilities_audio : &p->capabilities_video; if (*target_cap) free(*target_cap); @@ -637,41 +636,35 @@ callback_mixer(struct lws *wsi, enum lws_callback_reasons reason, if (*target_cap) { memcpy(*target_cap, in, len); (*target_cap)[len] = '\0'; - lwsl_notice("%s: Stored %s capabilities for '%s' (%zu bytes)\n", __func__, is_audio ? "audio" : "video", p->name, len); - lwsl_info("%s: Payload: %s\n", __func__, *target_cap); + lwsl_notice("[INSTRUMENT] %s: Stored %s capabilities for '%s' (%zu bytes): %s\n", __func__, is_audio ? "audio" : "video", p->name, len, *target_cap); /* Notify all clients about this update immediately */ broadcast_client_list(p->room, NULL); { /* Construct notification: {"type":"remote_capabilities","target":"","payload":} */ - size_t msg_len = len + 128 + (strlen(p->name) * 6); + size_t msg_len = len + 256 + (strlen(p->name) * 6); char *msg = malloc(msg_len); if (msg) { char esc_name[384]; lws_json_purify(esc_name, p->name, sizeof(esc_name), NULL); - int n = lws_snprintf(msg, msg_len, + lws_snprintf(msg, msg_len, "{\"type\":\"remote_capabilities\",\"target\":\"%s\",\"payload\":%s}", esc_name, *target_cap); - if (n > 0 && (size_t)n < msg_len) { - lwsl_info("%s: Broadcasting capabilities update for '%s' to room (len %d)\n", __func__, p->name, n); - /* Broadcast to everyone (including self? no, others) */ - lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&p->room->participants)) { - struct participant *other = lws_container_of(d, struct participant, list); - if (other->pss) - we_ops->send_text(other->pss, msg, strlen(msg)); - } lws_end_foreach_dll(d); - } else { - lwsl_err("%s: Truncation/Error broadcasting capabilities (n=%d, max=%zu)\n", __func__, n, msg_len); - } + lwsl_notice("[INSTRUMENT] %s: Broadcasting capabilities update for '%s' to room\n", __func__, p->name); + /* Broadcast to everyone else */ + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&p->room->participants)) { + struct participant *other = lws_container_of(d, struct participant, list); + if (other != p && other->pss) + we_ops->send_text(other->pss, msg, strlen(msg)); + } lws_end_foreach_dll(d); free(msg); } } } - } else { lwsl_err("%s: 'controls' key not found in capabilities message\n", __func__); } @@ -732,83 +725,59 @@ callback_mixer(struct lws *wsi, enum lws_callback_reasons reason, } /* Handle video_mute: {"type":"video_mute","muted":true/false} */ - if (al >= 10 && !strncmp(v, "\"video_mute\"", 12)) { + if (al >= 10 && (strstr(v, "video_mute") != NULL)) { const char *m = lws_json_simple_find((const char *)in, len, "\"muted\":", &al); if (m && p->session) { - p->session->video_muted = !strncmp(m, "true", 4); - lwsl_notice("%s: Participant '%s' video_muted=%d\n", __func__, p->name, p->session->video_muted); + p->session->video_muted = (strstr(m, "true") != NULL); + lwsl_notice("[INSTRUMENT] %s: Participant '%s' video_muted=%d\n", __func__, p->name, p->session->video_muted); broadcast_client_list(p->room, NULL); } } /* Handle request_caps: {"type":"request_caps","target":""} */ - if (al >= 12 && !strncmp(v, "\"request_caps\"", 12)) { + if (al >= 12 && (strstr(v, "request_caps") != NULL)) { const char *target = lws_json_simple_find((const char *)in, len, "\"target\":", &al); if (target) { char target_name[64]; size_t nl = al; if (*target == '\"') { target++; nl -= 2; } - if (nl >= sizeof(target_name)) nl = sizeof(target_name) - 1; - memcpy(target_name, target, nl); - target_name[nl] = '\0'; + lws_strnncpy(target_name, target, sizeof(target_name), nl); - lwsl_notice("%s: request_caps for '%s'\n", __func__, target_name); + lwsl_notice("[INSTRUMENT] %s: request_caps for '%s'\n", __func__, target_name); /* Find target participant */ - struct participant *tp = NULL; lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&p->room->participants)) { - struct participant *other = lws_container_of(d, struct participant, list); - if (!strcmp(other->name, target_name)) { - tp = other; - break; - } - } lws_end_foreach_dll(d); - - if (tp) { - int cached = 0; - /* Reply with cached capabilities */ - if (tp->capabilities_video) { - size_t msg_len = strlen(tp->capabilities_video) + 128 + (strlen(tp->name) * 6); - char *msg = malloc(msg_len); - if (msg) { - char esc_name[384]; - lws_json_purify(esc_name, tp->name, sizeof(esc_name), NULL); - int n = lws_snprintf(msg, msg_len, - "{\"type\":\"remote_capabilities\",\"target\":\"%s\",\"payload\":%s}", - esc_name, tp->capabilities_video); - we_ops->send_text(p->pss, msg, (size_t)n); - free(msg); - lwsl_notice("%s: Sent cached video caps for '%s' to '%s'\n", __func__, tp->name, p->name); - } - cached = 1; - } - if (tp->capabilities_audio) { - size_t msg_len = strlen(tp->capabilities_audio) + 128 + (strlen(tp->name) * 6); - char *msg = malloc(msg_len); - if (msg) { - char esc_name[384]; - lws_json_purify(esc_name, tp->name, sizeof(esc_name), NULL); - int n = lws_snprintf(msg, msg_len, - "{\"type\":\"remote_capabilities\",\"target\":\"%s\",\"payload\":%s}", - esc_name, tp->capabilities_audio); - we_ops->send_text(p->pss, msg, (size_t)n); - free(msg); - lwsl_notice("%s: Sent cached audio caps for '%s' to '%s'\n", __func__, tp->name, p->name); + struct participant *tp = lws_container_of(d, struct participant, list); + if (!strcmp(tp->name, target_name)) { + if (tp->capabilities_video) { + size_t msg_len = strlen(tp->capabilities_video) + 256 + (strlen(tp->name) * 6); + char *resp = malloc(msg_len); + if (resp) { + char esc_name[384]; + lws_json_purify(esc_name, tp->name, sizeof(esc_name), NULL); + lws_snprintf(resp, msg_len, + "{\"type\":\"remote_capabilities\",\"target\":\"%s\",\"payload\":%s}", + esc_name, tp->capabilities_video); + we_ops->send_text(p->pss, resp, strlen(resp)); + free(resp); + } } - cached = 1; - } - - if (!cached) { - /* Forward request to target */ - lwsl_notice("%s: No cached caps for '%s', forwarding request...\n", __func__, tp->name); - if (tp->pss) { - const char *fwd = "{\"type\":\"request_caps\"}"; - we_ops->send_text(tp->pss, fwd, strlen(fwd)); + if (tp->capabilities_audio) { + size_t msg_len = strlen(tp->capabilities_audio) + 256 + (strlen(tp->name) * 6); + char *resp = malloc(msg_len); + if (resp) { + char esc_name[384]; + lws_json_purify(esc_name, tp->name, sizeof(esc_name), NULL); + lws_snprintf(resp, msg_len, + "{\"type\":\"remote_capabilities\",\"target\":\"%s\",\"payload\":%s}", + esc_name, tp->capabilities_audio); + we_ops->send_text(p->pss, resp, strlen(resp)); + free(resp); + } } + break; } - } else { - lwsl_warn("%s: Target '%s' not found for request_caps\n", __func__, target_name); - } + } lws_end_foreach_dll(d); } } @@ -916,6 +885,15 @@ callback_mixer(struct lws *wsi, enum lws_callback_reasons reason, } } + /* The WebRTC offer might have been processed just before this join message, + meaning pss->media is now allocated. Update the session's media pointer + if it is currently NULL. */ + if (p->session && !p->session->media && we_ops && we_ops->get_media) { + p->session->media = we_ops->get_media(p->pss); + if (p->session->media && we_ops->media_ref) + we_ops->media_ref(p->session->media); + } + p->joined = 1; if (p->session) p->session->joined = 1; p->presence_missed = 0;