Commit Graph
36150 Commits
Author SHA1 Message Date
Dmitry Verenitsin fc0db829b8 Merge commit from fork
`rtmp_handle_control()` formats the control-message body into a fixed
200-byte stack buffer with an unbounded `sprintf` loop whose iteration
count is the wire message length. A body of ~70 bytes or more runs the
write off the end of `buf`, corrupting the stack frame; the length is
taken straight from the chunk header and reaches this path before any
login, so a remote peer can trigger it.

Bound the loop with `snprintf` against the remaining space and stop when
the buffer is full. This also caps the iteration count, so the loop can
no longer read `state->buf` past what was reassembled. The hex dump is
debug-only output, so capping it changes nothing operational.
2026-08-08 19:55:22 +03:00
Dmitry Verenitsin 349f55ff54 Merge commit from fork
In `msrp_parse_buffer()`, the `range_star` arm of `MSRP_ST_WAIT_BODY`
computed the body length by subtracting the delimiter length and trailing
framing from the received segment length. `payload_bytes` is a
`switch_size_t`, so a short segment wrapped it to near `SIZE_MAX`, which
`switch_msrp_msg_set_payload()` uses to size its allocation and as the
`memcpy` length. This affects both `len - dlen - 5`, whose scan pointer
also addressed memory before `buf`, and `delim_pos - buf - 2`, covered
only by a `switch_assert()` on received data.

Require room for the trailing end-line and the CRLF closing the body
before either is computed; a short segment is incomplete, so the parser
waits for more bytes.
2026-08-08 19:54:01 +03:00
Dmitry Verenitsin 86b10358e2 Merge commit from fork
`switch_core_media_add_crypto()` parses the SDP `a=crypto` keysalt and
decodes it into a fixed-size key buffer. Enforce the buffer contract at
the call site:

- Reject a zero-length, negative, or over-long keysalt (the length
  check now has both a lower bound and an upper bound against the copy
  buffer).
- Copy the keysalt token into a NUL-terminated buffer and decode from
  that, so `switch_b64_decode` stops at the token instead of reading on
  into the following key material (it consumes input to the NUL and
  skips non-base64 bytes).
- Pass the destination buffer size as the decode bound rather than the
  parsed token length.
- Require the decoded length to cover the crypto suite's key+salt so the
  subsequent copy cannot read past the decoded bytes.

Add `test_add_crypto_keysalt_bounds`, a table-driven test covering the
accepted and rejected keysalt shapes across suites (AES-128/192/256),
including RFC 4568 lifetime/MKI and multi-key lines.
2026-08-08 19:47:47 +03:00
Dmitry Verenitsin 6f47b5c38c [unit-tests] Bind the DTLS certificate test on the configured RTP port (#3108) 2026-08-08 19:46:34 +03:00
Dmitry Verenitsin e730a4e541 Merge commit from fork
The RTCP NACK feedback handler used the packet's header length field as the
loop bound instead of the byte count the caller already validated. An
oversized length ran the loop past the fixed-size RTCP buffer, an
out-of-bounds access.

Clamp the entry count to the feedback words that fit in the received packet.

Add an end-to-end test feeding an oversized-length NACK through the RTCP
reader.
2026-08-08 18:29:25 +03:00
Dmitry Verenitsin d557ca6d29 Merge commit from fork
The 127-length extended payload field was decoded with `ntohl()`, a
32-bit byte swap, dropping the upper word of the 64-bit length. Combined
with the signed `issize_t plen`, a high low-word truncated to a negative
length that slipped past the signed size guard and became `SIZE_MAX` in
the read loop, driving an out-of-bounds write past `wsh->buffer`.

Decode the full 64-bit length in network byte order and reject any value
that cannot fit the buffer with an unsigned comparison before narrowing
to `issize_t`, so a truncated or oversized length can no longer yield a
negative `plen` or pass the guard.

`ws_write_frame()` had the symmetric defect: it encoded the 64-bit length
field with a 32-bit `htonl()`, mis-framing any payload large enough to
use the 8-byte (127) length field. Emit the full 8 bytes there too.

Factor the byte assembly into `ws_get_be64()` / `ws_put_be64()` helpers.
2026-08-08 18:27:47 +03:00
Dmitry Verenitsin fbac12b451 Merge commit from fork
The nightmare-transfer path built its `switch_ivr_originate()` dial string by
interpolating the raw `Refer-To` URI params/headers into a
`{sip_invite_params=...}` brace block with `switch_core_sprintf()`.
`switch_ivr_originate()` splits a brace block on commas into separate channel
variables, so a comma in the remote-supplied URI params/headers was treated as a
variable separator and could set additional channel variables on the originated
leg.

Set `sip_invite_params` on the originate `ovars` event
(`nightmare_xfer_helper->vars`) instead. Values in that event are applied to the
peer channel verbatim and are never parsed for separators, so a comma stays
inside the single variable value. The composed value is unchanged
(`url_params?url_headers` when headers are present, `url_params` otherwise) and
`sofia_glue` reads `sip_invite_params` on the outbound leg as before, so a
well-formed REFER yields an identical outbound INVITE.

Drop the now-redundant `exten_with_params` helper field;
`nightmare_xfer_thread_run()` and the log use the plain
`nightmare_xfer_helper->exten` dial target directly.
2026-08-08 18:25:29 +03:00
Dmitry Verenitsin fb9f37b9c7 Merge commit from fork
Bound the XOR unmasking loop to `wsh->rplen` (payload only) instead of
`wsh->datalen`, which also covers the header and caused up to a 14-byte
OOB write past the frame payload in `wsh->buffer` using the
client-supplied mask key.

Tighten the size guard from `>` to `>=` to reserve 1 byte for the
trailing NUL written after the payload; a frame filling `buflen` exactly
otherwise NUL-wrote 1 byte past `wsh->buffer`.

Only reachable when `enable-websocket` is set in `mod_xml_rpc.conf.xml`
(off by default).
2026-08-08 18:22:30 +03:00
Dmitry Verenitsin 30cdb387ae Merge commit from fork
The positive-offset branch of `${variable:offset}` expansion advanced
`sub_val` past the cloned heap buffer without bounds-checking `offset`
against `strlen(sub_val)`. Subsequent `strlen()` and `strcat()` then
read adjacent heap memory into the output buffer.
2026-08-08 18:18:50 +03:00
ea74c0acb8 Merge commit from fork
* [Core, mod_commands] Interface allowlist (#3086)

* [core] Add interface allowlist to gate module app/api registration

Adds an optional, presence-activated allowlist in switch.conf.xml that
controls which modules may register application / api / json_api /
chat-application interfaces. With no <interface-allowlist> configured
nothing is enforced; when at least one <allow> entry is present, only
listed interfaces register at load time and all others are refused (the
module still loads and switch_loadable_module_process still returns
SUCCESS -- the blocked interface is simply never exposed).

Entries match at three levels of precision:
  mod_commands            - whole module
  mod_commands.system     - any interface named "system"
  mod_commands.system.api - a specific type (app|api|json_api|chat_app)

Enforcement lives in switch_loadable_module_process() so every module, at
boot and at runtime `load`, is subject to the same policy. This gives
operators a way to disable the "system"/"spawn" shell-exec API commands
(and equivalents) system-wide.

Also adds the `interface_allowlist_dump [modules] [plain]` API, which
walks the loaded modules and prints their interfaces in the allowlist key
format so the current state can be captured and pruned offline into config.

* [mod_commands] Add tests for the interface allowlist

New test_interface_allowlist boots the core with an active
<interface-allowlist> (conf_interface_allowlist/) that permits only a
couple of mod_commands interfaces, then loads mod_commands and verifies:

- listed commands register and run (status, version) while unlisted and
  shell-exec commands are refused (system, spawn, uptime) -- refusal
  surfaces as switch_api_execute returning FALSE / command-not-found,
  with the command function never invoked;
- a "module.name.type" entry gates by type: the API "status" loads while
  the JSON API of the same name stays blocked;
- interface_allowlist_dump prints the config format in its xml, modules
  and plain variants, and reflects module capabilities (system appears in
  the dump even though it was blocked from registering).

* [config] Fix interior -- in interface-allowlist comment breaking XML parse

The explanatory comment used -- as em-dash pairs. The XML parser treats
-- inside a comment as the comment close, causing an "unclosed <!--"
error that prevents the whole freeswitch.xml from parsing (boot and
reloadxml both fail). Replace the -- pairs with ordinary punctuation.

* update .gitignore

* [core] Warn when interface-allowlist section is present but parses no entries

Co-authored-by: Chris Rienzo <chris@signalwire.com>

* Merge commit from fork

Add `switch_stun_packet_verify_integrity()`, an HMAC-SHA1
MESSAGE-INTEGRITY verifier that is const and non-mutating: it runs
over a private copy of the pristine network-order packet, so the
caller's buffer and byte order stay untouched, and walks attributes
with its own unsigned bounded helper `stun_wire_attr_bounds()`
instead of the host-order iterator macros. A trailing
MESSAGE-INTEGRITY-SHA256 or FINGERPRINT after MESSAGE-INTEGRITY is
tolerated; any other trailing attribute is rejected.

Gate it in `handle_ice()` behind `ice->verify_integrity`: verify
before any ICE state is touched, keyed by message type (local
`ice->pass` for a request, remote `ice->rpass` for a response or
error response), and drop on failure. Keepalive indications carry no
MESSAGE-INTEGRITY and are ignored.

`ice->verify_integrity` is read from the `ice_verify_message_integrity`
channel variable in `switch_rtp_activate_ice()` and defaults off, so
receive-path behavior is unchanged unless it is enabled. Adds unit
tests in `tests/unit/switch_stun.c`.

* Merge commit from fork

* [core] Verify DTLS client cert against SDP fingerprint (server role)

Add opt-in verification of the client certificate when FreeSWITCH is
the DTLS server, mirroring the binding the client role already performs
on the server certificate: match the peer certificate against the SDP
`a=fingerprint` in `dtls_state_setup()`.

Selected per call by the `rtp_dtls_client_cert_verify_mode` channel
variable (`dtls_client_cert_verify_t`). An unset variable keeps the
default `DTLS_CLIENT_CERT_VERIFY_NONE`, so existing behavior is
unchanged:

- `none`: the server does not request a client certificate.
- `fingerprint`: request it (`SSL_VERIFY_PEER` + `dtls_accept_any_cert`)
  and require its fingerprint to match the SDP value; a self-signed
  certificate is accepted at the TLS layer and the match provides
  authenticity.
- `full`: additionally let OpenSSL enforce the certificate chain.

An unrecognized mode string falls back to `fingerprint` (fail closed)
with a warning.

The mode is set per `SSL` object in `switch_rtp_add_dtls()`. Verification
fails (`DS_FAIL`, no SRTP keys derived) when the client presents no
certificate, its fingerprint does not match, or the peer advertised no
usable `a=fingerprint`: `get_evp_by_name()` returns `NULL` for a missing
or empty hash type and `switch_core_cert_extract_fingerprint()` rejects a
`NULL` algorithm rather than passing it to `X509_digest()`.

`conf/vanilla/vars.xml` documents the knob as a disabled example. Tests
in `tests/unit/switch_rtp.c` cover matching, mismatched, absent-cert,
absent-fingerprint, `none`, and unrecognized-mode cases.

---------

Co-authored-by: Andrey Volk <andywolk@gmail.com>
Co-authored-by: Chris Rienzo <chris@signalwire.com>
2026-08-08 18:14:08 +03:00
Dmitry Verenitsin a097e12421 Merge commit from fork
`read_packet()` passed a peer-supplied `Content-Length` straight to
`switch_zmalloc(body, clen + 1)`. Huge values drove `calloc` failure
and `switch_zmalloc` `abort()`-ed the daemon.

- Cap `Content-Length` at 16 MiB; reject negatives.
- Destroy the partially-built `*event` at the new rejection site and
at the existing body-recv failure path so callers don't leak it.
- Add `test_mod_event_socket` covering `INT_MAX`, above-cap,
negative, `atoi`-overflow, zero, and valid-non-zero-body cases.
2026-08-08 17:48:05 +03:00
Jakub Karolczyk a38cd80733 Merge commit from fork
* [core] Add protection for RTP inject DoS

* [core] Wipe malicious packet out

* [core] Introduce rtp_auto_adjustment_wait_for_advertised_ms chanvar to keep the auto-adjustment window opened for X ms or until packet from the source IP advertised in the SDP is received

* [core] Perform auto adjustment logic before bytes can be zeroed by flush

* [core] Add DDoS protection for auto-adjustment window with configurable threshold of packets per ptime from non-advertised source to be rejected. Should be carefully used in bursty environments. Disabled by default.

* [core] Track auto-adjust packets-per-ptime per source IP; wipe and yield CPU on rate-reject of flooding sources

* [core] Harden RTP per-source rate-reject: O(1) LRU eviction, dynamic age window, timer-independent ptime, size guards
2026-08-08 17:10:31 +03:00
Dmitry Verenitsin 587474a158 Merge commit from fork 2026-08-08 17:07:32 +03:00
Dmitry Verenitsin 68ec688eb6 Merge commit from fork
Add `switch_stun_packet_verify_integrity()`, an HMAC-SHA1
MESSAGE-INTEGRITY verifier that is const and non-mutating: it runs
over a private copy of the pristine network-order packet, so the
caller's buffer and byte order stay untouched, and walks attributes
with its own unsigned bounded helper `stun_wire_attr_bounds()`
instead of the host-order iterator macros. A trailing
MESSAGE-INTEGRITY-SHA256 or FINGERPRINT after MESSAGE-INTEGRITY is
tolerated; any other trailing attribute is rejected.

Gate it in `handle_ice()` behind `ice->verify_integrity`: verify
before any ICE state is touched, keyed by message type (local
`ice->pass` for a request, remote `ice->rpass` for a response or
error response), and drop on failure. Keepalive indications carry no
MESSAGE-INTEGRITY and are ignored.

`ice->verify_integrity` is read from the `ice_verify_message_integrity`
channel variable in `switch_rtp_activate_ice()` and defaults off, so
receive-path behavior is unchanged unless it is enabled. Adds unit
tests in `tests/unit/switch_stun.c`.
2026-08-08 17:03:47 +03:00
Andrey VolkandChris Rienzo c1bb5c6ab3 [Core, mod_commands] Interface allowlist (#3086)
* [core] Add interface allowlist to gate module app/api registration

Adds an optional, presence-activated allowlist in switch.conf.xml that
controls which modules may register application / api / json_api /
chat-application interfaces. With no <interface-allowlist> configured
nothing is enforced; when at least one <allow> entry is present, only
listed interfaces register at load time and all others are refused (the
module still loads and switch_loadable_module_process still returns
SUCCESS -- the blocked interface is simply never exposed).

Entries match at three levels of precision:
  mod_commands            - whole module
  mod_commands.system     - any interface named "system"
  mod_commands.system.api - a specific type (app|api|json_api|chat_app)

Enforcement lives in switch_loadable_module_process() so every module, at
boot and at runtime `load`, is subject to the same policy. This gives
operators a way to disable the "system"/"spawn" shell-exec API commands
(and equivalents) system-wide.

Also adds the `interface_allowlist_dump [modules] [plain]` API, which
walks the loaded modules and prints their interfaces in the allowlist key
format so the current state can be captured and pruned offline into config.

* [mod_commands] Add tests for the interface allowlist

New test_interface_allowlist boots the core with an active
<interface-allowlist> (conf_interface_allowlist/) that permits only a
couple of mod_commands interfaces, then loads mod_commands and verifies:

- listed commands register and run (status, version) while unlisted and
  shell-exec commands are refused (system, spawn, uptime) -- refusal
  surfaces as switch_api_execute returning FALSE / command-not-found,
  with the command function never invoked;
- a "module.name.type" entry gates by type: the API "status" loads while
  the JSON API of the same name stays blocked;
- interface_allowlist_dump prints the config format in its xml, modules
  and plain variants, and reflects module capabilities (system appears in
  the dump even though it was blocked from registering).

* [config] Fix interior -- in interface-allowlist comment breaking XML parse

The explanatory comment used -- as em-dash pairs. The XML parser treats
-- inside a comment as the comment close, causing an "unclosed <!--"
error that prevents the whole freeswitch.xml from parsing (boot and
reloadxml both fail). Replace the -- pairs with ordinary punctuation.

* update .gitignore

* [core] Warn when interface-allowlist section is present but parses no entries

Co-authored-by: Chris Rienzo <chris@signalwire.com>
2026-08-05 18:44:33 +03:00
Andrey Volk a75491a318 Bump sofia-sip library requirement to version 1.13.18 (#3093) 2026-08-04 15:50:24 +03:00
Andrey Volk 1d77a57750 [SpanDSP] Update to v3.1.1 and fix build on Windows. (#3090) 2026-08-02 20:33:59 +03:00
Andrey Volk 0a54a48f37 [mod_lua] Fix build on MacOS (#3087) 2026-07-17 23:41:38 +03:00
Andrey Volk 95fad5ce1c [Build-System] Update gawk to 3.1.8 on Windows. (#3085) 2026-07-16 20:47:25 +03:00
Andrey Volk 2cae9b9aa2 [Build-System] Update SQLite to 3.53.3 on Windows. (#3084) 2026-07-16 20:21:00 +03:00
Andrey Volk 8020f5f2e7 [Build-System] Wix: Show progress when signing modules on Windows. 2026-07-14 17:19:05 +03:00
Andrey Volk d5f9409e9d [libvpx] Windows: Use NASM instead of Yasm 2026-07-10 21:05:00 +03:00
Andrey Volk 40ccfdb051 Merge pull request #3073 from signalwire/vpx1120
[libvpx] Update to v1.12.0
2026-07-10 20:13:24 +03:00
Andrey Volk 4939e86013 [Core] Introduce switch_image.c 2026-07-10 19:38:46 +03:00
Andrey Volk e5cacd7644 [libvpx] add yield to vpx 2026-07-10 19:38:46 +03:00
Andrey Volk 97ceaf43b2 [libvpx] Fix threading. 2026-07-10 19:38:46 +03:00
Andrey Volk 5136b60544 [libvpx] Fix dead nested assignments 2026-07-10 19:38:46 +03:00
Andrey Volk eb124e409b [libvpx] scan-build: avoid dereference of null pointer 2026-07-10 19:38:46 +03:00
Andrey Volk b41f55b33a [libvpx] scan-build: prevent division by zero in vpx_int_pro_row_c() 2026-07-10 19:38:46 +03:00
Andrey Volk d0af9bf4a3 [libvpx] scan-build: Assigned value is garbage or undefined 2026-07-10 19:38:46 +03:00
Andrey Volk 9ffb37a352 [libvpx] scan-build: fix false-positive dereference of null pointer 2026-07-10 19:38:46 +03:00
Andrey Volk 2f4160e024 [libvpx] scan-build: Fix "Result of operation is garbage or undefined" in vp9/encoder/vp9_rd.c 2026-07-10 19:38:46 +03:00
Andrey Volk 6c510d60bc [libvpx] scan-build: Fix "Assigned value is garbage or undefined" in vpx_post_proc_down_and_across_mb_row_c() 2026-07-10 19:38:46 +03:00
Dragos Oancea c27534c89a [libvpx] scan-build: Division by zero - measure_square_diff_partial() 2026-07-10 19:38:46 +03:00
Andrey Volk f4c9499198 [libvpx] scan-build: Fix "Dereference of null pointer" in vp8_peek_si_internal 2026-07-10 19:38:46 +03:00
Andrey Volk 51742eed1e [libvpx] Fix pthread configure checks: "Null pointer passed as an argument to a 'nonnull' parameter" 2026-07-10 19:38:46 +03:00
Andrey Volk b5046d2a76 [libvpx] Update to v1.12.0 2026-07-10 19:38:46 +03:00
Andrey Volk 287a71dc66 [GHA] Fix macos workflow 2026-07-10 19:38:13 +03:00
Andrey Volk 2d85032cef [mod_v8_skel] Use libnode instead of libv8 on Windows. 2026-07-04 13:23:34 +03:00
Andrey Volk 0615f8f3fa [Build-System] Windows: Download 7z, icsharpcode/SharpZipLib from GitHub instead of files.freeswitch.org during the build. 2026-07-03 22:45:13 +03:00
Andrey Volk 2096190199 [mod_av] Use pre-compiled FFmpeg 7.1.5 binary on Windows 2026-07-03 21:57:07 +03:00
Andrey Volk 225a5e197e [Build-system] Migrate music/sounds from files.freeswitch.org to https://github.com/freeswitch/freeswitch-sounds/releases (#3062) 2026-06-30 23:33:14 +03:00
Andrey Volk b9c41705b7 [mod_codec2] Use pre-compiled libcodec2 package on Windows. (#3058) 2026-06-23 23:25:55 +03:00
Andrey Volk 390444bc42 [mod_sofia] Fix broken sip: prefix check in deflect handler (#3057) 2026-06-12 19:31:01 +03:00
Ahron Greenberg (agree) 174b16a3d1 [mod_amqp] prevent segfault on double connection close
When a command response publish failed, `mod_amqp_command_response` closed
the connection and cleared conn_active. The command thread teardown then
called mod_amqp_connection_close(NULL), causing a segfault.

Also fix amqp_error_string2() calls to pass status codes without erroneous negation.
2026-06-12 17:53:40 +03:00
Andrey Volk be554e4a08 [Core] switch_sockaddr_info_get() will not resolve if the hostname is an IP address. Add new switch_is_ip_address() API. Add a unit-test. (#3055) 2026-06-12 16:13:47 +03:00
Andrey Volk 2e9b721d43 [mod_v8] Use pre-compiled libnode 20.19.2 binary instead of custom v8-6.1 lib on Windows. (#3053) 2026-06-11 03:00:34 +03:00
Andrey Volk f31b902a0a [mod_v8] Use upstream libnode-dev instead of custom libv8-6.1-dev on Linux. Enable for ARM64 and armhf. (#3052) 2026-06-11 01:38:53 +03:00
Dmitry Verenitsin 153c3a5139 [mod_lua] Move SWIG wrapper patches into ".i" typemaps, drop hack.diff (#3043)
A swig 4.1 regeneration dropped several hand-patched wrapper edits.
Express them (and the rest) as SWIG typemaps in `freeswitch.i` so they
survive reswig. `make reswig` now produces the final wrapper directly;
`hack.diff` and its `patch` step are removed.

Restored regressions:
- `setLUA(L)` on returned `Session`: a hangup hook or input callback on
a script-created `freeswitch.Session()` no longer crashes the process
- binary-safe `Stream::read` (`lua_pushlstring`)

Also moved to typemaps:
- `Dbh`/`JSON` self-pointer guards (`%typemap(check)`), now covering
every wrapper including four the hand-patch missed
- type-table isolation (`#define SWIG_TYPE_TABLE mod_lua`)
2026-05-28 01:34:53 +03:00
Serhii Ivanov 077393376a [GHA] Add source tarball generation workflow (#3019)
* [GHA] Add source tarball generation workflow

* [GHA] Add manual dispatch with ref input and artifact upload to tarball workflow
2026-05-27 12:01:12 +03:00