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

release: support probing SELinux state #6286

Merged
merged 10 commits into from Jan 9, 2019
10 changes: 10 additions & 0 deletions release/export_test.go
Expand Up @@ -58,6 +58,14 @@ func MockIoutilReadfile(newReadfile func(string) ([]byte, error)) (restorer func
}
}

func MockSELinuxIsEnforcing(isEnforcing func() (bool, error)) (restore func()) {
old := selinuxIsEnforcing
selinuxIsEnforcing = isEnforcing
return func() {
selinuxIsEnforcing = old
}
}

var (
ProbeAppArmorKernelFeatures = probeAppArmorKernelFeatures
ProbeAppArmorParserFeatures = probeAppArmorParserFeatures
Expand All @@ -68,6 +76,8 @@ var (
PreferredAppArmorParserFeatures = preferredAppArmorParserFeatures

IsWSL = isWSL

ProbeSELinux = probeSELinux
)

func FreshAppArmorAssessment() {
Expand Down
90 changes: 90 additions & 0 deletions release/selinux.go
@@ -0,0 +1,90 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2014-2018 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package release

import (
"fmt"

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

// SELinuxLevelType encodes the state of SELinux support found on this system.
type SELinuxLevelType int

const (
// NoSELinux indicates that SELinux is not enabled
NoSELinux SELinuxLevelType = iota
// SELinux is supported and in permissive mode
SELinuxPermissive
// SELinux is supported and in enforcing mode
SELinuxEnforcing
)

var (
selinuxIsEnabled = selinux.IsEnabled
selinuxIsEnforcing = selinux.IsEnforcing
)

// SELinuxLevel tells what level of SELinux enforcement is currently used
func SELinuxLevel() SELinuxLevelType {
level, _ := probeSELinux()
return level
}

// SELinuxSummary describes SELinux status
func SELinuxSummary() string {
_, summary := probeSELinux()
return summary
}

// SELinuxStatus returns the current level of SELinux support and a descriptive
// summary
func SELinuxStatus() (level SELinuxLevelType, summary string) {
return probeSELinux()
}

func probeSELinux() (SELinuxLevelType, string) {
enabled, err := selinuxIsEnabled()
if err != nil {
return NoSELinux, err.Error()
}
if !enabled {
return NoSELinux, ""
}

enforcing, err := selinuxIsEnforcing()
if err != nil {
return NoSELinux, fmt.Sprintf("SELinux is enabled, but status cannot be determined: %v", err)
}
if !enforcing {
return SELinuxPermissive, "SELinux is enabled and in permissive mode"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just a small nitpick about wording in this case:

Suggested change
return SELinuxPermissive, "SELinux is enabled and in permissive mode"
return SELinuxPermissive, "SELinux is enabled but in permissive mode"

}
return SELinuxEnforcing, "SELinux is enabled and in enforcing mode"
}

// MockSELinuxIsEnabled makes the system believe a certain SELinux state is
// currently true
func MockSELinuxIsEnabled(isEnabled func() (bool, error)) (restore func()) {
old := selinuxIsEnabled
selinuxIsEnabled = isEnabled
return func() {
selinuxIsEnabled = old
}
}
98 changes: 98 additions & 0 deletions release/selinux_test.go
@@ -0,0 +1,98 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2014-2018 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package release_test

import (
"errors"

. "gopkg.in/check.v1"

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

type selinuxSuite struct{}

var _ = Suite(&selinuxSuite{})

func (s *selinuxSuite) TestProbeNone(c *C) {
restore := release.MockSELinuxIsEnabled(func() (bool, error) { return false, nil })
defer restore()

level, status := release.ProbeSELinux()
c.Assert(level, Equals, release.NoSELinux)
c.Assert(status, Equals, "")

c.Assert(release.SELinuxLevel(), Equals, level)
c.Assert(release.SELinuxSummary(), Equals, status)
}

func (s *selinuxSuite) TestProbeEnforcingHappy(c *C) {
restore := release.MockSELinuxIsEnabled(func() (bool, error) { return true, nil })
defer restore()
restore = release.MockSELinuxIsEnforcing(func() (bool, error) { return true, nil })
defer restore()

level, status := release.ProbeSELinux()
c.Assert(level, Equals, release.SELinuxEnforcing)
c.Assert(status, Equals, "SELinux is enabled and in enforcing mode")

c.Assert(release.SELinuxLevel(), Equals, level)
c.Assert(release.SELinuxSummary(), Equals, status)
}

func (s *selinuxSuite) TestProbeEnabledError(c *C) {
restore := release.MockSELinuxIsEnabled(func() (bool, error) { return true, errors.New("so much fail") })
defer restore()

level, status := release.ProbeSELinux()
c.Assert(level, Equals, release.NoSELinux)
c.Assert(status, Equals, "so much fail")

c.Assert(release.SELinuxLevel(), Equals, level)
c.Assert(release.SELinuxSummary(), Equals, status)
}

func (s *selinuxSuite) TestProbeEnforcingError(c *C) {
restore := release.MockSELinuxIsEnabled(func() (bool, error) { return true, nil })
defer restore()
restore = release.MockSELinuxIsEnforcing(func() (bool, error) { return true, errors.New("so much fail") })
defer restore()

level, status := release.ProbeSELinux()
c.Assert(level, Equals, release.NoSELinux)
c.Assert(status, Equals, "SELinux is enabled, but status cannot be determined: so much fail")

c.Assert(release.SELinuxLevel(), Equals, level)
c.Assert(release.SELinuxSummary(), Equals, status)
}

func (s *selinuxSuite) TestProbePermissive(c *C) {
restore := release.MockSELinuxIsEnabled(func() (bool, error) { return true, nil })
defer restore()
restore = release.MockSELinuxIsEnforcing(func() (bool, error) { return false, nil })
defer restore()

level, status := release.ProbeSELinux()
c.Assert(level, Equals, release.SELinuxPermissive)
c.Assert(status, Equals, "SELinux is enabled and in permissive mode")
Copy link
Collaborator

Choose a reason for hiding this comment

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

To match the suggestion above:

Suggested change
c.Assert(status, Equals, "SELinux is enabled and in permissive mode")
c.Assert(status, Equals, "SELinux is enabled but in permissive mode")


c.Assert(release.SELinuxLevel(), Equals, level)
c.Assert(release.SELinuxSummary(), Equals, status)
}