Tobias Brunner [Mon, 29 Jun 2026 12:03:45 +0000 (14:03 +0200)]
eap-ttls: Add an upper bound for the length of the parsed AVP
On typical systems that have overcommitting enabled, the 16 MiB maximum
that's allocated via the 24-bit length field in the AVP header shouldn't
be an issue as there are various limits that affect how much data can
actually be written to the allocated buffer (e.g. the maximum IKE message
size, the maximum TLS record size, or the maximum number of accepted
EAP-TTLS payloads), so this is primarily a defense-in-depth measure.
The length field of an EAP payload, which is the only type of AVP we
accept, is 16 bits, so that's what we now enforce as maximum.
Tobias Brunner [Thu, 25 Jun 2026 16:56:23 +0000 (18:56 +0200)]
mediation-manager: Avoid potential use-after-free when checking online status
This is unlikely to be an issue in practice because only one caller
actually uses the ID and it does so immediately afterwards. So there is
only a tiny window in which the peer could terminate or rekey its SA to
cause the returned ID to get destroyed.
Fixes: d5cc1758332e ("experimental P2P-NAT-T for IKEv2 merged back from branch")
Tobias Brunner [Thu, 25 Jun 2026 16:21:14 +0000 (18:21 +0200)]
forecast: Fix undefined shift if traffic selector is /0
Not an issue in typical scenarios where the plugin is used on a gateway
with roadwarriors that use /32 addresses. But could be an issue if used
on a client that tunnels everything to the gateway.
Fixes: e5ad2e66146c ("forecast: Add the broadcast/multicast forwarding plugin called forecast")
Tobias Brunner [Thu, 25 Jun 2026 09:31:09 +0000 (11:31 +0200)]
child-create: Fix crash when responder doesn't send TS payloads
There are multiple paths that can trigger a crash. One is if transport
mode is negotiated via NAT and `get_transport_nat_ts()` is called to
substitute addresses in the received TS. If that's not the case,
`narrow_ts()` will select the configured TS and continue. Then,
`narrow_and_check_ts()` eventually attempts to destroy `this->tsi/tsr`,
which triggers a crash in any case.
The referenced commit refactored `select_and_install()` into
`narrow_and_check_ts()` and `install_child_sa()`. It move a check
for TS from that function to only `build_r()`.
Fixes: d7760416d62d ("child-create: Add support for multiple key exchanges")
Tobias Brunner [Wed, 24 Jun 2026 16:54:04 +0000 (18:54 +0200)]
sqlite: Fix transaction handling for multiple concurrent threads
Due to the shared database connection, the previous code, while tracking
transaction metadata per thread, didn't actually enforce that separation
on the database level. Which basically meant the transactions created
by multiple threads were shared.
This change uses an approach similar to the mysql plugin, using a pool of
connections. However, we always use thread-specific connections, not
only during transactions. That's because the implicit transactions
that are active in SQLite during queries block further queries from
other connections while enumerating (the pool utility uses such patterns).
It also fixes the issue that calling `rollback()` on the outer-most
transaction didn't have an effect.
Since it's very unlikely SQLite was built in single-thread mode and
handling that properly would require locking the mutex during
transactions, we remove that locking and move the check to the constructor
to refuse initialization.
Before, `ref_cur()` used RELAXED memory ordering, which is sufficient
for diagnostic reads but provides no ordering guarantees against
concurrent `ref_put()` operations on other threads. Since `ref_put()`
already uses ACQ_REL ordering, readers should use ACQUIRE ordering
so that observing a given refcount value (particularly zero) also
makes all prior stores by the releasing thread visible.
There is no significant performance impact as on x86 ACQUIRE loads
compile to the same instruction as RELAXED loads. But this fixes
potential issues on weakly-ordered architectures (e.g. ARM).
The __sync* and spinlock fallbacks already provide full ordering (they
might not actually be necessary anymore nowadays).
Tobias Brunner [Wed, 24 Jun 2026 13:34:01 +0000 (15:34 +0200)]
kernel-wfp: Uninstall policies before deleting provider
The policies reference the provider, so it might not actually get removed
and be left dangling in the WFP system (maybe Windows refcounts it and
still removes it).
Tobias Brunner [Wed, 24 Jun 2026 09:43:47 +0000 (11:43 +0200)]
connect-manager: Ignore connectivity checks for incomplete checklists
This could cause a crash due to signature verification with missing
key (although, memcpy() likely just returns with length 0), but it
also makes the signature predictable as the key is omitted.
Fixes: d5cc1758332e ("experimental P2P-NAT-T for IKEv2 merged back from branch")
Tobias Brunner [Wed, 24 Jun 2026 09:15:39 +0000 (11:15 +0200)]
systime-fix: Unregister validator also if time got valid
This avoids accepting expired certificates again should the time get
rolled back for some reason.
Fixes: c81b87ac265e ("systime-fix: Add timeout option to stop waiting for valid system time") Fixes: 295e42a47f9a ("systime-fix disables certificate lifetime validation if system time not synced")
Tobias Brunner [Tue, 23 Jun 2026 17:11:06 +0000 (19:11 +0200)]
xauth-pam: Properly initialize response array when handling sessions
This is documented as a requirement in the man page, so the caller of the
conversation function might expect such an array. Although, since we
only accept non-interactive requests, for which no response is expected,
it's doubtful whether it will actually try to access elements in the
array. And if the passed pointer was initialized to NULL, passing it to
free() is probably also fine.
Fixes: 2312504d1ef5 ("xauth-pam: Open/close a PAM session for each connected client")
Tobias Brunner [Tue, 23 Jun 2026 14:09:07 +0000 (16:09 +0200)]
stream-service: Avoid race condition when accepting sockets
Even if `poll()` indicates that the socket is ready it might block if
it's in blocking mode. This change avoids blocking in such cases (accept
will fail with EAGAIN/EWOULDBLOCK and `watch()` will return TRUE).
As the non-blocking mode is inherited on Windows (on Linux, the man page
documents the non-inheritance as a Linux specialty), we set the mode for
the accepted socket explicitly to blocking to match the expectations of
`stream_t`.
Fixes: daf1880b3947 ("stream: add a stream service class abstracting services using BSD sockets")
Tobias Brunner [Tue, 23 Jun 2026 12:35:36 +0000 (14:35 +0200)]
pkcs11: Fix ECDH derivation
The referenced commit moved the key derivation to `get_shared_secret()`
and broke the handling of ECDH public value as the copied struct now
referred to a buffer allocated on the stack.
Also fixes potential session leaks if generating key pairs fails.
Fixes: 26ca0c9f70ed ("pkcs11: Move shared secret calculation to get_shared_secret()")
Tobias Brunner [Mon, 22 Jun 2026 17:02:28 +0000 (19:02 +0200)]
asn1: Reject OIDs with too large sub-identifiers when converting to string
The shift would overflow the value which could produce garbage output
that might get interpreted as real OIDs (in case strings are compared).
This limit allows OID sub-identifiers to consist of at most 4 bytes,
which should be enough for any real-world OIDs (it's also the maximum we
used in tests so far).
Tobias Brunner [Mon, 22 Jun 2026 15:30:11 +0000 (17:30 +0200)]
constant-time: Add 64-bit versions of the helpers
While we could use _Generic() C11 expression to let the compiler select
between the different versions, this only allows selection based on one
of the arguments, which seems a bit fragile. So make this explicit for
now. In the future we might consider using the overloadable attribute.
Tobias Brunner [Fri, 19 Jun 2026 15:29:12 +0000 (17:29 +0200)]
tls-server: Fix memory leaks when processing certificates or Finished messages
Fixes: 7fbe2e27ecf6 ("tls-server: TLS 1.3 support for TLS server implementation") Fixes: d2fc9b0961c6 ("tls-server: Mutual authentication support for TLS 1.3") Fixes: 760f3b730fa6 ("tls-server: Add flag that makes client authentication optional")
Tobias Brunner [Fri, 19 Jun 2026 16:48:49 +0000 (18:48 +0200)]
connmark: Fix addresses when removing policies in ike_update() handler
These were never correct. And the first referenced commit, unfortunately,
just simplified the incorrect code.
Fixes: 08a3ee0cce1b ("bus: Change ike_update() signature and only call it once") Fixes: b8973b266131 ("connmark: Add CONNMARK rules to select correct output SA based on conntrack")
Tobias Brunner [Fri, 19 Jun 2026 15:12:47 +0000 (17:12 +0200)]
af-alg: Fix output offset if not all data was processed during en-/decryption
If only parts of the total data could be written to the kernel, the result
of the next read chunk would incorrectly get written at the beginning of
the output buffer again.
Also makes sure to close the accepted FD in error cases.
Fixes: 1b5de7ce3bdd ("Use a generic AF_ALG wrapper for common operations")
Tobias Brunner [Fri, 19 Jun 2026 15:00:53 +0000 (17:00 +0200)]
dhcp: Make sure to only modify transactions after receiving an expected DHCP OFFER
The `transaction` variable was previously still set to the one that
was enumerated last even if the received DHCP OFFER did not match
it. So the transaction was incorrectly modified. Once the real
DHCP OFFER would arrive, it would get modified again, but it could
allow adding unintended DNS servers.
Tobias Brunner [Fri, 19 Jun 2026 13:08:51 +0000 (15:08 +0200)]
charon: Avoid potential TOCTOU issues when accessing/writing PID file
The previous code could potentially truncate and change ownership of
a file that's a symlink to an unintended target file (requires the
attacker to be able to create the symlink in the directory the PID file
is located, which generally requires root privileges).
Tobias Brunner [Fri, 19 Jun 2026 10:33:01 +0000 (12:33 +0200)]
controller: Properly notify waiting thread after INVALID_KE_PAYLOAD retry failed
When initiating a Child SA with a CREATE_CHILD_SA exchange, the initial
exchange could fail with an INVALID_KE_PAYLOAD notify. The initiation
would then get retried from scratch (the `child_sa_t` object is destroyed
and a new one created). If it fails again, the previous code didn't
signal the waiting thread. If it did not set a timeout, it would wait
indefinitely.
Using a different `status_t` value avoids any confusion with NEED_MORE
that's used to indicate that the callback returned FALSE (e.g. if the
IKE_SA would go down before concluding the retry).
Fixes: c39b0c883dc6 ("controller: Keep following initiate() if the first DH guess was wrong")
Andreas Steffen [Fri, 19 Jun 2026 13:28:22 +0000 (15:28 +0200)]
pts: Verify PCR info during verification and reject mismatches
Replace `set()` with `extend()` in verify methods so the PCR value is
independently computed rather than taken from the evidence.
Also make `pcr_before` mismatch a hard failure instead of a warning.
Tobias Brunner [Wed, 17 Jun 2026 16:42:06 +0000 (18:42 +0200)]
identification: Avoid truncating identities created from data blobs
This is not necessarily an issue, but we should avoid not using the
full identity data as best as possible. The change also avoids the
dynamically sized buffer on the stack.
Fixes: 324528700d98 ("Added identification constructor using a chunk of data, guessing id type")
Tobias Brunner [Wed, 17 Jun 2026 16:12:12 +0000 (18:12 +0200)]
revocation: Avoid that a skipped CRL lookup/fetch prevents fetching delta CRLs
If we find a stale CRL in the cache and finding a newer one via
CRLIssuer fails for some reason, the validation state would get
overwritten with VALIDATION_SKIPPED. This would then prevent
fetching delta CRLs.
Fixes: 7d7beaa1fabf ("Use certificate CRLIssuer information to look up cacched CRLs or CDPs")
Tobias Brunner [Wed, 17 Jun 2026 13:37:39 +0000 (15:37 +0200)]
eap-peap: Don't allow authenticated client to skip EAP-TNC with unexpected EAP-SUCCESS
EAP-PEAP transports results as separate AVPs, we expect one after the
first phase 2 authentication (if not using certificates). However, with
the previous caching of the result of that, the client could then send
another confirmation as response to the EAP-TNC request to skip it.
Since no group membership is set if EAP-TNC is not complete, it shouldn't
really affect its outcome, though.
Fixes: 1be296dfb2af ("implemented the PEAP tunneling protocol as an EAP plugin")
Tobias Brunner [Wed, 17 Jun 2026 12:53:51 +0000 (14:53 +0200)]
windows: Avoid unnecessarily locking the global thread lock when removing TLS
Holding the lock could potentially cause a deadlock depending the
behavior of the called cleanup functions. The TLS removal happens in
the context of the respective thread, so no locking is necessary.
Looks like removing these lines was missed when the referenced commit
partly reverted 204098a75202 ("thread-value: Immediately cleanup all
Windows TLS values on destroy"), which added the locking originally.
Fixes: 23750961d5b9 ("thread-value: Defer cleanup handling to thread termination on Windows")
Tobias Brunner [Thu, 25 Jun 2026 07:56:06 +0000 (09:56 +0200)]
tnc-pdp: Fix lock type when adding/replacing a connection
The entry was updated and the previous EAP method and IKE SA destroyed
under a read lock, which is not safe (there could be threads in
get_state(), or even some that called it before and now still hold
that read lock while interacting with the EAP method).
Fixes: 37884ab10f0f ("Add locking to TNC-PDP connections")
Tobias Brunner [Tue, 16 Jun 2026 16:34:39 +0000 (18:34 +0200)]
cert-cache: Fix race conditions when inserting CRL/OCSP
Because no lock was held while comparing the type of the already stored
cache entry, it was theoretically possible that a NULL-pointer dereference
or use-after-free was caused if another thread concurrently replaced the
stored certificate.
While the cached type is also read without lock, the worst that can
happen is that we acquire the lock of an entry that doesn't match
eventually. The double check actually uses the stored certificate,
just in case the cached value gets out of sync due to a future
refactoring.
Fixes: 2271ebb3258f ("Newer CRLs replace older versions of the CRL in the cache") Fixes: 47e8b21c7636 ("cert_cache: Replace cached stale OCSP responses in-place")
Tobias Brunner [Tue, 16 Jun 2026 15:59:05 +0000 (17:59 +0200)]
tls-protection: Reject unencrypted TLS 1.3 records
We only allow unencrypted change_cipher_spec records (as before), which
are handled by the upper layers accordingly. Without the check, we
also accepted unprotected alerts and handshake records that could
potentially cause state confusion.
Fixes: 7a2b02667c30 ("libtls: Implement TLS 1.3 handshake on client-side")
Tobias Brunner [Tue, 16 Jun 2026 07:01:13 +0000 (09:01 +0200)]
eap-aka-3gpp: Fix SQN generation
As `tv_sec` is a `time_t`, i.e. typically 64 bits, assigning the result
of `htonl()` leaves the upper 32 bits zero. Copying from the
`sizeof(time_t) - 4` offset then copies those zeroes on little-endian
systems, which is not what was intended according to the comments.
Another issue was that the type of `tv_usec`, `suseconds_t`, is typically
a `long`, i.e. signed, so shifting the maximum value 0x000f423f (999'999)
by 12 bits technically overflows this. The cast fixes that.
Fixes: 1aba82bfd736 ("eap-aka-3gpp: Add plugin that implements 3GPP MILENAGE algorithm in software")
Tobias Brunner [Mon, 15 Jun 2026 06:42:57 +0000 (08:42 +0200)]
eap-mschapv2: Remove retry after failed attempts
Due to the `sleep()` call, a continuous stream of failed authentication
attempts can potentially exhaust the thread pool. While delays can also
happen due to RADIUS or DHCP on the server side, this can directly be
triggered by clients. It's questionable whether the delay ever had the
intended effect. But since the retry functionality is also quite
strange, let's just remove it so retries will require creating an IKE_SA
from scratch. To avoid leaking much of a timing difference if no secret
is found now that the two second delay is gone, we do the calculations
with a dummy NT hash.
Fixes: f98cdf7a4765 ("adding plugin for EAP-MS-CHAPv2")
Tobias Brunner [Mon, 15 Jun 2026 14:51:46 +0000 (16:51 +0200)]
pt-tls-server: Properly fail if processing PB-TNC batches failed
Because `assess()` returns a `status_t`, `FALSE` is interpreted as
`SUCCESS`. So a failure while processing PB-TNC batches terminated
the process successfully.
Fixes: 1e92d5f1145d ("Process PB-TNC batches received via PT-TLS asynchronously")
Note that wolfSSL before 5.9.2 required building with
WOLFSSL_ECDHX_SHARED_NOT_ZERO, which was added with 5.3.0, to get and
explicit check. Since the plugin validates the public key, the test case
fails nonetheless.
Tobias Brunner [Fri, 12 Jun 2026 15:21:16 +0000 (17:21 +0200)]
coupling: Avoid potential access by multiple threads to shared hasher
Using the same `hasher_t` instance from different threads concurrently
is not safe. The underlying implementation might e.g. use a single
shared state for multiple API calls within `get_hash()` (e.g. the openssl
plugin does that).
Tobias Brunner [Mon, 15 Jun 2026 11:37:12 +0000 (13:37 +0200)]
wolfssl: Avoid potential RNG state corruption during RSA/ECDSA operations
The WC_RNG instances are potentially shared between different threads
as private key objects are refcounted. This may corrupt their internal
state as they are not thread-safe.
For ECDSA, using separate instances for each signing operation has some
performance impact, but for signature operations that should be fine.
The implementation for RSA uses mutexes. That's due to the weird API.
While RNG instances can be passed for signing and encryption (probably
because they are also required for padding/salt besides blinding), they
can't for verifying and decryption. The latter use an RNG instance that
has to be set on the key object before calling these operations. So we
could potentially split the strategy, but to keep this consistent within
the RSA implementation, just continue with the shared RNG but use a
mutex around the API calls.
Fixes: c92eade82c81 ("wolfssl: Add wolfSSL plugin for cryptographic implementations")
Tobias Brunner [Fri, 12 Jun 2026 14:23:40 +0000 (16:23 +0200)]
esp-packet: Fix leak and avoid one if AEAD implementations misbehave
If an `aead_t` implementation incorrectly allocates memory if the
decryption/ICV verification failed, this avoids a leak. Unfortunately,
many implementations actually did that.
Fixes: 24a8d1253fe4 ("libipsec: Wrap traditional algorithms in AEAD wrapper")
Tobias Brunner [Fri, 12 Jun 2026 11:08:00 +0000 (13:08 +0200)]
watcher: Remove unnecessary pending flag
This was added with 5ce3c9b15a57 ("watcher: Rebuild fdset when select()
fails"), i.e. before switching to poll(), solely to suppress errors when
FDs are closed and select() would return with an error. With poll()
this should not happen result in an error (it potentially indicates this
via POLLNVAL in revents of that FD in the array).
Because the flag was not consistently changed/read with the mutex held,
some analysis tools got confused and imagined wild deadlock scenarios.
Tobias Brunner [Thu, 11 Jun 2026 13:48:00 +0000 (15:48 +0200)]
tls-socket: Avoid accessing stale data when processing application data
In non-blocking mode, the previous code set `in_done` to -1 (SIZE_MAX)
if `recv()` would block and nothing was read yet. If this was followed
by a call to `write()` and `process()` is called and actually processed
application data, the length calculation in the callback underflows and
`memcpy()` would write to `in.ptr + SIZE_MAX`. Since `read()` already
sets `errno` to `EWOULDBLOCK` and returns -1 if `in_done` is 0, the
removed check was redundant anyway.
Also, the buffer from the previous `read()` call might not be valid
anymore when `write()` is called (e.g. `splice()` uses the same buffer
for both, and the buffer could even be defined on a now invalid stack
frame of the function that called `read()` previously). Clearing the
data avoids that and ensures the application data is cached until the
next call to `read()`.
However, triggering this is rather difficult as `write()` should only
reach `recv()` while the handshake isn't complete and until then
`process_application()` doesn't accept application data. But if the
handshake is completed during a call to `write()` that follows a
non-blocking read and data immediately arrives, it's theoretically
imaginable. This scenario is highly unlikely on a TLS server, which
starts the process with a call to `read()` that then basically loops
until the handshake is done. Even if multiple calls are required, the
server will generally not call `write()` before it received application
data. And any calls to `write()` afterwards do not reach `recv()`
anymore (unless no data to send was passed, which would be weird, or
maybe for some weird corner case that lets `build()` fail before all
outbound application data was processed).
This does not seem to work on these runners. Tried setting
dataflowMemoryLimit, but that only seems to affect the analysis (which
causes a warning: "Taint analysis was stopped early due to low memory.
The analysis may not have explored the whole program.") not the
post-processing that evidently causes the runner to get killed due to
OOM (there is no explicit error, but in the debug log we see "The runner
has received a shutdown signal", which apparently can indicate OOM
issues).
Because the CFLAGS applied to the whole plugin, the compiler could
"optimize" the boilerplate plugin code, which could then cause a SIGILL
on hardware that doesn't support such instructions. This change makes
sure only the actual AES implementation is compiled like that, which
would then not get registered depending on the CPU feature detection.
ha: Make receive buffer size for the HA socket configurable
If there are lots of SAs to be synced, the default might be too low
and messages and SAs get dropped. The new default is already 8 MiB,
which should work fine for lots of SAs. The code mirrors the one in
the kernel-netlink plugin (but with a guard around SO_RCVBUFFORCE, even
though this plugin is mostly used on Linux as well).
testing: Run test scripts in a subshell so we can properly cancel them
This fixes handling of SIGINT (CTRL+C) so we don't continue with the
next potentially hanging command (e.g. several pings after another that
fail because a required SA didn't come up correctly). We try to kill
the PID for every SIGINT in case the first one arrived before the
subshell was started. The `TDUP_<host>` variables are not updated in
the main shell when `stop_tcpdump` is called from the subshell, so
it is called redundantly during the cleanup (removed the useless guard
there and use pkill to suppress any messages if no process is found).
If we get interrupted during one of the init/cleanup WAIT_FOR waits,
we just let them running in the background when exiting the script.
They should generally not do any harm (and be terminated once the next
run is attempted, which might require a rebuild during development
anyway).
Since a103f3a2849f ("testing: Add options to only run pre- or posttest
scripts of a scenario") we can now also skip the remaining steps easily.
However, we do run the posttest script to clean up properly (e.g.
terminate the daemons, restore firewall rules etc.). That's also why
it's not running in a subshell.
Tobias Brunner [Mon, 29 Jun 2026 15:12:45 +0000 (17:12 +0200)]
github: Remove installation of unnecessary debug symbols for OpenSSL
These are apparently not necessary anymore and since there are often
sync issues (package version mismatch if security fixes are shipped
because there is no *-security suite in the ddebs repo), lets not
install them anymore.