Skip to content

Ensure a configuration is only written to its own file - #3044

Open
jbonofre wants to merge 1 commit into
apache:mainfrom
jbonofre:config-write-own-file
Open

jbonofre wants to merge 1 commit into
apache:mainfrom
jbonofre:config-write-own-file

Conversation

@jbonofre

Copy link
Copy Markdown
Member

Problem

Confining configuration writes to ${karaf.etc} (added in #2867) is not sufficient, because the files reserved to admin live inside etc too: users.properties, and the JMX / command ACLs.

A manager-level user, who is allowed to edit ordinary PIDs but not those reserved files, can still overwrite them through ConfigRepositoryImpl:

  • via felix.fileinstall.filename — create a new PID and set its felix.fileinstall.filename to file:${karaf.etc}/users.properties, with properties that add an admin account. On the "new PID" branch the value is honored and the file is overwritten (an existing PID reuses its stored file, so that path was already safe).
  • via the storage type — config:edit --type acl.org.apache.karaf.config.cfg on PID jmx builds the file name jmx.acl.org.apache.karaf.config.cfg, i.e. the JMX ACL file.

Either way a manager escalates to admin. This is reachable from config:property-set / config:update and from the ConfigMBean (setProperty, update, create, factory creation) — the whole manager-reachable surface routes through ConfigRepository.update / createFactoryConfiguration.

Fix

checkConfigFile now requires the target to:

  1. be inside ${karaf.etc} (as before);
  2. use a configuration extension — cfg, config, or the configured JSON one; and
  3. be named after the PID (or the factory PID, for factory configurations).

So a configuration can only ever be written to its own file, and can no longer clobber another PID's file or a non-config file such as users.properties. The canonical path is resolved before the name check, which also rejects a case-only variant of a reserved name (e.g. JMX.ACL... vs jmx.acl...) on case-insensitive file systems, where the regex-based ACLs would not match. Factory creation additionally rejects an unsupported storage type.

Existing configurations that were deliberately given a custom file name keep working: the name check only runs when a configuration has no backing file yet, which is exactly the attack path.

Tests

ConfigRepositoryImplContainmentTest is extended to 13 cases: the reported felix.fileinstall.filename attack and the --type variant against both users.properties and an ACL file, the case-only bypass, and unsupported suffixes, plus non-regression checks that ordinary updates, JSON storage and factory aliases still write correctly. The six attack tests fail on the current code and pass with this change; config/core and config/command build green on JDK 17.

Note

This is a sibling of CVE-2026-91012 (same manager → admin boundary and the same #2867 fix), so it may warrant handling through the security list rather than purely as a public change.

Confining configuration writes to ${karaf.etc} is not enough: the files
reserved to admin (users.properties, the JMX and command ACLs) live in
etc too. A manager-level user could create a new PID and point its
felix.fileinstall.filename at etc/users.properties, or abuse the storage
type (--type) so the generated name resolves to a reserved file, and
overwrite it with an added admin account - a manager-to-admin escalation.
This completes the containment added in apache#2867.

checkConfigFile now requires the target to be inside ${karaf.etc}, use a
configuration extension (cfg, config or the JSON one) and be named after
the PID (or the factory PID for factory configurations), so a config can
only ever be written to its own file. The canonical path is resolved
first, which also rejects a case-only variant of a reserved name on
case-insensitive file systems, where the regex ACLs would not match.
Factory creation also rejects an unsupported storage type.

Existing configurations given a custom file name keep working: the name
check only runs when a configuration has no backing file yet, which is
the attack path.
@github-actions

Copy link
Copy Markdown

Test Results

  729 files  ± 0    729 suites  ±0   53m 34s ⏱️ - 9m 23s
1 087 tests + 9    953 ✅ + 9  134 💤 ±0  0 ❌ ±0 
3 261 runs  +27  2 858 ✅ +26  403 💤 +1  0 ❌ ±0 

Results for commit 86d6f5e. ± Comparison against base commit 05aa121.

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.

1 participant