Commit 88c65757b5 for asterisk.org

commit 88c65757b56c716b5a175f35941be22e038b256f
Author: Milan Kyselica <mil.kyselica@gmail.com>
Date:   Mon Mar 23 15:18:48 2026 +0100

    cel_pgsql, cel_tds: Escape eventtype field to prevent SQL injection

    The eventtype column handler in cel_pgsql.c inserts
    record.user_defined_name directly into the SQL query without
    calling PQescapeStringConn(), while all other string fields in
    the same function are properly escaped. Similarly, cel_tds.c
    passes the raw user_defined_name into the SQL INSERT without
    routing it through anti_injection(), while all other fields are
    processed through that function.

    For cel_pgsql.c, escape the eventtype value using
    PQescapeStringConn(), matching the existing pattern used for all
    other string fields at lines 308-331 of the same function.

    For cel_tds.c, route the eventtype value through
    anti_injection() consistent with how all other fields are handled
    in the same function.

    Resolves: #GHSA-ph27-3m5q-mj5m

diff --git a/cel/cel_pgsql.c b/cel/cel_pgsql.c
index 208bdd1f24..eb9bdbcd72 100644
--- a/cel/cel_pgsql.c
+++ b/cel/cel_pgsql.c
@@ -224,12 +224,25 @@ static void pgsql_log(struct ast_event *event)
 				} else {
 					/* Char field, probably */
 					const char *event_name;
+					size_t required_size;

 					event_name = (!cel_show_user_def
 						&& record.event_type == AST_CEL_USER_DEFINED)
 						? record.user_defined_name : record.event_name;
-					LENGTHEN_BUF2(strlen(event_name) + 1);
-					ast_str_append(&sql2, 0, "%s'%s'", SEP, event_name);
+					required_size = strlen(event_name) * 2 + 1;
+					if (required_size > bufsize) {
+						char *tmpbuf = ast_realloc(escapebuf, required_size);
+						if (!tmpbuf) {
+							AST_RWLIST_UNLOCK(&psql_columns);
+							goto ast_log_cleanup;
+						}
+						escapebuf = tmpbuf;
+						bufsize = required_size;
+					}
+					PQescapeStringConn(conn, escapebuf, event_name,
+						strlen(event_name), NULL);
+					LENGTHEN_BUF2(strlen(escapebuf) + 3);
+					ast_str_append(&sql2, 0, "%s'%s'", SEP, escapebuf);
 				}
 			} else if (strcmp(cur->name, "amaflags") == 0) {
 				if (strncmp(cur->type, "int", 3) == 0) {
diff --git a/cel/cel_tds.c b/cel/cel_tds.c
index 4b22ff1a48..d5c32184e4 100644
--- a/cel/cel_tds.c
+++ b/cel/cel_tds.c
@@ -110,7 +110,7 @@ static int mssql_disconnect(void);
 static void tds_log(struct ast_event *event)
 {
 	char start[80];
-	char *accountcode_ai, *clidnum_ai, *exten_ai, *context_ai, *clid_ai, *channel_ai, *app_ai, *appdata_ai, *uniqueid_ai, *linkedid_ai, *cidani_ai, *cidrdnis_ai, *ciddnid_ai, *peer_ai, *userfield_ai;
+	char *accountcode_ai, *clidnum_ai, *exten_ai, *context_ai, *clid_ai, *channel_ai, *app_ai, *appdata_ai, *uniqueid_ai, *linkedid_ai, *cidani_ai, *cidrdnis_ai, *ciddnid_ai, *peer_ai, *userfield_ai, *eventtype_ai;
 	RETCODE erc;
 	int attempt = 1;
 	struct ast_cel_event_record record = {
@@ -138,6 +138,9 @@ static void tds_log(struct ast_event *event)
 	linkedid_ai    = anti_injection(record.linked_id, 32);
 	userfield_ai   = anti_injection(record.user_field, 32);
 	peer_ai        = anti_injection(record.peer, 32);
+	eventtype_ai   = anti_injection(
+		(record.event_type == AST_CEL_USER_DEFINED)
+			? record.user_defined_name : record.event_name, 32);

 	get_date(start, sizeof(start), record.event_time);

@@ -199,8 +202,7 @@ retry:
 		")",
 		settings->table, accountcode_ai, clidnum_ai, clid_ai, cidani_ai, cidrdnis_ai,
 		ciddnid_ai, exten_ai, context_ai, channel_ai, app_ai, appdata_ai, start,
-		(record.event_type == AST_CEL_USER_DEFINED)
-			? record.user_defined_name : record.event_name,
+		eventtype_ai,
 					ast_channel_amaflags2string(record.amaflag), uniqueid_ai, linkedid_ai,
 		userfield_ai, peer_ai);

@@ -250,6 +252,7 @@ done:
 	ast_free(linkedid_ai);
 	ast_free(userfield_ai);
 	ast_free(peer_ai);
+	ast_free(eventtype_ai);

 	return;
 }