TLS 1.3 has no renegotiation and provides transparent rekeys but this
was not used by the server. Instead rekey-method=ssl was silently
downgraded to new-tunnel, causing a full tunnel/TUN-device rebuild (and
a brief data-path interruption) on every rekey whenever a client
negotiated TLS 1.3.
This commit simplifies that handling by taking advantage of TLS 1.3's own
rekey mechanism instead: the server now performs the "ssl" rekey on
TLS 1.3 sessions via the standard TLS 1.3 KeyUpdate message.
TLS <= 1.2 rekey behavior (client-driven rehandshake, gated on RFC
5746 safe renegotiation) is unchanged.
Resolves: #745
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Commit 5cf457b4 ("Removed the listen-clear-file config option", Dec
2020) deleted the only code path that could ever construct a
SOCK_TYPE_UNIX worker connection, but left every branch that handled
it in place. Since then ws->session has been unconditionally non-NULL
and ws->conn_type unconditionally not SOCK_TYPE_UNIX for every worker,
making all of the following unreachable:
- tlslib.c: recv_remaining(), _cstp_recv_packet() (the non-TLS CSTP
reassembly path hardened in the previous commit), and
tls_has_session_cert() (zero callers) deleted outright; cstp_cork/
cstp_uncork/cstp_send/cstp_recv_packet/cstp_recv/cstp_close/
cstp_fatal_close collapsed to their TLS-only body.
- worker-http.c, worker-auth.c, worker-vpn.c, main.c: dead
ws->session == NULL / ws->conn_type == SOCK_TYPE_UNIX branches
removed or simplified to their live half.
- worker-proxyproto.c: parse_ssl_tlvs() and its TLV structs/macros
removed. This proxy-protocol SSL-CN extraction was itself only
ever invoked from the same dead SOCK_TYPE_UNIX branch, so it has
been as unreachable as the rest since 2020 despite a recent,
otherwise-correct bug fix.
- tests/cstp-recv.c deleted (exercised only the removed reassembly
path); tests/proxyproto-v2.c trimmed to the still-live IPv6
address-parsing regression test.
- doc/sample.config: dropped the stale "TCP or UNIX socket" wording
for listen-proxy-proto left over from the same 2020 removal; only
a TCP socket is ever listened on for the proxy protocol now.
Narrows the worker's attack surface to the code paths a client can
actually reach, and removes a source of wasted maintenance effort.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
recv_remaining() is used only on the non-TLS CSTP path (a UNIX socket
proxying plaintext CSTP in front of ocserv). On a recv() failure or
peer close mid-read, it discarded the error and returned whatever
partial byte count it had accumulated so far. Since every caller only
checks "ret <= 0" and otherwise trusts the count as a complete read,
a truncated body could come back as a positive, non-zero total that
looked like success: _cstp_recv_packet() would then report the full
8+pktlen size to its caller with only part of the buffer actually
populated from the network, feeding stale/uninitialized bytes into
parse_cstp_data() as if they were received client data.
Make the contract unambiguous: recv_remaining() now returns either
exactly the requested byte count or a negative error - never a
partial positive count a caller could mistake for success.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
ip_route_sanity_check() was a format normalizer, not a validator: any
dot-free route (all IPv6, or arbitrary text) returned success
unexamined, and a dotted-netmask IPv4 route passed through unchanged.
Since route_adddel() substitutes the validated route into
route-add-cmd/route-del-cmd and executes it via `/bin/sh -c` as root,
a route string carrying shell metacharacters that survived this check
was a root command-injection vector.
Rewrite the check to fully parse the route as an IPv4 or IPv6 address
plus prefix, or the literal keyword "default" (the documented
all-traffic-through-VPN shortcut, checked separately by config.c after
this function runs), and reject anything left over. Numeric IPv4
prefixes are still normalized to a dotted netmask as before.
Adds REQ-MAIN-SEC-008 and tests/route-sanity-check.c, confirmed
against real config/test usage (including "route = default") so the
stricter validation doesn't regress documented syntax.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
main read cookie.data[0] (to select sec_mod_instance_index) before
validating the cookie's length. A worker sending an empty cookie
unpacks with cookie.data == NULL (protobuf-c "required bytes"
semantics), so the read crashed the root main process, tearing down
every connected client.
Validate cookie.data/len immediately after unpacking, before the
first byte is read. handle_auth_cookie_req()'s own later check
remains as defense-in-depth.
Adds REQ-IPC-018.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
RFC 2866 (5.5) allows an Access-Request to carry Acct-Session-Id and
requires the same value in the session's Accounting-Requests. Sending it
already at authentication time lets the RADIUS server correlate the two
exchanges by a single per-session key (e.g. for rlm_ippool, so concurrent
sessions of the same user from the same client do not collide on one IP
lease).
Documented as REQ-AUTH-AUTH-025, with a positive check in tests/radius
that the id sent as Acct-Session-Id in the Access-Request matches the
Accounting-Request.
Signed-off-by: Dmitrii <dimmispencer@gmail.com>
Resolves: #751
The previous commit dropped stats_st.uptime and the uptime fields of
cli_stats_msg/secm_session_close_msg, computing Acct-Session-Time directly
in sec-mod as now - e->created instead. Update the two requirements that
described the old IPC-carried, summed uptime, and add REQ-AUTH-ACCT-007 to
formally document the Acct-Session-Time definition (wall-clock lifetime of
the logical session, spanning cookie-resumed reconnects, bounded by
session-timeout and cookie-timeout) that was implicit in the fix, citing
tests/radius-reconnect-acct as its acceptance test.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
A session that reconnects under the same cookie (roaming, DPD, a new-tunnel
rekey) reported an Acct-Session-Time far larger than the real duration. Each
connection segment reported uptime measured from the original session start
(now - e->created, as session_start_time is not reset across reconnects), and
sec-mod summed these per-segment cumulative values, so the reported time grew
~= D*(N+1)/2 with the number of reconnects.
Acct-Session-Time is the wall-clock lifetime of the logical session and is a
pure function of the session creation time, so it does not need to be reported
by the worker, carried through cli_stats_msg / secm_session_close_msg, or
accumulated alongside the byte counters. Drop stats_st.uptime and the uptime
fields of both IPC messages, and compute it in sec-mod as now - e->created:
live for each interim update, and snapshotted at disconnect for the Stop so the
cookie-timeout lingering period is not counted. The byte counters keep their
per-segment report-and-sum handling.
Signed-off-by: Alex Protsko <fidget2015@yahoo.com>
When a certificate contains a single group there is no need to
request the user to select. Auto-select the group.
Resolves: #692
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Documents the intended hardening of get_sup_config() so that
username/groupname (attacker-influenced) can no longer be used to escape
per-user-dir/per-group-dir when looking up supplemental configuration.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Add doc/requirements/, a structured set of normative requirements
extracted from the current ocserv implementation (internal/*.md,
generated with the requirements-from-implementation protocol) and
from the OpenConnect/AnyConnect protocol sources, reconciled into
protocol/unified.md.
Update AGENTS.md so that new features and bug fixes are documented as
requirements first: find or add the relevant REQ-* entry (with
acceptance criteria) and update the implied tests before changing
code, and confirm in merge requests that existing requirements and
use-cases still hold.
This follows partially https://github.com/microsoft/PromptKit
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
ocserv always logged to the syslog daemon(3) facility, making it
impossible to route its messages separately from other daemons.
Adds a syslog-facility config key (and --syslog-facility CLI flag)
accepting daemon/user/auth/authpriv/local0-local7; defaults to daemon.
Resolves: #691
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Native cgroup placement is removed. Delegating resource enforcement
to systemd eliminates the privileged /sys/fs/cgroup writes from
the main process and simplifies the code.
Administrators who previously relied on the 'cgroup' option should use
the [Service] section of the ocserv unit file instead; see
systemd.resource-control(5) for details.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Introduce a protobuf-generated ReloadableConfig (cfg.proto) to hold all
fields that reload on SIGHUP, and separate them from static_cfg_st, which
holds fields that require a server restart. Named vhosts inherit from the
default vhost via a pack/unpack round-trip. Adding a new config field
only requires editing cfg.proto. Struct and accessor names (ReloadableConfig,
static_cfg_st, GETRCONFIG, GETSCONFIG) now reflect each field's lifetime.
A new unit test covers the full inheritance path.
Resolves: #705
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Document that there is no need to reload ocserv after modifying per-user
or per-group configuration.
Signed-off-by: Grigory Trenin <grigory.trenin@gmail.com>
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>
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>
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>
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>
Add 'terminate user', 'terminate id' and 'terminate session' commands
to occtl that disconnect users and invalidate their session cookies,
preventing reconnection with cached credentials.
Short session IDs are resolved to full safe_id by fetching the cookie
list from sec-mod via CTL_CMD_LIST_COOKIES with prefix matching and
ambiguity detection. Active sessions trigger a warning before
invalidation.
Add integration tests for all three terminate commands.
Signed-off-by: Ivan Verbin <verbinivan@gmail.com>
The group functionality is available globally only and
there is no benefit from this option being per vhost.
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
This introduces the 'select-group-by-url' config option
that allows selecting an authgroup just by connecting to
a dedicated URI.
Signed-off-by: Marcin Ochab <marcin.ochab@gmail.com>
Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
Hijack Roaring Penguin's RADIUS attributes for that purpose:
* RP-Upstream-Speed-Limit → rx_per_sec
* RP-Downstream-Speed-Limit → tx_per_sec
While the ocserv configuration options use b/s, ocserv uses kb/s
internally. The radius attributes are already expressed in kb/s,
so we don't need to convert them.
Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com>