Commit Graph
4201 Commits
Author SHA1 Message Date
Nikos Mavrogiannopoulos b37a2dd0de sec-mod: guard against NULL orig_remote_ip in auth init HMAC
protobuf-c sets absent string fields to NULL at runtime even when
marked required in the schema, contrary to the in-code comment.
Calling strlen(NULL) is undefined behaviour and crashes sec-mod.
A compromised worker could exploit this to deny authentication for
all connected clients.

Add an explicit NULL check before the strlen call, consistent with
the existing guard on the our_ip field.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 22a0c2d54c sec-mod: bound-check cli_addr.len before memcpy in TLS session store
A compromised worker could supply a cli_addr.len value larger than
sizeof(struct sockaddr_storage) (128 bytes), overflowing the fixed-size
remote_addr field in tls_cache_st and corrupting adjacent talloc heap
metadata inside the privileged sec-mod process.

Add the missing upper-bound check, consistent with the existing checks
on session_id.len and session_data.len in the same function.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 0db69e24f9 worker-tun: eliminate strict-aliasing UB in tun_write()
Accessing iph->ip_v via a struct ip* cast from void* is undefined
behaviour under C99/C11 strict-aliasing rules.  Replace the cast with a
direct byte read from the existing uint8_t pointer; the IP version is in
the high nibble of the first byte (finding 4.2).

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 175867ce71 worker-http: fix unsigned comparison with <= 0 in header_value_check()
req->value.length is size_t (unsigned); comparing it with <= 0 is
equivalent to == 0 but misleading.  Use == 0 to accurately reflect
the intent (finding 4.1).

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos f2795a10a9 worker-auth: guard against NULL msg->msg in recv_auth_reply()
msg->msg is a protobuf optional field and may be NULL.  Guard the
talloc_strdup call so a missing message field does not silently
propagate NULL to the caller (finding 2.2).

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 4c9e97fe2d worker-auth: document intentional protobuf message lifetime for user_config
ws->user_config borrows a pointer into the unpacked auth_cookie_reply
protobuf message.  The message must therefore stay alive for the entire
worker session and is only freed on the error path.  Clarify this
non-obvious ownership invariant with a comment so future readers do not
accidentally free msg on success (finding 1.2).

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 1acf13f0bd proxy-proto: fix TLV parsing loop skipping data pointer advance
Two continue statements in parse_ssl_tlvs() bypassed the
data += tlv.length update at the bottom of the loop:

- PP2_TYPE_SSL with tlv.length < sizeof(pp2_tlv_ssl): the body bytes
  were re-parsed as a new TLV header on the next iteration.
- PP2_TYPE_SSL_CN with an oversized length: same misbehaviour, and
  could allow a crafted proxy header to cause ws->cert_auth_ok to be
  set without a real client certificate being present.

Fix by advancing data and decrementing data_size within each branch
before continuing, and moving data += tlv.length into each branch so
the loop has no shared trailing update to be skipped. Also preserve
the full original TLV length for PP2_TYPE_SSL to consume the entire
body rather than only sizeof(pp2_tlv_ssl) bytes.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 071762674e proxy-proto: fix our_addr_len set to IPv4 size for IPv6 connections
When parsing a proxy protocol v2 header with AF_INET6, our_addr_len
was set to sizeof(struct sockaddr_in) (16) instead of
sizeof(struct sockaddr_in6) (28). Any socket API call passing
(our_addr, our_addr_len) for an IPv6 address would use a truncated
length, potentially reading uninitialised memory.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 8e6d81a83a worker: validate session_id length from IPC reply before memcpy
recv_cookie_auth_reply() copied msg->session_id into the fixed-size
ws->session_id buffer without checking that msg->session_id.len fits.
A malformed IPC message could overflow the buffer. Add the same length
guard that recv_auth_reply() already applies to dtls_session_id.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 464463c594 worker: limit HTTP request body to 256 KB to prevent memory exhaustion
An unauthenticated client could stream an arbitrarily large POST body,
causing the worker process to grow its heap without bound via repeated
talloc_realloc_size() calls in http_body_cb(). Add a 256 KB cap that
covers the largest legitimate payload (KKDCP Kerberos messages) while
rejecting oversized bodies early by returning an llhttp error code.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-16 22:46:41 +02:00
Nikos Mavrogiannopoulos 76a17dadac Merge branch 'del_build-aux' into 'master'
build: remove obsolete build-aux directory

See merge request openconnect/ocserv!519
2026-04-15 06:48:04 +00:00
Grigory Trenin 3af9edaa8e build: remove obsolete build-aux directory
This directory contained scripts (install-sh, compile, etc.) used
by Autotools. Since the project has migrated to Meson, these files
are no longer needed.

Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
2026-04-14 16:40:59 -04:00
Nikos Mavrogiannopoulos d3eceef085 Merge branch '64bit-timestamps' into 'master'
protobuf: use 64-bit signed integers for timestamp fields

See merge request openconnect/ocserv!506
2026-04-14 19:03:05 +00:00
Nikos Mavrogiannopoulos 468335a7cf Merge branch 'vhosts-no-udp' into 'master'
Allow using no-udp option in vhosts

Closes #680

See merge request openconnect/ocserv!516
2026-04-14 19:02:03 +00:00
Grigory Trenin cde58c22be protobuf: use 64-bit signed integers for timestamp fields
Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
2026-04-14 13:59:05 -04:00
Grigory Trenin db125634b8 Allow using no-udp option in vhosts
Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
2026-04-14 13:26:32 -04:00
Nikos Mavrogiannopoulos 4a0a087996 config: align default values with sample.config
Several options had hardcoded or named defaults in apply_default_conf()
that diverged from the values documented in doc/sample.config, leading
to silent behaviour differences for servers running without explicit
configuration.

Relates: #680

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-14 18:57:45 +02:00
Nikos Mavrogiannopoulos 611eb00527 config: add scope annotations and validation script
Add machine-readable [scope: X] annotations to doc/sample.config and
src/vpn.h struct fields to document which config options are permanent,
global-only, per-vhost, or per-user/group overridable.

Scope vocabulary:
  global (non-reloadable)  -- in perm_cfg_st; requires restart; cannot differ per vhost
  vhost (non-reloadable)   -- in perm_cfg_st; requires restart; can differ per vhost
  global            -- in cfg_st; reloadable; cannot be set in [vhost:] sections
  vhost             -- in cfg_st; reloadable; can differ per vhost
  vhost user        -- in cfg_st; reloadable; also overridable per user/group

Add tests/check-config-scope.py: a script that cross-checks the annotations
against the actual code:
  (a) every option in sample.config has a [scope:] annotation
  (b-c) [global] options match error_on_vhost() calls in config.c
  (d-e) [vhost user] options match handlers in src/sup-config/file.c
  (f) every field in cfg_st and perm_cfg_st has a [scope:] comment

Also fix a pre-existing bug in src/sup-config/file.c: the tunnel-all-dns
option was compared using an underscore ("tunnel_all_dns") instead of
the correct dash ("tunnel-all-dns"), silently ignoring the per-user
setting.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-14 18:57:45 +02:00
Nikos Mavrogiannopoulos 62a5f72864 DPD time: corrected duplicate assignment and set value for mobile-dpd
Resolves: #680

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-14 18:57:45 +02:00
Nikos Mavrogiannopoulos 0096ebcc2b config: convert warn_on_vhost to error_on_vhost
Setting a global-only option (e.g. rate-limit-ms, device, ban-time)
inside a [vhost:X] section previously produced a warning and silently
discarded the value, leaving administrators wondering why the setting
had no effect.

Replace warn_on_vhost() with error_on_vhost() which emits an ERRSTR
diagnostic and returns 0 from the ini handler.  Returning 0 causes
ini_parse() to record a parse error, which in turn causes
parse_cfg_file() to call exit(EXIT_FAILURE), aborting startup or a
SIGHUP reload.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-14 18:57:44 +02:00
Nikos Mavrogiannopoulos 05cf33adb7 tests: added tests for multiple ocpasswd options
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-13 22:43:52 +02:00
Nikos Mavrogiannopoulos 24d924f65c Merge branch 'tmp-typos' into 'master'
Fix typos

See merge request openconnect/ocserv!515
2026-04-10 04:31:08 +00:00
Nikos Mavrogiannopoulos db50c02cbd Merge branch 'tmp-radius-group-separator' into 'master'
radius: add group-separator option for OU= Class attributes

Closes #428

See merge request openconnect/ocserv!513
2026-04-10 04:29:39 +00:00
Nikos Mavrogiannopoulos 78e6a28ff5 Merge branch 'bugfix/vhosts' into 'master'
Fix virtual host global option inheritance

Closes #698

See merge request openconnect/ocserv!505
2026-04-10 04:28:52 +00:00
Grigory Trenin bf12b9fe9c Fix global option inheritance for virtual hosts
Ensure global options are properly copied to virtual hosts when not
explicitly specified.

This change also:
- Documents global options that cannot be overridden in virtual hosts.
- Adds a 'VIRTUAL HOSTS' section to the man page to clarify behavior.

Closes #698

Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
2026-04-09 20:20:45 -04:00
Dimitri Papadopoulos 21eed65a60 Fix typos
Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com>
2026-04-09 21:49:54 +02:00
Nikos Mavrogiannopoulos 89a40dde31 tests: add radius-multi-group-comma test for group-separator=comma
Regression test for the group-separator=comma option: verifies that
OU= Class attributes with comma-separated group names (as sent by
Freeradius) are correctly parsed when group-separator=comma is set.

Relates: #428

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-06 21:27:28 +02:00
Nikos Mavrogiannopoulos 828fca691e radius: add group-separator option for OU= Class attributes
Freeradius deployments may send groups in the Class attribute using a
comma as separator (e.g. "OU=group1,group2") rather than the semicolon
that ocserv expects by default. Add a group-separator option to the
radius auth configuration to allow this to be changed:

  auth = "radius[config=...,group-separator=comma]"

Accepted values are 'semicolon' (default) and 'comma'. The literal
character is not accepted in the config syntax as it conflicts with the
key=value pair delimiter.

Resolves: #428

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-06 21:27:28 +02:00
Nikos Mavrogiannopoulos ab23290899 .gitignore: added .cache
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-06 21:26:58 +02:00
Nikos Mavrogiannopoulos 95c8207e07 auth/plain: use real passwd salt for timing normalisation and clear on deinit
Instead of always using a hardcoded $5$fakesalt$ when the requested user
is not found, read_auth_pass() now captures the salt from the first entry
in the passwd file whose password field starts with '$'. This ensures the
crypt() call in plain_auth_pass() uses the same algorithm as real entries,
preventing observable timing differences that could leak username existence.
Falls back to $5$fakesalt$ when no usable entry is found.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-06 21:12:38 +02:00
Nikos Mavrogiannopoulos 2e81af318e strsep: removed conditional code for strsep
strsep() was used conditionally in plain authentication with fallback
code, whereas radius code used it unconditionally. Rely on strsep()
unconditionally to simplify the code.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-06 21:02:06 +02:00
Nikos Mavrogiannopoulos 85b4d19f0a tests: check that empty password with correct OTP fails when password is set
Regression test: a user with both a password and OTP configured must not
be able to authenticate by supplying an empty password (even with the
correct OTP).

Relates: #323
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-03 13:58:18 +02:00
Nikos Mavrogiannopoulos ebb34f6787 auth/plain: fix password retry and prevent username enumeration via timing
Corrected bugs that prevented a correct password from being accepted after
a wrong one in the same session:

- worker-auth.c: a stale webvpncontext SID cookie caused a reconnecting
  client to skip SEC_AUTH_INIT and jump directly to SEC_AUTH_CONT with
  a PS_AUTH_FAILED session, which sec-mod rejects.

- auth/plain.c: the 'failed' flag was sticky across retry attempts, so a
  correct password following a wrong one was still treated as failure.
  Refactored into 'unknown_user' (sticky, for timing protection) and a
  local 'wrong_pass' (fresh each call). crypt() is now always called
  regardless of whether the user exists to normalise response timing and
  prevent username enumeration.

Resolves: #323

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-03 13:55:07 +02:00
Nikos Mavrogiannopoulos c45e3467bb tests: check for the case where a password is incorrectly entered
This is a reproducer for the issue reported in #323

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-03 12:29:01 +02:00
Nikos Mavrogiannopoulos 9840050511 .gitlab-ci.yml: increased jobs
Several tests run longer without taking any resources, allow
more parallelization to finish faster.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-02 23:15:56 +02:00
Nikos Mavrogiannopoulos b25a1e7d81 tests: check for IPv6 handling issue
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-02 23:04:29 +02:00
Nikos Mavrogiannopoulos 107c12a5b6 if_address_init: correctly handle local IPv6 addresses
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-04-02 22:44:38 +02:00
Nikos Mavrogiannopoulos 3db9ecd259 tests: fixed flaky condition in disconnect-user
Ensure the client is killed eventually to prevent an openconnect
re-connect to keep the test up.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 21:13:28 +02:00
Nikos Mavrogiannopoulos fd3235784f tests: gssapi tests were moved to a specific testsuite
They are skipped in Ubuntu 22.04.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 17:32:23 +02:00
Nikos Mavrogiannopoulos 233aa02236 tests: do not run the firewall tests in i386/Debian
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 17:32:23 +02:00
Nikos Mavrogiannopoulos 3026f46d1b cipher-tests: skip if legacy DTLS is disabled
This is to enable running the test suite under Ubuntu 24.04.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 17:32:23 +02:00
Nikos Mavrogiannopoulos 0ba0c091af tests: initialize worker_st using = {0}
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 17:32:16 +02:00
Nikos Mavrogiannopoulos 979ccc711d .gitlab-ci.yml: updated to Ubuntu 24.04 and Fedora 43
This deprecates Ubuntu 20.04 builds and Fedora 42 builds
and tests the latest version.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 17:00:33 +02:00
Nikos Mavrogiannopoulos 271b8b9303 tests: added tests for ocserv setting fw rules
This validates that restrict-user-to-ports and
restrict-user-to-routes are enforced by the fw script.

The test verifies three cases after connecting with a config that
allows only TCP 80 and advertises a single route:
 - allowed port + advertised route: connection succeeds
 - denied port + advertised route: rejected at port level
 - allowed port + non-advertised route: rejected at route level

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 16:55:12 +02:00
Nikos Mavrogiannopoulos bb4e91c58a ocserv-fw: allow override using environment var
This enables testing of the script.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 16:55:12 +02:00
Nikos Mavrogiannopoulos 598bcf405e Added ocserv-fw for nftables
This also introduces a basic functional test for ocserv-fw.

Resolves: #397

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-29 16:55:12 +02:00
Nikos Mavrogiannopoulos e21359716b test-script-multi-user: simplified
This enables the script to terminate quickly under meson.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-22 22:54:48 +01:00
Nikos Mavrogiannopoulos e5eb5d6de7 .lcovrc: exclude from coverage bundled 3rd party projects and tests
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-22 21:17:18 +01:00
Nikos Mavrogiannopoulos 0a2dc40251 occtl: fix trailing comma in JSON array for show sessions valid/all
The has_more flag passed to print_end_block() used the raw array index,
producing a trailing comma when the last entry in args->cookies[] was
filtered out (e.g. a PS_AUTH_FAILED session lingering until cookie-timeout).

Fix by adding a pre-pass that collects the indices of entries that will
actually be printed into a C99 variable array vis[].

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
2026-03-22 21:17:02 +01:00
Nikos Mavrogiannopoulos efa28e1492 worker: flush coverage data on exit
Add a worker_exit() wrapper that calls __gcov_exit() before _exit()
when built with coverage instrumentation (-Db_coverage=true -> WITH_COVERAGE).
As atexit() handlers from libgcov are not triggered by SIGTERM, it caused
caused all worker-side coverage data to be silently lost.

umask(022) is set before __gcov_exit() so the .gcda files
are written with 0644 permissions and are readable by lcov.

This restores coverage for code paths that run exclusively in the
worker, such as lzs_compress(), lz4_compress().

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-03-22 21:16:57 +01:00