Skip to content

autls: add per-identity PSK key binding to TLS client ACL - #543

Merged
stevegrubb merged 6 commits into
linux-audit:masterfrom
sergio-correia:tls
Jul 28, 2026
Merged

autls: add per-identity PSK key binding to TLS client ACL#543
stevegrubb merged 6 commits into
linux-audit:masterfrom
sergio-correia:tls

Conversation

@sergio-correia

Copy link
Copy Markdown
Contributor

The TLS external-PSK listener uses one global key for all client identities.
A disabled collector that still possesses the shared PSK can claim the enabled
collector's label and be admitted — the ACL checks the label but the PSK callback
installs the same key regardless. This series adds per-identity key material to ACL
entries via a key=/path column, so the TLS 1.3 binder cryptographically binds each
identity to its own key. Cross-identity key pairing now fails at the protocol level.
Includes SIGHUP client eviction, mode transition guards, and a BIO-pair regression
test that exercises actual TLS handshakes with mismatched keys

Extend the ACL data structures and parser to support per-identity
pre-shared keys.  Each ACL entry can now carry its own key material
loaded from a file specified via a key=/path column.

Add psk_key, psk_key_len, and key_file fields to autls_acl_entry,
and a has_per_identity_keys flag to autls_acl_table.

The parser recognizes key= only when followed by '/' (absolute
path), preserving backward compatibility with existing ACL files
that use free-form notes after the status field.  When any entry
has a key= path, all entries must have one (all-or-nothing
enforcement prevents mixed single-PSK/per-identity configurations).
Duplicate key file paths and duplicate key content across entries
are both rejected to enforce the identity-key binding invariant.

Add autls_acl_lookup() which returns the full entry for callers
that need access to key material.  Rewrite autls_acl_check() to
delegate to autls_acl_lookup(), preserving the existing boolean
API.

Extract acl_entry_free() to cleanse per-identity key material via
OPENSSL_cleanse on all free paths, including partial-construction
error paths during parsing.

This commit adds the library support; the server-side callback
changes that use per-identity keys follow in the next commit.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
Restructure the TLS server initialization and PSK callback to
select per-identity key material when available, eliminating the
identity confusion vulnerability where a disabled collector could
authenticate as the enabled identity using the shared global PSK.

Restructure init_tls_server_context() from a single monolithic
if(tls_psk_file) block into four independent steps: global PSK
loading, expected identity setup, ACL loading, and callback
registration.  This decoupling allows per-identity key mode to
operate without a global tls_psk_file.

In tls_psk_find_session_cb(), replace autls_acl_check() with
autls_acl_lookup() and add mode-aware key selection.  In
per-identity mode, always use the entry's own key material and
hard-reject if it is missing (no silent fallback to the global
key).  In single-PSK mode, continue using server_psk_key.

In reload_tls_client_acl(), add guards for per-identity-only mode
ACL removal, reject mode transitions between single-PSK and
per-identity on SIGHUP, enforce enabled_count using the new ACL's
has_per_identity_keys flag, and evict connected clients whose
identity is disabled or absent in the reloaded ACL.  Client
eviction follows the periodic_handler pattern: save next pointer,
stop the ev_io watcher, then release and free.

Add a post-handshake re-check in tls_handshake_handler() against
the current ACL before admitting a client, closing the window
where a SIGHUP swaps the ACL between the PSK callback and
handshake completion.

Warn at init and reload when the ACL has zero enabled identities.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
Relax the configuration validation to allow tls_allowed_clients
as an alternative to tls_psk_file.  When per-identity keys are
specified via key= entries in the ACL file, a global tls_psk_file
is unnecessary.

The actual validation that per-identity key material exists
happens in init_tls_server_context() after ACL loading.  A
diagnostic notice is logged when tls_psk_file is absent to guide
operators.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
Document the key= column for per-identity key file paths,
permission requirements, the distinction between single-PSK and
per-identity mode, SIGHUP client eviction behavior, and the
operational requirement to provision unique per-identity keys.

Note that disabling an identity in single-PSK mode does not
revoke a client that still possesses the shared key, and that
switching between modes requires a daemon restart.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
Add test_autls_acl_per_identity_keys() with 9 test cases covering
the new per-identity key ACL functionality:

1. Per-identity key loading with key= paths
2. Mixed format (some key=, some not) rejected
3. Duplicate key file paths rejected
4. Missing key file rejected
5. Partial-load cleanup (ASAN-safe error path)
6. Multiple enabled identities accepted with per-identity keys
7. Backward compat (no key= columns, has_per_identity_keys=0)
8. Notes starting with key= (no /) treated as notes
9. Trailing tokens after key= path rejected

Also exercises autls_acl_lookup() return values and verifies
that per-identity keys are distinct across entries.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
Add the essential regression test for the per-identity PSK fix:
verify that cross-pairing an identity with a different identity's
key fails at the TLS binder level, not just at the ACL label
level.

Uses in-memory BIO pairs with psk_ke mode (no certificate, no DHE)
to exercise actual TLS 1.3 external-PSK handshakes through OpenSSL.
The server callback selects per-identity keys via autls_acl_lookup,
mirroring the production tls_psk_find_session_cb key selection
logic.

Five test cases:
1. Correct identity+key pairing succeeds
2. Cross-identity key pairing fails (the essential regression)
3. Disabled identity with own key is rejected by ACL
4. Unknown identity is rejected
5. Global key does not match per-identity entry

Without this test, a regression in the PSK callback that falls
back to a shared key would pass all unit tests while leaving the
identity confusion vulnerability exploitable.

Assisted-by: Claude Opus 4.6
Signed-off-by: Sergio Correia <scorreia@redhat.com>
@stevegrubb
stevegrubb merged commit 6002a3d into linux-audit:master Jul 28, 2026
4 checks passed
@sergio-correia
sergio-correia deleted the tls branch July 28, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants