From 5d4a5fcf89a6107cdb3fd7be1298313dd5ae3d6e Mon Sep 17 00:00:00 2001
From: Joshua Rogers <MegaManSec@users.noreply.github.com>
Date: Wed, 22 Oct 2025 02:27:35 +0800
Subject: [PATCH] ssl_ncp: make tls_item_in_cipher_list zero-alloc and linear

This avoids avoids string_alloc + strtok on every membership check, which
for very long IV_CIPHERS with many colon-separated tokens, forced O(N*M)
comparisons with I(M) allocations in a single negotiatin.

This was found by ZeroPath.

Signed-off-by: Joshua Rogers <MegaManSec@users.noreply.github.com>
---
 src/openvpn/ssl_ncp.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 790e50fc..a66f7a60 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -205,22 +205,28 @@ append_cipher_to_ncp_list(struct options *o, const char *ciphername)
 bool
 tls_item_in_cipher_list(const char *item, const char *list)
 {
-    char *tmp_ciphers = string_alloc(list, NULL);
-    char *tmp_ciphers_orig = tmp_ciphers;
+    const size_t item_len = strlen(item);
+    const char *p = list;
 
-    const char *token = strtok(tmp_ciphers, ":");
-    while (token)
+    while (*p)
     {
-        if (0 == strcmp(token, item))
+        const char *sep = strchr(p, ':');
+        size_t len = sep ? (size_t)(sep - p) : strlen(p);
+
+        if (len == item_len && 0 == strncmp(p, item, len))
+        {
+            return true;
+        }
+
+        if (!sep)
         {
             break;
         }
-        token = strtok(NULL, ":");
+        p = sep + 1;
     }
-    free(tmp_ciphers_orig);
-
-    return token != NULL;
+    return false;
 }
+
 const char *
 tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc)
 {
-- 
2.51.1

