An exec rule that names a profile which is not loaded denies the exec.
The only way policy can say "use something else instead" today is the
composite mode letters:

  /usr/bin/stg  Pix -> stg,     # if stg is missing, run under the parent
  /usr/bin/stg  PUx -> stg,     # if stg is missing, run unconfined

However, ix and ux are not always adapted: ix gives the child whatever
the parent has and ux gives it everything. Neither can be fitted to what
the child actually does, so the choice is between denying and
over-granting.

Two cases want a real answer. Policy that ships in parts needs a rule to
stay useful while its target is still missing (a helper packaged
separately, a profile staged for a later release, and apparmor.d, whose
deployment is inherently partial). And a profile that spawns a helper
usually has a sensible category profile to name instead, say
network-helper: not necessarily stricter than ix, but fitted to the job
rather than inherited from the parent.

This series lets the rule name it. Both forms of exec rule take a list,
whether or not they name a target:

  /usr/bin/stg  Px -> stg  fallback=(generic-helper),
  /usr/bin/foo  Px         fallback=(restricted-helper),

This is the profile-side equivalent of identity delegation, and
complements it.

At exec the kernel tries, in order, the rule's own target (the -> target
if present, or the profile the executable's path attaches to) and then
each fallback in the order written. The first that resolves wins.

When the list is exhausted, the rule behaves exactly as an unresolved
target does without one: the ix or ux composite if the mode letters ask
for it, otherwise -EACCES with info="profile transition not found".

The list is walked afresh on every exec, so loading the primary target
later makes it win immediately.

The alternatives are appended to the rule's existing transition table
entry, which becomes a \0-separated list. A list is told from a plain
name by a second terminator, decided per entry rather than per table, so
policy built before this series keeps loading unchanged.

The implicit form needs a transition type of its own, and AA_X_TYPE_MASK
had one value spare: AA_X_NAME | AA_X_TABLE, previously unused, now means
resolve by attachment and walk the entry only if that finds nothing.

The kernel advertises domain/exec_fallback, allowing the parser to use
this feature in policies if supported by the kernel, or degrade loading
gracefully.

 - Patch 1 moves per-element target resolution into one place.
 - Patch 2 makes a table entry able to hold a list.
 - Patch 3 walks it.
 - Patch 4 adds the implicit form and advertises the feature

The parser side has been posted upstream separately.

Based on apparmor-next (4ec11f14d1d6).

Maxime Bélair (4):
  apparmor: split exec target resolution by kind
  apparmor: allow multi-entry transition tables
  apparmor: try exec fallback targets in order
  apparmor: resolve fallbacks after attachment

 security/apparmor/apparmorfs.c            |   1 +
 security/apparmor/domain.c                | 274 +++++++++++++++++-----
 security/apparmor/include/domain.h        |   3 -
 security/apparmor/include/file.h          |   4 +
 security/apparmor/include/policy_unpack.h |   1 +
 security/apparmor/policy_unpack.c         |  86 +++++--
 security/apparmor/policy_unpack_test.c    | 144 ++++++++++++
 7 files changed, 423 insertions(+), 90 deletions(-)

-- 
2.51.0


Reply via email to