From a4881de3cfb6534c78fe3fd7c73da4f29050c6dc Mon Sep 17 00:00:00 2001 From: nvenka781 Date: Mon, 20 Oct 2025 21:10:40 +0000 Subject: [PATCH 1/5] TCXB8-3707: Parental Controls blocks false positives Reason for change: Parental control block on access with certain keywords cause false positives. User intended to block websites like matchweed.com, catchherb.co with keyword "thc", "bc". Along with these, any url with substring "thc" also gets blocked. Change: Keyword search is done on the full string with kmp algorithm. Since no limit is mentioned, the default of 65535 is considered as upper limit of packet payload size for kmp algorithm search. This is restricted to have an upper limit of upto 128 bytes for keyword search. Also, search is limited to the host part of the url for keyword match instead of full url match. Recommendation to user: Avoid very short generic query keywords for blocking Test Procedure: Validate IPTV playback Priority: P1 Risks: None Signed-off-by: nagalakshmi_venkataraman@comcast.com --- source/firewall/firewall.c | 82 +++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/source/firewall/firewall.c b/source/firewall/firewall.c index 0cb72ca..126f8f2 100644 --- a/source/firewall/firewall.c +++ b/source/firewall/firewall.c @@ -9068,6 +9068,28 @@ static int do_parcon_mgmt_service(FILE *fp, int iptype, FILE *cron_fp) return(0); } +// convert_word_to_host_hex converts string to lowercase hex +static int convert_word_to_host_hex(const char *input, char *output, size_t outsize) +{ + if (!input || !output || outsize == 0) return 0; + + size_t len = strlen(input); + if (len == 0 || outsize < len * 2 + 1) return 0; + + for (size_t i = 0; i < len; ++i) { + unsigned char c = (unsigned char)input[i]; + + if (isspace(c) || !isprint(c) || c == '"' || c == '\\') { + return 0; + } + + snprintf(output + i * 2, outsize - i * 2, "%02x", tolower(c)); + } + + output[len * 2] = '\0'; + return 1; +} + /* * add parental control managed site/keyword rules */ @@ -9309,33 +9331,45 @@ static int do_parcon_mgmt_site_keywd(FILE *fp, FILE *nat_fp, int iptype, FILE *c block_url_by_ipaddr(fp, query + host_name_offset, drop_log, iptype, ins_num, nstdPort); } - else if (strncasecmp(method, "KEYWD", 5)==0) + else if (strncasecmp(method, "KEYWD", 5) == 0) { - // consider the case that user input whole url. - if(strstr(query, "://") != 0) { - fprintf(fp, "-A lan2wan_pc_site -m string --string \"%s\" --algo kmp --icase -j %s\n", strstr(query, "://") + 3, drop_log); -#if defined(_HUB4_PRODUCT_REQ_) || defined (_RDKB_GLOBAL_PRODUCT_REQ_) -#if defined (_RDKB_GLOBAL_PRODUCT_REQ_) - if( 0 == strncmp( devicePartnerId, "sky-", 4 ) ) -#endif - { - //In Hub4 keyword blocking feature is not working with FORWARD chain rules as CPE (dnsmasq) acts as DNS Proxy. - //Add rules in INPUT chain to resolve this issue. - fprintf(fp, "-I INPUT -i %s -j lan2wan_pc_site \n", lan_ifname); - } -#endif - } else { - fprintf(fp, "-A lan2wan_pc_site -m string --string \"%s\" --algo kmp --icase -j %s\n", query, drop_log); -#if defined(_HUB4_PRODUCT_REQ_) || defined (_RDKB_GLOBAL_PRODUCT_REQ_) -#if defined (_RDKB_GLOBAL_PRODUCT_REQ_) - if( 0 == strncmp( devicePartnerId, "sky-", 4 ) ) -#endif - { - fprintf(fp, "-I INPUT -i %s -j lan2wan_pc_site \n", lan_ifname); - } + const char *queryKw = query; + char hexKw[256] = {0}; + int kmpAlgoLen = 128; //upto 128 bytes + + // Extract hostname part if full URL + if (strstr(query, "://") != NULL) { + queryKw = strstr(query, "://") + 3; + } + + // Log warning for very short keywords + if (strlen(queryKw) < 3) { + FIREWALL_DEBUG("Keyword '%s' too short to safely match\n", queryKw); + } + + // Match using httphost + fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m httphost --host \"%s\" -j %s\n", queryKw, drop_log); + + // Match using "Host: " string + fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m string --string \"Host: %s\" --algo kmp --to %d --icase -j %s\n", + queryKw, kmpAlgoLen, drop_log); + + // Match using hex-string + if (convert_word_to_host_hex(queryKw, hexKw, sizeof(hexKw))) { + fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m string --algo kmp --to %d --hex-string \"|0D0A486F73743A20%s|\" -j %s\n", + kmpAlgoLen, hexKw, drop_log); + } + +#if defined(_HUB4_PRODUCT_REQ_) || defined(_RDKB_GLOBAL_PRODUCT_REQ_) +#if defined(_RDKB_GLOBAL_PRODUCT_REQ_) + if (strncmp(devicePartnerId, "sky-", 4) == 0) #endif + { + // In Hub4, DNS proxy prevents FORWARD chain rules from working — use INPUT + fprintf(fp, "-I INPUT -i %s -j lan2wan_pc_site\n", lan_ifname); } - } +#endif + } } } FIREWALL_DEBUG("Exiting do_parcon_mgmt_site_keywd\n"); From dcbb174a550d8e482e5ccd664d6de0bd9a939b07 Mon Sep 17 00:00:00 2001 From: nvenka781 Date: Mon, 20 Oct 2025 23:02:33 +0000 Subject: [PATCH 2/5] Corrected debug print syntax --- source/firewall/firewall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/firewall/firewall.c b/source/firewall/firewall.c index 126f8f2..9b226b3 100644 --- a/source/firewall/firewall.c +++ b/source/firewall/firewall.c @@ -9344,7 +9344,7 @@ static int do_parcon_mgmt_site_keywd(FILE *fp, FILE *nat_fp, int iptype, FILE *c // Log warning for very short keywords if (strlen(queryKw) < 3) { - FIREWALL_DEBUG("Keyword '%s' too short to safely match\n", queryKw); + FIREWALL_DEBUG("Keyword too short to safely match\n"); } // Match using httphost From ca58327f22f0e50aacdb78d1ea0857f6a008bff4 Mon Sep 17 00:00:00 2001 From: nvenka781 Date: Wed, 22 Oct 2025 19:30:10 +0000 Subject: [PATCH 3/5] Added multiple ranged rules to filter based on host and keyword --- source/firewall/firewall.c | 55 +++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/source/firewall/firewall.c b/source/firewall/firewall.c index 9b226b3..33c3e56 100644 --- a/source/firewall/firewall.c +++ b/source/firewall/firewall.c @@ -9331,45 +9331,56 @@ static int do_parcon_mgmt_site_keywd(FILE *fp, FILE *nat_fp, int iptype, FILE *c block_url_by_ipaddr(fp, query + host_name_offset, drop_log, iptype, ins_num, nstdPort); } - else if (strncasecmp(method, "KEYWD", 5) == 0) + else if (strncasecmp(method, "KEYWD", 5) == 0) { - const char *queryKw = query; - char hexKw[256] = {0}; - int kmpAlgoLen = 128; //upto 128 bytes + const char *keyword = NULL; + char hostStr[] = "Host:"; + int range_max = 1024; //max payload bytes to filter + int range_incr = 64; //byte ranges to filter - // Extract hostname part if full URL + // Extract keyword if user input is a full URL if (strstr(query, "://") != NULL) { - queryKw = strstr(query, "://") + 3; + keyword = strstr(query, "://") + 3; + } else { + keyword = query; } - // Log warning for very short keywords - if (strlen(queryKw) < 3) { - FIREWALL_DEBUG("Keyword too short to safely match\n"); + if (keyword == NULL || strlen(keyword) == 0) { + fprintf(stderr, "Warning: Empty keyword, skipping rule generation.\n"); + return(0); } - // Match using httphost - fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m httphost --host \"%s\" -j %s\n", queryKw, drop_log); + // Create rules for various ranges of payload to filter + int from; + for (from = 0; from < range_max; from += range_incr) { + int to = from + range_incr; + char chainName[64]; + + // Create new chain LOG_SiteBlocked_check_kw__ + snprintf(chainName, sizeof(chainName), "LOG_SiteBlocked_check_kw_%d_%d", from, to); + fprintf(fp, "-N %s\n", chainName); - // Match using "Host: " string - fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m string --string \"Host: %s\" --algo kmp --to %d --icase -j %s\n", - queryKw, kmpAlgoLen, drop_log); + // Add rule to jump to private chain if "Host:" is found in this offset range + fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m string --string \"%s\" --algo kmp --from %d --to %d --icase -j %s\n", + hostStr, from, to, chainName); - // Match using hex-string - if (convert_word_to_host_hex(queryKw, hexKw, sizeof(hexKw))) { - fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 80 -m string --algo kmp --to %d --hex-string \"|0D0A486F73743A20%s|\" -j %s\n", - kmpAlgoLen, hexKw, drop_log); + // Add rule to match keyword in private chain within same offset range + fprintf(fp, "-A %s -m string --string \"%s\" --algo kmp --from %d --to %d --icase -j %s\n", + chainName, keyword, from, to, drop_log); + + // Default rule to return if not matched + fprintf(fp, "-A %s -j RETURN\n", chainName); } -#if defined(_HUB4_PRODUCT_REQ_) || defined(_RDKB_GLOBAL_PRODUCT_REQ_) -#if defined(_RDKB_GLOBAL_PRODUCT_REQ_) +#if defined(_HUB4_PRODUCT_REQ_) || defined (_RDKB_GLOBAL_PRODUCT_REQ_) +#if defined (_RDKB_GLOBAL_PRODUCT_REQ_) if (strncmp(devicePartnerId, "sky-", 4) == 0) #endif { - // In Hub4, DNS proxy prevents FORWARD chain rules from working — use INPUT fprintf(fp, "-I INPUT -i %s -j lan2wan_pc_site\n", lan_ifname); } #endif - } + } } } FIREWALL_DEBUG("Exiting do_parcon_mgmt_site_keywd\n"); From bdcc7b1b0a1c97afdb22cddb24314c88f38d7b6a Mon Sep 17 00:00:00 2001 From: nvenka781 Date: Wed, 22 Oct 2025 19:34:27 +0000 Subject: [PATCH 4/5] Removed unused function --- source/firewall/firewall.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/source/firewall/firewall.c b/source/firewall/firewall.c index 33c3e56..8c8b5b3 100644 --- a/source/firewall/firewall.c +++ b/source/firewall/firewall.c @@ -9068,28 +9068,6 @@ static int do_parcon_mgmt_service(FILE *fp, int iptype, FILE *cron_fp) return(0); } -// convert_word_to_host_hex converts string to lowercase hex -static int convert_word_to_host_hex(const char *input, char *output, size_t outsize) -{ - if (!input || !output || outsize == 0) return 0; - - size_t len = strlen(input); - if (len == 0 || outsize < len * 2 + 1) return 0; - - for (size_t i = 0; i < len; ++i) { - unsigned char c = (unsigned char)input[i]; - - if (isspace(c) || !isprint(c) || c == '"' || c == '\\') { - return 0; - } - - snprintf(output + i * 2, outsize - i * 2, "%02x", tolower(c)); - } - - output[len * 2] = '\0'; - return 1; -} - /* * add parental control managed site/keyword rules */ From b01f00000ef64b52809f7bccad544217a6d3dbf7 Mon Sep 17 00:00:00 2001 From: nvenka781 Date: Wed, 22 Oct 2025 20:38:40 +0000 Subject: [PATCH 5/5] Adding rule for handling https keyword filtering --- source/firewall/firewall.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/firewall/firewall.c b/source/firewall/firewall.c index 8c8b5b3..0adfdb6 100644 --- a/source/firewall/firewall.c +++ b/source/firewall/firewall.c @@ -9350,6 +9350,10 @@ static int do_parcon_mgmt_site_keywd(FILE *fp, FILE *nat_fp, int iptype, FILE *c fprintf(fp, "-A %s -j RETURN\n", chainName); } + // Add rule for https filter + fprintf(fp, "-A lan2wan_pc_site -p tcp --dport 443 -m string --string \"%s\" --algo kmp --icase -j %s\n", + keyword, drop_log); + #if defined(_HUB4_PRODUCT_REQ_) || defined (_RDKB_GLOBAL_PRODUCT_REQ_) #if defined (_RDKB_GLOBAL_PRODUCT_REQ_) if (strncmp(devicePartnerId, "sky-", 4) == 0)