@@ -2132,13 +2132,27 @@ cb_dht(void *closure, int event, const lws_dht_hash_t *info_hash,
21322132 ((uint64_t )p [4 ] << 24 ) | ((uint64_t )p [5 ] << 16 ) |
21332133 ((uint64_t )p [6 ] << 8 ) | p [7 ];
21342134
2135- if (data_len > 8 ) {
2136- size_t dl = data_len - 8 ;
2137- if (dl > sizeof (newer_domain ) - 1 ) dl = sizeof (newer_domain ) - 1 ;
2138- memcpy (newer_domain , p + 8 , dl );
2139- newer_domain [dl ] = '\0' ;
2135+ if (data_len > 8 ) {
2136+ size_t dl = data_len - 8 ;
2137+ if (dl > sizeof (newer_domain ) - 1 ) dl = sizeof (newer_domain ) - 1 ;
2138+ memcpy (newer_domain , p + 8 , dl );
2139+ newer_domain [dl ] = '\0' ;
2140+ }
2141+ }
2142+
2143+ /*
2144+ * F-052: this unauthenticated datagram's domain
2145+ * string is stored in subscription state and later
2146+ * composed into filesystem paths (zone cache paths,
2147+ * lws_dir walks), so it must be a syntactically
2148+ * valid DNS name and not eg "../../x".
2149+ */
2150+ if (newer_domain [0 ] &&
2151+ !lws_dht_valid_domain_name (newer_domain )) {
2152+ lwsl_notice ("%s: Rejecting NOTIFY with malformed domain string\n" , __func__ );
2153+ break ;
21402154 }
2141- }
2155+
21422156
21432157 {
21442158 char peer_ip [64 ];
@@ -4444,6 +4458,18 @@ do_fetch_zone(struct lws_context *context, struct lws_dht_dnssec_fetch_zone_args
44444458
44454459 if (!strncmp (clean_domain , "dht-hash-" , 9 )) {
44464460 lws_strncpy (hex , clean_domain + 9 , sizeof (hex ));
4461+
4462+ /*
4463+ * F-052: the remainder is used verbatim for the
4464+ * lws_dir() storage walk and outgoing GET target hash,
4465+ * so it must be exactly the lowercase-hex shape the
4466+ * parser gate (F-051) would have produced.
4467+ */
4468+ if (!dht_dnssec_hash_token_ok (hex )) {
4469+ lwsl_warn ("%s: refusing fetch with malformed hash token\n" , __func__ );
4470+ return 1 ;
4471+ }
4472+
44474473 lws_hex_to_byte_array (hex , hash , (int )lws_genhash_size (LWS_DHT_STORE_GENHASH ));
44484474 } else {
44494475 char domain_str [256 ];
@@ -4816,6 +4842,18 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain)
48164842 for (int i = 0 ; i < (int )strlen (clean_domain ); i ++ )
48174843 clean_domain [i ] = (char )tolower (clean_domain [i ]);
48184844
4845+ /*
4846+ * F-052 defense-in-depth: the stored domain string is later
4847+ * composed into filesystem paths (zone cache paths, lws_dir
4848+ * walks), so refuse anything that is not a syntactically valid
4849+ * DNS name. This covers ops-side callers as well as the
4850+ * cb_dht NOTIFY intake gate.
4851+ */
4852+ if (!lws_dht_valid_domain_name (clean_domain )) {
4853+ lwsl_warn ("%s: refusing subscription for malformed domain string\n" , __func__ );
4854+ return 1 ;
4855+ }
4856+
48194857 lwsl_notice ("%s: Normalizing domain to %s for DHT hash calculation\n" , __func__ , clean_domain );
48204858 lws_snprintf (domain_str , sizeof (domain_str ), "lws-dnssec-dht-%s" , clean_domain );
48214859 if (lws_genhash_init (& ctx , LWS_DHT_STORE_GENHASH ) ||
@@ -4828,7 +4866,7 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain)
48284866 int exists = 0 ;
48294867 lws_start_foreach_dll (struct lws_dll2 * , d , lws_dll2_get_head (& vhd -> subscribed_domains )) {
48304868 struct lws_dht_dnssec_subscribed_domain * sub = lws_container_of (d , struct lws_dht_dnssec_subscribed_domain , list );
4831- if (!strcmp (sub -> domain , domain )) {
4869+ if (!strcmp (sub -> domain , clean_domain )) {
48324870 exists = 1 ;
48334871 break ;
48344872 }
@@ -4838,7 +4876,7 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain)
48384876 struct lws_dht_dnssec_subscribed_domain * nsub = malloc (sizeof (* nsub ));
48394877 if (nsub ) {
48404878 memset (nsub , 0 , sizeof (* nsub ));
4841- lws_strncpy (nsub -> domain , domain , sizeof (nsub -> domain ));
4879+ lws_strncpy (nsub -> domain , clean_domain , sizeof (nsub -> domain ));
48424880 memcpy (nsub -> hash , hash , (size_t )lws_genhash_size (LWS_DHT_STORE_GENHASH ));
48434881
48444882 char hex [65 ];
@@ -4871,7 +4909,7 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain)
48714909 struct lws_dht_dnssec_fetch_zone_args args ;
48724910 memset (& args , 0 , sizeof (args ));
48734911 args .vhost = vhd -> vhost ;
4874- args .domain = domain ;
4912+ args .domain = clean_domain ;
48754913 args .cache_dir = NULL ;
48764914 args .cb = NULL ;
48774915 args .opaque = NULL ;
@@ -4881,7 +4919,7 @@ do_subscribe_zone(struct lws_vhost *vhost, const char *domain)
48814919 time_t now = time (NULL );
48824920 lws_start_foreach_dll (struct lws_dll2 * , d , lws_dll2_get_head (& vhd -> subscribed_domains )) {
48834921 struct lws_dht_dnssec_subscribed_domain * sub = lws_container_of (d , struct lws_dht_dnssec_subscribed_domain , list );
4884- if (!strcmp (sub -> domain , domain )) {
4922+ if (!strcmp (sub -> domain , clean_domain )) {
48854923 if (sub -> needs_initial_fetch || now - sub -> last_notify_fetch >= 60 ) {
48864924 sub -> needs_initial_fetch = 0 ;
48874925 sub -> last_notify_fetch = now ;
0 commit comments