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

interfaces/utils: allow commas in filepaths #12697

Merged
merged 8 commits into from May 19, 2023

Conversation

olivercalder
Copy link
Member

@olivercalder olivercalder commented Apr 3, 2023

Some device paths contain commas outside of groups (i.e. {a,b}) or classes (i.e. [,.:;'"]). For example, /dev/foo,bar is a valid device path which one might wish to use with the custom-device interface.

Most filesystems allow commas in filepaths, as does apparmor: https://gitlab.com/apparmor/apparmor/-/blob/master/parser/parser_regex.c#L340

Previously, createRegex() would throw an error if a comma was used outside of a group or class. This commit removes that error and instead treats commas outside of groups and classes as literal commas. The accompanying tests are also adjusted to reflect this change.

@olivercalder
Copy link
Member Author

Currently, this change affects all uses of interfaces/utils/path_patterns.go:NewPathPattern(). I decided to make the change in this way so there would be consistent rules regarding comma usage in filepaths. If, however, this change should be confined to the custom_device interface, then I would propose a change similar to the attached diff, where the custom_device interface would use NewPathPatternAllowCommas and NewPathPattern would remain unchanged for all other callers. This is slightly more complicated and requires some duplicate testing, but it avoids changing behavior for anything besides the custom_device interface.

I'm very open to any comments or suggestions. Thanks!

restricted_change.txt

@alexmurray alexmurray added the Needs security review Can only be merged once security gave a :+1: label Apr 4, 2023
@alexmurray alexmurray self-requested a review April 4, 2023 03:00
Copy link
Collaborator

@alexmurray alexmurray left a comment

Choose a reason for hiding this comment

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

I was initially quite concerned that by allowing a comma it would then allow malicious snaps to inject arbitrary AppArmor rules and hence escape confinement as the comma is used to delimit separate AppArmor rules within AppArmor profiles.

However, there are a number of things that prevent this currently:

  1. NewPathPattern() appears to only be used in the mount-control and custom-device interfaces, both of which need a matching snap declaration from the store to be able to be used, and so a snap publisher can only achieve the ability to inject content with the help of a store admin to grant a matching snap declaration
  2. Both of these interfaces quote the generated path with "" which ensures that AppArmor treats the supplied argument as a complete path name rather than as say two AppArmor rules, separated by a comma.

This change does however remove an existing defensive measure to stop possible rule injection, so it will require careful attention to any new future users of NewPathPattern() to ensure they ultimately quote their final argument when emitting AppArmor rules.

So from a security point of view I am not opposed to this change as clearly it is required to allow snaps to specify a custom device path containing a comma which is a real-world use-case.

I wonder if perhaps though it would be worth parameterising NewPathPatter() with a boolean to allow / deny the use of a comma (and hence pass this parameter on to createRegex()) so that this new behaviour can be restricted to just the custom-device interface for now rather than to all / any callers of NewPathPattern() to keep things safer.

@alexmurray alexmurray removed Security-High Needs security review Can only be merged once security gave a :+1: labels Apr 11, 2023
@olivercalder
Copy link
Member Author

olivercalder commented Apr 11, 2023

After a bit more discussion, it seems that there are three options regarding when to allow commas:

  1. Allow commas in paths always (current state of the PR)
    • At the moment, with the only callers of NewPathPattern() being the mount-control and custom-device interfaces, this might be fine since both are privileged and presumably need to allow commas in filepaths
    • It is important to vet any future callers of NewPathPattern()
  2. Add a boolean parameter to NewPathPattern() and createRegex() which toggles whether commas are allowed in filepaths
    • Since the only callers of NewPathPattern() are mount-control and custom-device, this change would be minimally disruptive
  3. Make a separate function NewPathPatternWithCommas() and add the boolean parameter to createRegex(), thus making the default still disallow commas and the new separate function needed to allow them
    • This is probably not necessary given that there are currently only two callers, so widespread changes are not a concern
    • This would involve duplicate testing, which is undesirable

@pedronis pedronis self-requested a review April 14, 2023 08:13
@pedronis pedronis added this to the 2.60 milestone Apr 14, 2023
olivercalder added a commit to olivercalder/snapd that referenced this pull request Apr 17, 2023
Rather than allowing any caller of `NewPathPattern()` to successfully
validate paths containing commas, this change adds a boolean argument
which explicitly specifies whether commas should be allowed in the
filepath.

There are some risks involved with allowing commas in filepaths (see
discussion at snapcore#12697), so it is
desirable to restrict when commas are allowed based on the caller.  In
particular, superprivileged interfaces (such as `custom-device` and
`mount-control`) have valid needs for commas in filepaths, and users of
these interfaces are individually verified, so it is safe for them to
use `NewPathPattern()` with commas allowed.  Other callers (particularly
unprivileged interfaces) should probably not allow commas.

I was unsure whether `overlord/hookstate/ctlcmd/mount.go` should call
`NewPathPattern()` with commas allowed or not, but since commas had
previously been disallowed and tests continue to pass with
`allowCommas=false`, then I decided to leave it as `false`.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
@github-actions github-actions bot added the Needs Documentation -auto- Label automatically added which indicates the change needs documentation label Apr 17, 2023
@olivercalder
Copy link
Member Author

I opted for the second option of those listed above. I think a boolean parameter is the most flexible while minimizing duplicate code or execution paths.

One question is whether overlord/hookstate/ctlcmd/mount.go should call NewPathPattern() with commas allowed or not, but since commas had previously been disallowed and tests continue to pass with allowCommas=false, I decided to leave it as false. Please reply if this should be changed.

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 pass

overlord/hookstate/ctlcmd/mount.go Outdated Show resolved Hide resolved
@@ -76,7 +76,7 @@ func (iface *customDeviceInterface) validateFilePath(path string, attrName strin
return fmt.Errorf(`custom-device %q path is not clean: %q`, attrName, path)
}

if _, err := utils.NewPathPattern(path); err != nil {
if _, err := utils.NewPathPattern(path, true); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

when we rarely use boolean args we use constants to help reading:

const allowCommas = true
... utils.NewPathPattern(path, allowCommas) ...

Copy link
Collaborator

Choose a reason for hiding this comment

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

this needs a corresponding unit test

Copy link
Collaborator

@ernestl ernestl left a comment

Choose a reason for hiding this comment

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

Looks good.
Left two minor comments.

For the mount control use case, do we currently need comma path support for more than functionfs?

@olivercalder
Copy link
Member Author

Thanks for the review and suggestions @ernestl

Since commas are allowed in filepaths on Linux generally, I don't see why it wouldn't be the case that a user of the mount-control interface might need to mount a device with a comma in its path or mount to a mount point with a comma in its path, regardless of filesystem type. However, I don't think this has yet been a problem in the field. I'm happy to restrict it to the custom-device interface, if that's desirable (particularly with respect to security review).

Some device paths contain commas outside of groups (i.e. {a,b}) or
classes (i.e. [,.:;'"]).  For example, `/dev/foo,bar` is a valid device
path which one might with to use with the custom-device interface.

Most filesystems allow commas in filepaths, as does apparmor:
https://gitlab.com/apparmor/apparmor/-/blob/master/parser/parser_regex.c#L340

Previously, createRegex() would throw an error if a comma was used
outside of a group or class.  This commit removes that error and instead
treats commas outside of groups and classes as literal commas.  The
accompanying tests are also adjusted to reflect this change.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Rather than allowing any caller of `NewPathPattern()` to successfully
validate paths containing commas, this change adds a boolean argument
which explicitly specifies whether commas should be allowed in the
filepath.

There are some risks involved with allowing commas in filepaths (see
discussion at snapcore#12697), so it is
desirable to restrict when commas are allowed based on the caller.  In
particular, superprivileged interfaces (such as `custom-device` and
`mount-control`) have valid needs for commas in filepaths, and users of
these interfaces are individually verified, so it is safe for them to
use `NewPathPattern()` with commas allowed.  Other callers (particularly
unprivileged interfaces) should probably not allow commas.

I was unsure whether `overlord/hookstate/ctlcmd/mount.go` should call
`NewPathPattern()` with commas allowed or not, but since commas had
previously been disallowed and tests continue to pass with
`allowCommas=false`, then I decided to leave it as `false`.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Also, switched `overlord/hookstate/ctlcmd/mount.go` to allow commas
(previously did not, but this should match what is allowed in
`interfaces/builtin/mount_control.go`.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Since `,` is not a regex special character, the `QuoteMeta` call is
unnecessary.

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
@pedronis pedronis self-requested a review April 20, 2023 08:47
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.

this looks good but needs matching unit test extensions

interfaces/builtin/custom_device.go Show resolved Hide resolved
interfaces/builtin/mount_control.go Show resolved Hide resolved
overlord/hookstate/ctlcmd/mount.go Show resolved Hide resolved
…=true

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
@pedronis pedronis self-requested a review May 12, 2023 13:05
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

@mvo5 mvo5 added the Squash-merge Please squash this PR when merging. label May 17, 2023
@mvo5 mvo5 modified the milestones: 2.60, 2.59 May 17, 2023
@mvo5 mvo5 merged commit 80289f5 into snapcore:master May 19, 2023
41 of 50 checks passed
mvo5 added a commit that referenced this pull request May 27, 2023
* interfaces/utils: allow commas in filepaths

Some device paths contain commas outside of groups (i.e. {a,b}) or
classes (i.e. [,.:;'"]).  For example, `/dev/foo,bar` is a valid device
path which one might with to use with the custom-device interface.

Most filesystems allow commas in filepaths, as does apparmor:
https://gitlab.com/apparmor/apparmor/-/blob/master/parser/parser_regex.c#L340

Previously, createRegex() would throw an error if a comma was used
outside of a group or class.  This commit removes that error and instead
treats commas outside of groups and classes as literal commas.  The
accompanying tests are also adjusted to reflect this change.

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

* interfaces/utils: added argument to allow commas in filepaths

Rather than allowing any caller of `NewPathPattern()` to successfully
validate paths containing commas, this change adds a boolean argument
which explicitly specifies whether commas should be allowed in the
filepath.

There are some risks involved with allowing commas in filepaths (see
discussion at #12697), so it is
desirable to restrict when commas are allowed based on the caller.  In
particular, superprivileged interfaces (such as `custom-device` and
`mount-control`) have valid needs for commas in filepaths, and users of
these interfaces are individually verified, so it is safe for them to
use `NewPathPattern()` with commas allowed.  Other callers (particularly
unprivileged interfaces) should probably not allow commas.

I was unsure whether `overlord/hookstate/ctlcmd/mount.go` should call
`NewPathPattern()` with commas allowed or not, but since commas had
previously been disallowed and tests continue to pass with
`allowCommas=false`, then I decided to leave it as `false`.

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

* interfaces/{builtin,utils}: added named variables for allowCommas

Also, switched `overlord/hookstate/ctlcmd/mount.go` to allow commas
(previously did not, but this should match what is allowed in
`interfaces/builtin/mount_control.go`.

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

* interfaces/utils: added unit tests for commas in paths

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

* interfaces/utils: remove `QuoteMeta` when adding `","` to path regex

Since `,` is not a regex special character, the `QuoteMeta` call is
unnecessary.

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

* interfaces/utils: renamed TestCommasInRegex to TestCreateRegexWithCommas

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

* many: added unit tests for callers of NewPathPattern with allowCommas=true

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

---------

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Co-authored-by: Michael Vogt <mvo@ubuntu.com>
alexmurray pushed a commit to alexmurray/snapd that referenced this pull request Oct 17, 2023
* interfaces/utils: allow commas in filepaths

Some device paths contain commas outside of groups (i.e. {a,b}) or
classes (i.e. [,.:;'"]).  For example, `/dev/foo,bar` is a valid device
path which one might with to use with the custom-device interface.

Most filesystems allow commas in filepaths, as does apparmor:
https://gitlab.com/apparmor/apparmor/-/blob/master/parser/parser_regex.c#L340

Previously, createRegex() would throw an error if a comma was used
outside of a group or class.  This commit removes that error and instead
treats commas outside of groups and classes as literal commas.  The
accompanying tests are also adjusted to reflect this change.

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

* interfaces/utils: added argument to allow commas in filepaths

Rather than allowing any caller of `NewPathPattern()` to successfully
validate paths containing commas, this change adds a boolean argument
which explicitly specifies whether commas should be allowed in the
filepath.

There are some risks involved with allowing commas in filepaths (see
discussion at snapcore#12697), so it is
desirable to restrict when commas are allowed based on the caller.  In
particular, superprivileged interfaces (such as `custom-device` and
`mount-control`) have valid needs for commas in filepaths, and users of
these interfaces are individually verified, so it is safe for them to
use `NewPathPattern()` with commas allowed.  Other callers (particularly
unprivileged interfaces) should probably not allow commas.

I was unsure whether `overlord/hookstate/ctlcmd/mount.go` should call
`NewPathPattern()` with commas allowed or not, but since commas had
previously been disallowed and tests continue to pass with
`allowCommas=false`, then I decided to leave it as `false`.

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

* interfaces/{builtin,utils}: added named variables for allowCommas

Also, switched `overlord/hookstate/ctlcmd/mount.go` to allow commas
(previously did not, but this should match what is allowed in
`interfaces/builtin/mount_control.go`.

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

* interfaces/utils: added unit tests for commas in paths

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

* interfaces/utils: remove `QuoteMeta` when adding `","` to path regex

Since `,` is not a regex special character, the `QuoteMeta` call is
unnecessary.

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

* interfaces/utils: renamed TestCommasInRegex to TestCreateRegexWithCommas

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

* many: added unit tests for callers of NewPathPattern with allowCommas=true

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

---------

Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Co-authored-by: Michael Vogt <mvo@ubuntu.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cherry-picked Needs Documentation -auto- Label automatically added which indicates the change needs documentation Squash-merge Please squash this PR when merging.
Projects
None yet
5 participants