]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_config_ldap: Escape LDAP filter values per RFC 4515
authorMilan Kyselica <mil.kyselica@gmail.com>
Mon, 23 Mar 2026 14:20:27 +0000 (15:20 +0100)
committerGeorge Joseph <gtjoseph@users.noreply.github.com>
Thu, 25 Jun 2026 14:21:09 +0000 (08:21 -0600)
The LDAP realtime driver constructs search filters by directly
concatenating user-supplied values without RFC 4515 escaping.
When LDAP is used as a realtime backend for endpoint
identification, characters with special meaning in LDAP filters
(*, (, ), \) can be injected via the SIP From header username.

Add ldap_filter_escape_value() that escapes RFC 4515 special
characters to their \HH hex representation, and apply it to
non-LIKE query values. The LIKE query path preserves the existing
wildcard conversion behavior with a note for maintainers.

Resolves: #GHSA-r6c2-hwc2-j4mp

res/res_config_ldap.c

index 295a5cee63466cb32357683d3ee00298f49b24d5..730727fb3420a8fd09ba423dff82a64fb8796fdb 100644 (file)
@@ -733,6 +733,40 @@ static int replace_string_in_string(char *string, const char *search, const char
        return replaced;
 }
 
+/*!
+ * \internal
+ * \brief Escape a value for safe inclusion in an LDAP filter per RFC 4515.
+ *
+ * Characters that have special meaning in LDAP filters are escaped
+ * to their \\HH hex representation: * ( ) \\ and NUL.
+ *
+ * \param value The raw value to escape
+ * \param escaped Output buffer (caller-allocated ast_str)
+ */
+static void ldap_filter_escape_value(const char *value, struct ast_str **escaped)
+{
+       ast_str_reset(*escaped);
+       for (; *value; value++) {
+               switch (*value) {
+               case '*':
+                       ast_str_append(escaped, 0, "\\2a");
+                       break;
+               case '(':
+                       ast_str_append(escaped, 0, "\\28");
+                       break;
+               case ')':
+                       ast_str_append(escaped, 0, "\\29");
+                       break;
+               case '\\':
+                       ast_str_append(escaped, 0, "\\5c");
+                       break;
+               default:
+                       ast_str_append(escaped, 0, "%c", *value);
+                       break;
+               }
+       }
+}
+
 /*! \brief Append a name=value filter string. The filter string can grow.
  */
 static void append_var_and_value_to_filter(struct ast_str **filter,
@@ -742,9 +776,15 @@ static void append_var_and_value_to_filter(struct ast_str **filter,
        char *new_name = NULL;
        char *new_value = NULL;
        const char *like_pos = strstr(name, " LIKE");
+       struct ast_str *escaped_value;
 
        ast_debug(2, "name='%s' value='%s'\n", name, value);
 
+       escaped_value = ast_str_create(256);
+       if (!escaped_value) {
+               return;
+       }
+
        if (like_pos) {
                int len = like_pos - name;
 
@@ -753,11 +793,20 @@ static void append_var_and_value_to_filter(struct ast_str **filter,
                value = new_value = ast_strdupa(value);
                replace_string_in_string(new_value, "\\_", "_");
                replace_string_in_string(new_value, "%", "*");
+               name = convert_attribute_name_to_ldap(table_config, name);
+               /* Note: The LIKE path preserves original wildcard behavior.
+                * A more comprehensive escaping of the LIKE path is left
+                * to the maintainers familiar with the query semantics.
+                */
+               ast_str_append(filter, 0, "(%s=%s)", name, value);
+       } else {
+               name = convert_attribute_name_to_ldap(table_config, name);
+               ldap_filter_escape_value(value, &escaped_value);
+               ast_str_append(filter, 0, "(%s=%s)", name,
+                       ast_str_buffer(escaped_value));
        }
 
-       name = convert_attribute_name_to_ldap(table_config, name);
-
-       ast_str_append(filter, 0, "(%s=%s)", name, value);
+       ast_free(escaped_value);
 }
 
 /*!