Commit 7015f6a73a for freeswitch.com

commit 7015f6a73ab3d67fed5fbfc277ecfb88a74c2a86
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date:   Wed Aug 26 18:46:56 2026 +0500

    [mod_sofia] Add enable-chat-api-proto to gate the api chat proto (#3135)

    The chat layer lets an inbound SIP MESSAGE select which chat proto handles it,
    and the `api` proto runs the address as a FreeSWITCH API command. The new
    per-profile `enable-chat-api-proto` param controls that route and is off unless
    set. A MESSAGE selecting the proto on a profile without it is answered 403 and
    logged with the source and the requested command.

    The proto compare is case-insensitive, matching the chat interface registry,
    which is created with `switch_core_hash_init_nocase()`.

    The param ships commented out in the four vanilla profiles and the mod_sofia
    sofia.conf.xml sample.

diff --git a/conf/vanilla/sip_profiles/external-ipv6.xml b/conf/vanilla/sip_profiles/external-ipv6.xml
index 1b9d0c857d..f278cd1d19 100644
--- a/conf/vanilla/sip_profiles/external-ipv6.xml
+++ b/conf/vanilla/sip_profiles/external-ipv6.xml
@@ -56,6 +56,8 @@
     <param name="inbound-codec-negotiation" value="generous"/>
     <param name="nonce-ttl" value="60"/>
     <param name="auth-calls" value="false"/>
+    <!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands, so enable only for trusted peers -->
+    <!-- <param name="enable-chat-api-proto" value="true"/> -->
     <param name="inbound-late-negotiation" value="true"/>
     <!--
         DO NOT USE HOSTNAMES, ONLY IP ADDRESSES IN THESE SETTINGS!
diff --git a/conf/vanilla/sip_profiles/external.xml b/conf/vanilla/sip_profiles/external.xml
index 57ec4a6e74..6bae9720b8 100644
--- a/conf/vanilla/sip_profiles/external.xml
+++ b/conf/vanilla/sip_profiles/external.xml
@@ -56,6 +56,8 @@
     <param name="inbound-codec-negotiation" value="generous"/>
     <param name="nonce-ttl" value="60"/>
     <param name="auth-calls" value="false"/>
+    <!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands, so enable only for trusted peers -->
+    <!-- <param name="enable-chat-api-proto" value="true"/> -->
     <param name="inbound-late-negotiation" value="true"/>
     <!--
         DO NOT USE HOSTNAMES, ONLY IP ADDRESSES IN THESE SETTINGS!
diff --git a/conf/vanilla/sip_profiles/internal-ipv6.xml b/conf/vanilla/sip_profiles/internal-ipv6.xml
index 26c891ba98..00cbd0b959 100644
--- a/conf/vanilla/sip_profiles/internal-ipv6.xml
+++ b/conf/vanilla/sip_profiles/internal-ipv6.xml
@@ -93,6 +93,8 @@
     <!-- add a ;received="<ip>:<port>" to the contact when replying to register for nat handling -->
     <!--<param name="NDLB-received-in-nat-reg-contact" value="true"/>-->
     <param name="auth-calls" value="$${internal_auth_calls}"/>
+    <!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands, so enable only for trusted peers -->
+    <!-- <param name="enable-chat-api-proto" value="true"/> -->
     <!-- on authed calls, authenticate *all* the packets not just invite -->
     <param name="auth-all-packets" value="false"/>
     <!-- Shouldn't set these on IPv6 -->
diff --git a/conf/vanilla/sip_profiles/internal.xml b/conf/vanilla/sip_profiles/internal.xml
index eb07779f43..79fcad741e 100644
--- a/conf/vanilla/sip_profiles/internal.xml
+++ b/conf/vanilla/sip_profiles/internal.xml
@@ -107,6 +107,9 @@
     <!-- extended info parsing -->
     <!-- <param name="extended-info-parsing" value="true"/> -->

+    <!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands, so enable only for trusted peers -->
+    <!-- <param name="enable-chat-api-proto" value="true"/> -->
+
     <!--<param name="aggressive-nat-detection" value="true"/>-->
     <!--
         There are known issues (asserts and segfaults) when 100rel is enabled.
diff --git a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml
index 4999681cdd..4732ae6fbf 100644
--- a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml
+++ b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml
@@ -148,6 +148,10 @@
         <!-- extended info parsing -->
         <!-- <param name="extended-info-parsing" value="true"/> -->

+        <!-- lets an inbound MESSAGE to api+<command> run FreeSWITCH API commands,
+             so enable only for trusted peers -->
+        <!-- <param name="enable-chat-api-proto" value="true"/> -->
+
         <!-- <param name="aggressive-nat-detection" value="true"/> -->
         <!-- There are known issues (asserts and segfaults) when 100rel is
              enabled.  It is not recommended to enable 100rel at this time. -->
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h
index 3689f82a20..81d0517d78 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.h
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.h
@@ -311,6 +311,7 @@ typedef enum {
 	PFLAG_AUTH_REQUIRE_USER,
 	PFLAG_AUTH_CALLS_ACL_ONLY,
 	PFLAG_USE_PORT_FOR_ACL_CHECK,
+	PFLAG_ENABLE_CHAT_API_PROTO,

 	/* No new flags below this line */
 	PFLAG_MAX
diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c
index 0451a4bdea..f5bc04de26 100644
--- a/src/mod/endpoints/mod_sofia/sofia.c
+++ b/src/mod/endpoints/mod_sofia/sofia.c
@@ -4917,6 +4917,12 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
 						} else {
 							sofia_clear_pflag(profile, PFLAG_ENABLE_CHAT);
 						}
+					} else if (!strcasecmp(var, "enable-chat-api-proto")) {
+						if (switch_true(val)) {
+							sofia_set_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO);
+						} else {
+							sofia_clear_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO);
+						}
 					} else if (!strcasecmp(var, "fire-bye-response-events")) {
 						if (switch_true(val)) {
 							sofia_set_pflag(profile, PFLAG_FIRE_BYE_RESPONSE_EVENTS);
diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c
index d9257285a1..667886bccb 100644
--- a/src/mod/endpoints/mod_sofia/sofia_presence.c
+++ b/src/mod/endpoints/mod_sofia/sofia_presence.c
@@ -4879,6 +4879,24 @@ void sofia_presence_handle_sip_i_message(int status,
 				p = strchr(proto, '+');
 				*p++ = '\0';

+				if (!strcasecmp(proto, "api") && !sofia_test_pflag(profile, PFLAG_ENABLE_CHAT_API_PROTO)) {
+					/* p is the rest of the To user, still percent-encoded and bounded by the proto buffer. */
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+									  "Profile [%s] rejected a MESSAGE from %s@%s (%s:%d) addressed to the 'api' chat proto [%s]. "
+									  "Set enable-chat-api-proto=true on the profile to permit it.\n",
+									  profile->name, switch_str_nil(from_user), switch_str_nil(from_host), network_ip, network_port, p);
+
+					nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
+
+					if (full_from) {
+						su_free(nua_handle_get_home(nh), full_from);
+					}
+
+					/* Return rather than goto end: the end label answers 200/202, which would be a
+					   second response on this transaction. */
+					return;
+				}
+
 				if ((to_addr = strdup(p))) {
 					if ((p = strchr(to_addr, '+'))) {
 						*p = '@';