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
i/prompting: add constraints and abstract permissions #13850
Conversation
838fac8
to
596f185
Compare
596f185
to
90d08d9
Compare
There was a problem hiding this 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
// 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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
interfaces/prompting/constraints.go
Outdated
|
||
// 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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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!
interfaces/prompting/constraints.go
Outdated
// 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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
35c4529
to
1cce32f
Compare
Now that the |
There was a problem hiding this 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?
interfaces/prompting/constraints.go
Outdated
// 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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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!
There was a problem hiding this 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
There was a problem hiding this 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>
971a7d4
to
59aeeb6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
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>
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.