* o/hookstate: recognize "--" in snapctl argument parser
When parsing snapctl argument vector recognize the "--" as an option
terminator, so that dash-options are not recognized afterwards.
Fixes: https://bugs.launchpad.net/snapd/+bug/2065077
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
* tests: add regression test for lp-
2065077
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
---------
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
Gbp-Pq: Name 0005-PATCH-03-18-Merge-pull-request-from-GHSA-p9v8-q5m4-p.patch
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
+// isAllowedToRun returns true if the user with the given UID can run the given snapctl command vector.
+//
+// Commands still need valid context and snaps can only access own config.
func isAllowedToRun(uid uint32, args []string) bool {
- // A command can run if any of the following are true:
- // * It runs as root
- // * It's contained in nonRootAllowed
- // * It's used with the -h or --help flags
- // note: commands still need valid context and snaps can only access own config.
- return uid == 0 ||
- strutil.ListContains(nonRootAllowed, args[0]) ||
- strutil.ListContains(args, "-h") ||
- strutil.ListContains(args, "--help")
+ // Root can run all snapctl commands.
+ if uid == 0 {
+ return true
+ }
+
+ for idx, arg := range args {
+ // A number of sub-commands are allowed to be executed by non-root users.
+ if idx == 0 && strutil.ListContains(nonRootAllowed, arg) {
+ return true
+ }
+
+ // Invoking help is always allowed.
+ if arg == "-h" || arg == "--help" {
+ return true
+ }
+
+ // Note that we are not interrupting parsing after the first non-option
+ // argument (POSIX style), because we want to cater to the use case of
+ // the user appending --help or -h at the end of the command and still
+ // getting something useful. The only exception is the condition below.
+
+ // The explicit termination argument terminates parsing.
+ if arg == "--" {
+ break
+ }
+ }
+
+ return false
}
c.Check(err.Error(), Equals, `cannot use "start" with uid 1000, try with sudo`)
}
+func (s *ctlcmdSuite) TestRootRequiredCommandFailureParsingTweak(c *C) {
+ _, _, err := ctlcmd.Run(s.mockContext, []string{"start", "--", "--help"}, 1000)
+
+ c.Check(err, FitsTypeOf, &ctlcmd.ForbiddenCommandError{})
+ c.Check(err.Error(), Equals, `cannot use "start" with uid 1000, try with sudo`)
+}
+
func (s *ctlcmdSuite) TestRunNoArgsFailure(c *C) {
_, _, err := ctlcmd.Run(s.mockContext, []string{}, 0)
c.Check(err, NotNil)
--- /dev/null
+summary: Ensure that snapctl argument parser handles --
+
+details: |
+ Snapctl argument parser used to have a flaw related to parsing -- which
+ might have lead to incorrect interpretation of the following arguments.
+
+systems:
+ - -ubuntu-14.04-* # systemd is too old for generated mount units
+
+prepare: |
+ "$TESTSTOOLS"/snaps-state install-local test-snapd-sh
+ tests.cleanup defer snap remove --purge test-snapd-sh
+
+ snap connect test-snapd-sh:mount-control
+
+ mkdir -p /var/snap/test-snapd-sh/common/base-files
+ echo 'snapctl mount -o ro,bind,noatime,noexec /usr/share/base-files /var/snap/test-snapd-sh/common/base-files' | snap run --shell test-snapd-sh.sh
+ mountpoint /var/snap/test-snapd-sh/common/base-files
+ tests.cleanup defer umount /var/snap/test-snapd-sh/common/base-files
+
+ tests.session prepare -u test
+ tests.cleanup defer tests.session restore -u test
+
+execute: |
+ tests.session -u test exec snap run --shell test-snapd-sh.sh -c 'snapctl umount /var/snap/test-snapd-sh/common/base-files -- --help' 2>&1 | MATCH 'error: cannot use "umount" with uid 12345, try with sudo'
+ mountpoint /var/snap/test-snapd-sh/common/base-files
--- /dev/null
+#!/bin/sh
+PS1='$ '
+exec /bin/sh "$@"
--- /dev/null
+name: test-snapd-sh
+version: 1.0
+apps:
+ sh:
+ command: bin/sh
+plugs:
+ mount-control:
+ mount:
+ - what: /usr/share/base-files
+ where: $SNAP_COMMON/base-files
+ options:
+ - ro
+ - bind
+ - noatime
+ - noexec