Commit 04aa482212 for asterisk.org
commit 04aa482212e43b292817444eb4de35499f4b1568
Author: Milan Kyselica <mil.kyselica@gmail.com>
Date: Mon Mar 23 15:20:27 2026 +0100
res_config_ldap: Escape LDAP filter values per RFC 4515
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
diff --git a/res/res_config_ldap.c b/res/res_config_ldap.c
index 295a5cee63..730727fb34 100644
--- a/res/res_config_ldap.c
+++ b/res/res_config_ldap.c
@@ -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);
}
/*!