Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i/prompting: add constraints and abstract permissions #13850

Merged
merged 10 commits into from May 2, 2024

Conversation

olivercalder
Copy link
Member

This PR introduces constraints and abstract permissions, originally from #13668. This PR is based on #13849, and should be continually rebased on it whenever changes are pushed there.

@olivercalder olivercalder added the Skip spread Indicate that spread job should not run label Apr 18, 2024
@olivercalder olivercalder added the Needs Samuele review Needs a review from Samuele before it can land label Apr 18, 2024
@olivercalder olivercalder force-pushed the prompting-add-constraints branch 2 times, most recently from 838fac8 to 596f185 Compare April 18, 2024 18:30
@olivercalder olivercalder removed the Skip spread Indicate that spread job should not run label Apr 18, 2024
@olivercalder olivercalder reopened this Apr 18, 2024
@olivercalder olivercalder marked this pull request as ready for review April 18, 2024 18:32
Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did a first pass with some questions/comments

interfaces/prompting/constraints.go Outdated Show resolved Hide resolved
interfaces/prompting/constraints.go Show resolved Hide resolved
interfaces/prompting/constraints.go Show resolved Hide resolved
// AbstractPermissionsFromList validates the given permissions list for the
// given interface and returns a list containing those permissions in the order
// in which they occur in the list of available permissions for that interface.
func AbstractPermissionsFromList(iface string, permissions []string) ([]string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the name doesn't really convey that this does also validation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, the only use case for this right now is in constraints.ValidateForInterface, so this could be called ValidatePermissions as a method on the Constraints type.


// ValidateConstraintsOutcomeLifespanExpiration returns an error if the given
// constraints, outcome, lifespan, or duration are invalid, else returns nil.
func ValidateConstraintsOutcomeLifespanExpiration(iface string, constraints *Constraints, outcome OutcomeType, lifespan LifespanType, expiration *time.Time, currTime time.Time) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is strange, do we need a struct with the thing passed to this all together and this is a method on it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this should be a method on the RequestRule type, likely rule.Validate(), and this is only used when looking over existing rules and making sure they are valid and consistent. Thanks!

// ValidateConstraintsOutcomeLifespanDuration returns an error if the given
// constraints, outcome, lifespan, or duration are invalid. Otherwise, converts
// the given duration to an expiration timestamp and returns it and nil error.
func ValidateConstraintsOutcomeLifespanDuration(iface string, constraints *Constraints, outcome OutcomeType, lifespan LifespanType, duration string) (*time.Time, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again it seems that this should be folded in the other taking a struct and make decision based on what lifespan is?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is a bit more complex, as these fields are validated both when receiving a PromptReply from the client and when adding a new rule. I think the simplest/safest approach would be to continue to validate using rule.Validate() as part of PopulateNewRule, which is a helper in the requestrules called whenever a rule is being added or modified.

Rule expansion also occurs immediately after validation in requestrules, so rule.Validate() could also call ExpandPathPattern, making it all simpler.

The problem is that we want to be able to validate PromptReply contents before sending back a reply to the listener, all of which occurs before we go about creating a new rule. See: https://github.com/snapcore/snapd/pull/12964/files#diff-1bc7f26dd750993e7bf7bb0c8feb2e6d664f99313b5069f1dd68f4c3a66d51f9R234-R266

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for now, I'm inclined to remove this function, and we can re-add something like it when it is known precisely what we need, likely during the upcoming apparmorprompting package PR.

@olivercalder
Copy link
Member Author

Now that the ValidateConstraintsOutcomeLifespan* functions are removed, this no longer depends on #13849, hopefully making reviews a bit easier as well. Rebased on master while we're at it.

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, but a bit unsure why the split out helpers?

// AbstractPermissionsFromAppArmorPermissions returns the list of permissions
// corresponding to the given AppArmor permissions for the given interface.
func AbstractPermissionsFromAppArmorPermissions(iface string, permissions interface{}) ([]string, error) {
return abstractPermissionsFromAppArmorFilePermissions(iface, permissions)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the call to a helper instead of having the implementation here? same for the functions below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows us to easily support other mediation classes in the future by adding equivalent helpers. And don't have to deal with type assertions here. Alternatively, could switch on permissions.(type), which could perhaps be better. But the original idea was to switch on iface and choose the appropriate abstractPermissionsFromAppArmor{File,Dbus,Network,...}Permissions function for that interface --- this was subsequently replaced by simply calling abstractPermissionsFromAppArmorFilePermissions, for now.

If we just switch on permissions.(type), then if the iface is not supported for the mediation class (e.g. file, network), currently we return an internal error, but we could change the error message.

The problem with this approach is that, for AbstractPermissionsToAppArmorPermissions, we don't have any indication of which mediation class is applicable unless we switch on iface, and even then this doesn't work if an interface could potentially handle more than one mediation class.

So I agree, for the time being, it would be simpler/clearer to just have the ...FilePermission implementation in the function body, no helper. Thanks!

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, comments mostly about errors

interfaces/prompting/constraints.go Outdated Show resolved Hide resolved
interfaces/prompting/constraints.go Outdated Show resolved Hide resolved
interfaces/prompting/constraints.go Outdated Show resolved Hide resolved
interfaces/prompting/constraints.go Outdated Show resolved Hide resolved
@olivercalder olivercalder requested a review from pedronis May 1, 2024 14:55
Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: added function to select one interface

Multiple interfaces may be included in the tag in the kernel message,
and the listener passes these on to the other prompting components. This
PR adds a function to decide which of those interfaces to use in prompt
requests and rules.

Rules only apply to a particular interface, and we don't want duplicate
rules, so we must choose one interface from the list provided by the
listener which we use for the prompting requests and rules associated
with the listener request.

It is rather arbitrary which interfaces should have priority, and in
many cases interfaces do not have overlapping permissions, but we should
nonetheless manually assign a priority to any interface for which we
enable prompting.

Any request with only interfaces which are not explicitly included in
the list will be treated as having interface "other", as will any
request with an empty interfaces list.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: add "constraints" field to rules and replies

Adds a "constraints" field to request rules and other related structure,
such as prompt replies. These constraints vary by interface, with some
interfaces supporting different permissions than others, some interfaces
supporting different constraints on path patterns (or non-path
resources), and possibly future extensions in the future. The idea
behind constraints is to allow these interface-specific variations in
the future.

Addionally, there are some changes to behavior which are introduced
alongside the constraints changes:
1. Constraints (formerly permissions lists) are no longer duplicated
   when creating rules, to avoid unnecessary memory allocations.
2. Permissions are removed from constraints (formerly permission lists)
   in-place, rather than by creating a new list, again to avoid
   unnecessary memory allocations, so constraints should never be shared
   or reused between multiple rules.
3. Prompt reply fields are validated *before* sending back a reply to
   the kernel, and if any fields are invalid, or the reply constraints
   do not match the original request, a reply is not sent.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: abstract apparmor permissions

Convert AppArmor permissions into abstract permission strings, where the
available permissions are dependent on the interface associated with the
prompt or rule.

This allows greater flexibility to accept requests with new interfaces
and/or new mediation classes from the kernel without changing the
user-facing API (at least, regarding permissions), and with minimal
internal code changes.

In particular, the functions for parsing request permissions from
AppArmor are modular, and all that is required to add a new interface
with an existing mediation class is to add the mappings from abstract to
AppArmor permissions.

Additionally, reorganized and added more unit tests to increase coverage.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

o/i/a/common: unexport unused exported function

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: small refactors and quote variables in error messages

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: mark constraints fields as omitempty

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: move constraints and abstract permissions to interfaces/prompting

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
…terface

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
`ValidateConstraintsOutcomeLifespanExpiration` should be replaced by a
`Validate` method on the forthcoming `RequestRule` type, while
`ValidateConstraintsOutcomeLifespanDuration` should be unnecessary,
since validation of outcomes and lifespans will occur during
unmarshalling, and converting from duration to expiration should be done
explicitly when necessary.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
…ppArmorPermissions

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Copy link
Contributor

@MiguelPires MiguelPires left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@olivercalder olivercalder added the Squash-merge Please squash this PR when merging. label May 2, 2024
@pedronis pedronis merged commit 5ef32b3 into snapcore:master May 2, 2024
45 of 48 checks passed
MiguelPires pushed a commit to MiguelPires/snapd that referenced this pull request May 2, 2024
This PR introduces constraints and abstract permissions.

* i/prompting: add constraints and abstract permissions

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: added function to select one interface

Multiple interfaces may be included in the tag in the kernel message,
and the listener passes these on to the other prompting components. This
PR adds a function to decide which of those interfaces to use in prompt
requests and rules.

Rules only apply to a particular interface, and we don't want duplicate
rules, so we must choose one interface from the list provided by the
listener which we use for the prompting requests and rules associated
with the listener request.

It is rather arbitrary which interfaces should have priority, and in
many cases interfaces do not have overlapping permissions, but we should
nonetheless manually assign a priority to any interface for which we
enable prompting.

Any request with only interfaces which are not explicitly included in
the list will be treated as having interface "other", as will any
request with an empty interfaces list.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: add "constraints" field to rules and replies

Adds a "constraints" field to request rules and other related structure,
such as prompt replies. These constraints vary by interface, with some
interfaces supporting different permissions than others, some interfaces
supporting different constraints on path patterns (or non-path
resources), and possibly future extensions in the future. The idea
behind constraints is to allow these interface-specific variations in
the future.

Addionally, there are some changes to behavior which are introduced
alongside the constraints changes:
1. Constraints (formerly permissions lists) are no longer duplicated
   when creating rules, to avoid unnecessary memory allocations.
2. Permissions are removed from constraints (formerly permission lists)
   in-place, rather than by creating a new list, again to avoid
   unnecessary memory allocations, so constraints should never be shared
   or reused between multiple rules.
3. Prompt reply fields are validated *before* sending back a reply to
   the kernel, and if any fields are invalid, or the reply constraints
   do not match the original request, a reply is not sent.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: abstract apparmor permissions

Convert AppArmor permissions into abstract permission strings, where the
available permissions are dependent on the interface associated with the
prompt or rule.

This allows greater flexibility to accept requests with new interfaces
and/or new mediation classes from the kernel without changing the
user-facing API (at least, regarding permissions), and with minimal
internal code changes.

In particular, the functions for parsing request permissions from
AppArmor are modular, and all that is required to add a new interface
with an existing mediation class is to add the mappings from abstract to
AppArmor permissions.

Additionally, reorganized and added more unit tests to increase coverage.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

o/i/a/common: unexport unused exported function

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: small refactors and quote variables in error messages

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: mark constraints fields as omitempty

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

i/prompting: move constraints and abstract permissions to interfaces/prompting

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: remove SelectSingleInterface and references to camera interface

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: removed switches for handlers based on interface name

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: use *time.Time for expiration

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: simplify RemovePermission

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: renamed AbstractPermissionsFromList to ValidatePermissions

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: remove ValidateConstraintsOutcomeLifespan* functions

`ValidateConstraintsOutcomeLifespanExpiration` should be replaced by a
`Validate` method on the forthcoming `RequestRule` type, while
`ValidateConstraintsOutcomeLifespanDuration` should be unnecessary,
since validation of outcomes and lifespans will occur during
unmarshalling, and converting from duration to expiration should be done
explicitly when necessary.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: assume file permissions in AbstractPermissions{To,From}AppArmorPermissions

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: adjust abstract permission error messages

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

* i/prompting: use separate test suite for constraints

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>

---------

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Samuele review Needs a review from Samuele before it can land Squash-merge Please squash this PR when merging.
Projects
None yet
3 participants