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

selinux: package to query SELinux status and verify/restore file contexts #6285

Merged
merged 7 commits into from Dec 13, 2018

Conversation

bboozzoo
Copy link
Collaborator

Adding a new package for working with SELinux.

There is basic functionality to query SELinux state, enabled and
permissive/enforcing. The code founds out where the selinux filesystem is
mounted and uses that path to query the status, rather than hardcoding
/sys/fs/selinux.

The package has additional helpers to verify whether a path is labeled accroding
it its default context and possibly restore the context. Trying to avoid calling
to libselinux directly, the command line tools matchpathcon and restorecon
are used.

…exts

Adding a new package for working with SELinux.

There is basic functionality to query SELinux state, enabled and
permissive/enforcing. The code founds out where the selinux filesystem is
mounted and uses that path to query the status, rather than hardcoding
/sys/fs/selinux.

The package has additional helpers to verify whether a path is labeled accroding
it its default context and possibly restore the context. Trying to avoid calling
to libselinux directly, the command line tools `matchpathcon` and `restorecon`
are used.

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
…arwin

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>

// Verifypathcon checks whether a given path is labeled according to its default
// SELinux context
func Verifypathcon(aPath string) (bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func Verifypathcon(aPath string) (bool, error) {
func VerifyPathContext(aPath string) (bool, error) {

*/
package selinux

// Verifypathcon checks whether a given path is labeled according to its default
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Verifypathcon checks whether a given path is labeled according to its default
// VerifyPathContext checks whether a given path is labeled according to its default

}

// Restorecon restores the default SELinux context of given path
func Restorecon(aPath string, recursive bool) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func Restorecon(aPath string, recursive bool) error {
func RestoreContext(aPath string, recursive bool) error {

"github.com/snapcore/snapd/osutil"
)

var (
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: drop the var ( ... ) and use just one-liner var ...


// Verifypathcon checks whether a given path is labeled according to its default
// SELinux context
func Verifypathcon(aPath string) (bool, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func Verifypathcon(aPath string) (bool, error) {
func VerifyPathContext(aPath string) (bool, error) {

matchIncorrectLabel = regexp.MustCompile("^.* has context .* should be .*\n$")
)

// Verifypathcon checks whether a given path is labeled according to its default
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Verifypathcon checks whether a given path is labeled according to its default
// VerifyPathContext checks whether a given path is labeled according to its default

return false, err
}

// Restorecon restores the default SELinux context of given path
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Restorecon restores the default SELinux context of given path
// RestoreContext restores the default SELinux context of given path

}

// Restorecon restores the default SELinux context of given path
func Restorecon(aPath string, recursive bool) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func Restorecon(aPath string, recursive bool) error {
func RestoreContext(aPath string, recursive bool) error {

return err
}
// if restorecon is found we may restore SELinux context
restoreconPath, err := exec.LookPath("restorecon")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason you are going through the hook of calling LookPath yourself? exec.Command will do that for you unless you provide an absolute path.

Copy link
Collaborator Author

@bboozzoo bboozzoo Dec 12, 2018

Choose a reason for hiding this comment

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

It was left behind after some earlier refactoring. I'll drop it in the next push.

cmd := testutil.MockCommand(c, "matchpathcon", "")
defer cmd.Restore()

ok, err := selinux.Verifypathcon(l.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon(l.path)
ok, err := selinux.VerifyPathContext(l.path)

cmd := testutil.MockCommand(c, "matchpathcon", "echo gibberish; exit 1 ")
defer cmd.Restore()

ok, err := selinux.Verifypathcon(l.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon(l.path)
ok, err := selinux.VerifyPathContext(l.path)

cmd := testutil.MockCommand(c, "matchpathcon", "echo gibberish; exit 5 ")
defer cmd.Restore()

ok, err := selinux.Verifypathcon(l.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon(l.path)
ok, err := selinux.VerifyPathContext(l.path)

cmd := testutil.MockCommand(c, "matchpathcon", ``)
defer cmd.Restore()

ok, err := selinux.Verifypathcon("does-not-exist")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon("does-not-exist")
ok, err := selinux.VerifyPathContext("does-not-exist")

}

func (l *labelSuite) TestVerifyFailNoTool(c *check.C) {
ok, err := selinux.Verifypathcon(l.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon(l.path)
ok, err := selinux.VerifyPathContext(l.path)

exit 1`, l.path))
defer cmd.Restore()

ok, err := selinux.Verifypathcon(l.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ok, err := selinux.Verifypathcon(l.path)
ok, err := selinux.VerifyPathContext(l.path)

cmd := testutil.MockCommand(c, "restorecon", "")
defer cmd.Restore()

err := selinux.Restorecon(l.path, false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
err := selinux.Restorecon(l.path, false)
err := selinux.RestoreContext(l.path, false)


cmd.ForgetCalls()

err = selinux.Restorecon(l.path, true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
err = selinux.Restorecon(l.path, true)
err = selinux.RestoreContext(l.path, true)

}

func (l *labelSuite) TestRestoreFail(c *check.C) {
err := selinux.Restorecon(l.path, false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
err := selinux.Restorecon(l.path, false)
err := selinux.RestoreContext(l.path, false)

cmd := testutil.MockCommand(c, "restorecon", "exit 1")
defer cmd.Restore()

err = selinux.Restorecon(l.path, false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
err = selinux.Restorecon(l.path, false)
err = selinux.RestoreContext(l.path, false)

err = selinux.Restorecon(l.path, false)
c.Assert(err, check.ErrorMatches, "exit status 1")

err = selinux.Restorecon("does-not-exist", false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
err = selinux.Restorecon("does-not-exist", false)
err = selinux.RestoreContext("does-not-exist", false)

Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

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

Looks very nice overall.

I made a small swarm of suggestions for names that are more natural and not match the underlaying commands.

LGTM

Renames suggested during the review (thanks @zyga!)

Extend the comment on matchpathcon.

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
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.

looks good overall but left some comments

}

// RestoreContext restores the default SELinux context of given path
func RestoreContext(aPath string, recursive bool) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we have a on element option struct to avoid the unclear bool only argument?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Pushed

// <the-path> has context <some-context>, should be <some-other-context>
// match the message so that we can distinguish a failed verification
// case from other errors
if exit == 1 && matchIncorrectLabel.Match(out) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

seems we need a test that calls the real tool if available to test this bit, otherwise we are just checking the expected output against again our own expectations, if the tool output changes nothing breaks here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Calling the actual tool we'd have to have the policy module installed in the host system. The only way to verify this reasonably is a spread test that does the same thing I'm doing locally: mkdir ~/snap/foo && chcon -t user_home_t -R ~/snap && snap run <snap> && ls -RZ ~/snap.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We definitely want some kind of test (here or in the one using this) that tests this bit as directly/with as little layers as possible. Otherwise the other approach would be to write our own tool in C using libselinux, then we control the format completely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've pushed a spread test to #6265 where this API is actually exercised. That's probably as much as can be done now until #6270 lands.

@@ -0,0 +1,30 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
Copy link
Collaborator

Choose a reason for hiding this comment

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

we need the per-plaform impls because we will use selinux in the cmd/snap code, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes

c.Assert(ok, check.Equals, false)
}

func (l *labelSuite) TestVerifyFailNoTool(c *check.C) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this will fail on fedora like systems, no? aren't we running unit tests there?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. I'll need to skip this iff the tool is already present. AFAIK we run unit tests only Ubuntu and Debian

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
Replace a naked bool passed to RestoreContext with a struct to make the code
easier to read/follow.

Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
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

}

// RestoreContext restores the default SELinux context of given path
func RestoreContext(aPath string, mode RestoreMode) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

we tend to use pointer for option structs so that nil can be passed and mean the default as well but no strong opinion for this particular case

@bboozzoo bboozzoo merged commit 6a2be91 into snapcore:master Dec 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants