github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/interfaces/apparmor/apparmor_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package apparmor_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path"
    26  	"path/filepath"
    27  	"testing"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/interfaces/apparmor"
    33  	"github.com/snapcore/snapd/osutil"
    34  	apparmor_sandbox "github.com/snapcore/snapd/sandbox/apparmor"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  func Test(t *testing.T) {
    39  	TestingT(t)
    40  }
    41  
    42  type appArmorSuite struct {
    43  	testutil.BaseTest
    44  	profilesFilename string
    45  }
    46  
    47  var _ = Suite(&appArmorSuite{})
    48  
    49  func (s *appArmorSuite) SetUpTest(c *C) {
    50  	s.BaseTest.SetUpTest(c)
    51  	// Mock the list of profiles in the running kernel
    52  	s.profilesFilename = path.Join(c.MkDir(), "profiles")
    53  	apparmor.MockProfilesPath(&s.BaseTest, s.profilesFilename)
    54  }
    55  
    56  // Tests for LoadProfiles()
    57  
    58  func (s *appArmorSuite) TestLoadProfilesRunsAppArmorParserReplace(c *C) {
    59  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    60  	defer cmd.Restore()
    61  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, apparmor_sandbox.CacheDir, 0)
    62  	c.Assert(err, IsNil)
    63  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    64  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd"},
    65  	})
    66  }
    67  
    68  func (s *appArmorSuite) TestLoadProfilesMany(c *C) {
    69  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    70  	defer cmd.Restore()
    71  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd", "/path/to/another.profile"}, apparmor_sandbox.CacheDir, 0)
    72  	c.Assert(err, IsNil)
    73  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    74  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd", "/path/to/another.profile"},
    75  	})
    76  }
    77  
    78  func (s *appArmorSuite) TestLoadProfilesNone(c *C) {
    79  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    80  	defer cmd.Restore()
    81  	err := apparmor.LoadProfiles([]string{}, apparmor_sandbox.CacheDir, 0)
    82  	c.Assert(err, IsNil)
    83  	c.Check(cmd.Calls(), HasLen, 0)
    84  }
    85  
    86  func (s *appArmorSuite) TestLoadProfilesReportsErrors(c *C) {
    87  	cmd := testutil.MockCommand(c, "apparmor_parser", "exit 42")
    88  	defer cmd.Restore()
    89  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, apparmor_sandbox.CacheDir, 0)
    90  	c.Assert(err.Error(), Equals, `cannot load apparmor profiles: exit status 42
    91  apparmor_parser output:
    92  `)
    93  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    94  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd"},
    95  	})
    96  }
    97  
    98  func (s *appArmorSuite) TestLoadProfilesRunsAppArmorParserReplaceWithSnapdDebug(c *C) {
    99  	os.Setenv("SNAPD_DEBUG", "1")
   100  	defer os.Unsetenv("SNAPD_DEBUG")
   101  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   102  	defer cmd.Restore()
   103  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, apparmor_sandbox.CacheDir, 0)
   104  	c.Assert(err, IsNil)
   105  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   106  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "/path/to/snap.samba.smbd"},
   107  	})
   108  }
   109  
   110  // Tests for Profile.Unload()
   111  
   112  func (s *appArmorSuite) TestUnloadProfilesMany(c *C) {
   113  	err := apparmor.UnloadProfiles([]string{"/path/to/snap.samba.smbd", "/path/to/another.profile"}, apparmor_sandbox.CacheDir)
   114  	c.Assert(err, IsNil)
   115  }
   116  
   117  func (s *appArmorSuite) TestUnloadProfilesNone(c *C) {
   118  	err := apparmor.UnloadProfiles([]string{}, apparmor_sandbox.CacheDir)
   119  	c.Assert(err, IsNil)
   120  }
   121  
   122  func (s *appArmorSuite) TestUnloadRemovesCachedProfile(c *C) {
   123  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   124  	defer cmd.Restore()
   125  
   126  	dirs.SetRootDir(c.MkDir())
   127  	defer dirs.SetRootDir("")
   128  	err := os.MkdirAll(apparmor_sandbox.CacheDir, 0755)
   129  	c.Assert(err, IsNil)
   130  
   131  	fname := filepath.Join(apparmor_sandbox.CacheDir, "profile")
   132  	ioutil.WriteFile(fname, []byte("blob"), 0600)
   133  	err = apparmor.UnloadProfiles([]string{"profile"}, apparmor_sandbox.CacheDir)
   134  	c.Assert(err, IsNil)
   135  	_, err = os.Stat(fname)
   136  	c.Check(os.IsNotExist(err), Equals, true)
   137  }
   138  
   139  func (s *appArmorSuite) TestUnloadRemovesCachedProfileInForest(c *C) {
   140  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   141  	defer cmd.Restore()
   142  
   143  	dirs.SetRootDir(c.MkDir())
   144  	defer dirs.SetRootDir("")
   145  	err := os.MkdirAll(apparmor_sandbox.CacheDir, 0755)
   146  	c.Assert(err, IsNil)
   147  	// mock the forest subdir and features file
   148  	subdir := filepath.Join(apparmor_sandbox.CacheDir, "deadbeef.0")
   149  	err = os.MkdirAll(subdir, 0700)
   150  	c.Assert(err, IsNil)
   151  	features := filepath.Join(subdir, ".features")
   152  	ioutil.WriteFile(features, []byte("blob"), 0644)
   153  
   154  	fname := filepath.Join(subdir, "profile")
   155  	ioutil.WriteFile(fname, []byte("blob"), 0600)
   156  	err = apparmor.UnloadProfiles([]string{"profile"}, apparmor_sandbox.CacheDir)
   157  	c.Assert(err, IsNil)
   158  	_, err = os.Stat(fname)
   159  	c.Check(os.IsNotExist(err), Equals, true)
   160  	c.Check(osutil.FileExists(features), Equals, true)
   161  }
   162  
   163  // Tests for LoadedProfiles()
   164  
   165  func (s *appArmorSuite) TestLoadedApparmorProfilesReturnsErrorOnMissingFile(c *C) {
   166  	profiles, err := apparmor.LoadedProfiles()
   167  	c.Assert(err, ErrorMatches, "open .*: no such file or directory")
   168  	c.Check(profiles, IsNil)
   169  }
   170  
   171  func (s *appArmorSuite) TestLoadedApparmorProfilesCanParseEmptyFile(c *C) {
   172  	ioutil.WriteFile(s.profilesFilename, []byte(""), 0600)
   173  	profiles, err := apparmor.LoadedProfiles()
   174  	c.Assert(err, IsNil)
   175  	c.Check(profiles, HasLen, 0)
   176  }
   177  
   178  func (s *appArmorSuite) TestLoadedApparmorProfilesParsesAndFiltersData(c *C) {
   179  	ioutil.WriteFile(s.profilesFilename, []byte(
   180  		// The output contains some of the snappy-specific elements
   181  		// and some non-snappy elements pulled from Ubuntu 16.04 desktop
   182  		//
   183  		// The pi2-piglow.{background,foreground}.snap entries are the only
   184  		// ones that should be reported by the function.
   185  		`/sbin/dhclient (enforce)
   186  /usr/bin/ubuntu-core-launcher (enforce)
   187  /usr/bin/ubuntu-core-launcher (enforce)
   188  /usr/lib/NetworkManager/nm-dhcp-client.action (enforce)
   189  /usr/lib/NetworkManager/nm-dhcp-helper (enforce)
   190  /usr/lib/connman/scripts/dhclient-script (enforce)
   191  /usr/lib/lightdm/lightdm-guest-session (enforce)
   192  /usr/lib/lightdm/lightdm-guest-session//chromium (enforce)
   193  /usr/lib/telepathy/telepathy-* (enforce)
   194  /usr/lib/telepathy/telepathy-*//pxgsettings (enforce)
   195  /usr/lib/telepathy/telepathy-*//sanitized_helper (enforce)
   196  snap.pi2-piglow.background (enforce)
   197  snap.pi2-piglow.foreground (enforce)
   198  webbrowser-app (enforce)
   199  webbrowser-app//oxide_helper (enforce)
   200  `), 0600)
   201  	profiles, err := apparmor.LoadedProfiles()
   202  	c.Assert(err, IsNil)
   203  	c.Check(profiles, DeepEquals, []string{
   204  		"snap.pi2-piglow.background",
   205  		"snap.pi2-piglow.foreground",
   206  	})
   207  }
   208  
   209  func (s *appArmorSuite) TestLoadedApparmorProfilesHandlesParsingErrors(c *C) {
   210  	ioutil.WriteFile(s.profilesFilename, []byte("broken stuff here\n"), 0600)
   211  	profiles, err := apparmor.LoadedProfiles()
   212  	c.Assert(err, ErrorMatches, "newline in format does not match input")
   213  	c.Check(profiles, IsNil)
   214  	ioutil.WriteFile(s.profilesFilename, []byte("truncated"), 0600)
   215  	profiles, err = apparmor.LoadedProfiles()
   216  	c.Assert(err, ErrorMatches, `syntax error, expected: name \(mode\)`)
   217  	c.Check(profiles, IsNil)
   218  }
   219  
   220  func (s *appArmorSuite) TestValidateFreeFromAAREUnhappy(c *C) {
   221  	var testCases = []string{"a?", "*b", "c[c", "dd]", "e{", "f}", "g^", `h"`, "f\000", "g\x00"}
   222  
   223  	for _, s := range testCases {
   224  		c.Check(apparmor.ValidateNoAppArmorRegexp(s), ErrorMatches, ".* contains a reserved apparmor char from .*", Commentf("%q is not raising an error", s))
   225  	}
   226  }
   227  
   228  func (s *appArmorSuite) TestValidateFreeFromAAREhappy(c *C) {
   229  	var testCases = []string{"foo", "BaR", "b-z", "foo+bar", "b00m!", "be/ep", "a%b", "a&b", "a(b", "a)b", "a=b", "a#b", "a~b", "a'b", "a_b", "a,b", "a;b", "a>b", "a<b", "a|b"}
   230  
   231  	for _, s := range testCases {
   232  		c.Check(apparmor.ValidateNoAppArmorRegexp(s), IsNil, Commentf("%q raised an error but shouldn't", s))
   233  	}
   234  }
   235  
   236  func (s *appArmorSuite) TestMaybeSetNumberOfJobs(c *C) {
   237  	var cpus int
   238  	restore := apparmor.MockRuntimeNumCPU(func() int {
   239  		return cpus
   240  	})
   241  	defer restore()
   242  
   243  	cpus = 10
   244  	c.Check(apparmor.MaybeSetNumberOfJobs(), Equals, "-j8")
   245  
   246  	cpus = 2
   247  	c.Check(apparmor.MaybeSetNumberOfJobs(), Equals, "-j1")
   248  
   249  	cpus = 1
   250  	c.Check(apparmor.MaybeSetNumberOfJobs(), Equals, "-j1")
   251  }