github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  	"github.com/snapcore/snapd/testutil"
    35  )
    36  
    37  func Test(t *testing.T) {
    38  	TestingT(t)
    39  }
    40  
    41  type appArmorSuite struct {
    42  	testutil.BaseTest
    43  	profilesFilename string
    44  }
    45  
    46  var _ = Suite(&appArmorSuite{})
    47  
    48  func (s *appArmorSuite) SetUpTest(c *C) {
    49  	s.BaseTest.SetUpTest(c)
    50  	// Mock the list of profiles in the running kernel
    51  	s.profilesFilename = path.Join(c.MkDir(), "profiles")
    52  	apparmor.MockProfilesPath(&s.BaseTest, s.profilesFilename)
    53  }
    54  
    55  // Tests for LoadProfiles()
    56  
    57  func (s *appArmorSuite) TestLoadProfilesRunsAppArmorParserReplace(c *C) {
    58  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    59  	defer cmd.Restore()
    60  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, dirs.AppArmorCacheDir, 0)
    61  	c.Assert(err, IsNil)
    62  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    63  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd"},
    64  	})
    65  }
    66  
    67  func (s *appArmorSuite) TestLoadProfilesMany(c *C) {
    68  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    69  	defer cmd.Restore()
    70  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd", "/path/to/another.profile"}, dirs.AppArmorCacheDir, 0)
    71  	c.Assert(err, IsNil)
    72  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    73  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd", "/path/to/another.profile"},
    74  	})
    75  }
    76  
    77  func (s *appArmorSuite) TestLoadProfilesNone(c *C) {
    78  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
    79  	defer cmd.Restore()
    80  	err := apparmor.LoadProfiles([]string{}, dirs.AppArmorCacheDir, 0)
    81  	c.Assert(err, IsNil)
    82  	c.Check(cmd.Calls(), HasLen, 0)
    83  }
    84  
    85  func (s *appArmorSuite) TestLoadProfilesReportsErrors(c *C) {
    86  	cmd := testutil.MockCommand(c, "apparmor_parser", "exit 42")
    87  	defer cmd.Restore()
    88  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, dirs.AppArmorCacheDir, 0)
    89  	c.Assert(err.Error(), Equals, `cannot load apparmor profiles: exit status 42
    90  apparmor_parser output:
    91  `)
    92  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
    93  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "--quiet", "/path/to/snap.samba.smbd"},
    94  	})
    95  }
    96  
    97  func (s *appArmorSuite) TestLoadProfilesRunsAppArmorParserReplaceWithSnapdDebug(c *C) {
    98  	os.Setenv("SNAPD_DEBUG", "1")
    99  	defer os.Unsetenv("SNAPD_DEBUG")
   100  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   101  	defer cmd.Restore()
   102  	err := apparmor.LoadProfiles([]string{"/path/to/snap.samba.smbd"}, dirs.AppArmorCacheDir, 0)
   103  	c.Assert(err, IsNil)
   104  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   105  		{"apparmor_parser", "--replace", "--write-cache", "-O", "no-expr-simplify", "--cache-loc=/var/cache/apparmor", "/path/to/snap.samba.smbd"},
   106  	})
   107  }
   108  
   109  // Tests for Profile.Unload()
   110  
   111  func (s *appArmorSuite) TestUnloadProfilesMany(c *C) {
   112  	err := apparmor.UnloadProfiles([]string{"/path/to/snap.samba.smbd", "/path/to/another.profile"}, dirs.AppArmorCacheDir)
   113  	c.Assert(err, IsNil)
   114  }
   115  
   116  func (s *appArmorSuite) TestUnloadProfilesNone(c *C) {
   117  	err := apparmor.UnloadProfiles([]string{}, dirs.AppArmorCacheDir)
   118  	c.Assert(err, IsNil)
   119  }
   120  
   121  func (s *appArmorSuite) TestUnloadRemovesCachedProfile(c *C) {
   122  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   123  	defer cmd.Restore()
   124  
   125  	dirs.SetRootDir(c.MkDir())
   126  	defer dirs.SetRootDir("")
   127  	err := os.MkdirAll(dirs.AppArmorCacheDir, 0755)
   128  	c.Assert(err, IsNil)
   129  
   130  	fname := filepath.Join(dirs.AppArmorCacheDir, "profile")
   131  	ioutil.WriteFile(fname, []byte("blob"), 0600)
   132  	err = apparmor.UnloadProfiles([]string{"profile"}, dirs.AppArmorCacheDir)
   133  	c.Assert(err, IsNil)
   134  	_, err = os.Stat(fname)
   135  	c.Check(os.IsNotExist(err), Equals, true)
   136  }
   137  
   138  func (s *appArmorSuite) TestUnloadRemovesCachedProfileInForest(c *C) {
   139  	cmd := testutil.MockCommand(c, "apparmor_parser", "")
   140  	defer cmd.Restore()
   141  
   142  	dirs.SetRootDir(c.MkDir())
   143  	defer dirs.SetRootDir("")
   144  	err := os.MkdirAll(dirs.AppArmorCacheDir, 0755)
   145  	c.Assert(err, IsNil)
   146  	// mock the forest subdir and features file
   147  	subdir := filepath.Join(dirs.AppArmorCacheDir, "deadbeef.0")
   148  	err = os.MkdirAll(subdir, 0700)
   149  	c.Assert(err, IsNil)
   150  	features := filepath.Join(subdir, ".features")
   151  	ioutil.WriteFile(features, []byte("blob"), 0644)
   152  
   153  	fname := filepath.Join(subdir, "profile")
   154  	ioutil.WriteFile(fname, []byte("blob"), 0600)
   155  	err = apparmor.UnloadProfiles([]string{"profile"}, dirs.AppArmorCacheDir)
   156  	c.Assert(err, IsNil)
   157  	_, err = os.Stat(fname)
   158  	c.Check(os.IsNotExist(err), Equals, true)
   159  	c.Check(osutil.FileExists(features), Equals, true)
   160  }
   161  
   162  // Tests for LoadedProfiles()
   163  
   164  func (s *appArmorSuite) TestLoadedApparmorProfilesReturnsErrorOnMissingFile(c *C) {
   165  	profiles, err := apparmor.LoadedProfiles()
   166  	c.Assert(err, ErrorMatches, "open .*: no such file or directory")
   167  	c.Check(profiles, IsNil)
   168  }
   169  
   170  func (s *appArmorSuite) TestLoadedApparmorProfilesCanParseEmptyFile(c *C) {
   171  	ioutil.WriteFile(s.profilesFilename, []byte(""), 0600)
   172  	profiles, err := apparmor.LoadedProfiles()
   173  	c.Assert(err, IsNil)
   174  	c.Check(profiles, HasLen, 0)
   175  }
   176  
   177  func (s *appArmorSuite) TestLoadedApparmorProfilesParsesAndFiltersData(c *C) {
   178  	ioutil.WriteFile(s.profilesFilename, []byte(
   179  		// The output contains some of the snappy-specific elements
   180  		// and some non-snappy elements pulled from Ubuntu 16.04 desktop
   181  		//
   182  		// The pi2-piglow.{background,foreground}.snap entries are the only
   183  		// ones that should be reported by the function.
   184  		`/sbin/dhclient (enforce)
   185  /usr/bin/ubuntu-core-launcher (enforce)
   186  /usr/bin/ubuntu-core-launcher (enforce)
   187  /usr/lib/NetworkManager/nm-dhcp-client.action (enforce)
   188  /usr/lib/NetworkManager/nm-dhcp-helper (enforce)
   189  /usr/lib/connman/scripts/dhclient-script (enforce)
   190  /usr/lib/lightdm/lightdm-guest-session (enforce)
   191  /usr/lib/lightdm/lightdm-guest-session//chromium (enforce)
   192  /usr/lib/telepathy/telepathy-* (enforce)
   193  /usr/lib/telepathy/telepathy-*//pxgsettings (enforce)
   194  /usr/lib/telepathy/telepathy-*//sanitized_helper (enforce)
   195  snap.pi2-piglow.background (enforce)
   196  snap.pi2-piglow.foreground (enforce)
   197  webbrowser-app (enforce)
   198  webbrowser-app//oxide_helper (enforce)
   199  `), 0600)
   200  	profiles, err := apparmor.LoadedProfiles()
   201  	c.Assert(err, IsNil)
   202  	c.Check(profiles, DeepEquals, []string{
   203  		"snap.pi2-piglow.background",
   204  		"snap.pi2-piglow.foreground",
   205  	})
   206  }
   207  
   208  func (s *appArmorSuite) TestLoadedApparmorProfilesHandlesParsingErrors(c *C) {
   209  	ioutil.WriteFile(s.profilesFilename, []byte("broken stuff here\n"), 0600)
   210  	profiles, err := apparmor.LoadedProfiles()
   211  	c.Assert(err, ErrorMatches, "newline in format does not match input")
   212  	c.Check(profiles, IsNil)
   213  	ioutil.WriteFile(s.profilesFilename, []byte("truncated"), 0600)
   214  	profiles, err = apparmor.LoadedProfiles()
   215  	c.Assert(err, ErrorMatches, `syntax error, expected: name \(mode\)`)
   216  	c.Check(profiles, IsNil)
   217  }
   218  
   219  func (s *appArmorSuite) TestValidateFreeFromAAREUnhappy(c *C) {
   220  	var testCases = []string{"a?", "*b", "c[c", "dd]", "e{", "f}", "g^", `h"`, "f\000", "g\x00"}
   221  
   222  	for _, s := range testCases {
   223  		c.Check(apparmor.ValidateNoAppArmorRegexp(s), ErrorMatches, ".* contains a reserved apparmor char from .*", Commentf("%q is not raising an error", s))
   224  	}
   225  }
   226  
   227  func (s *appArmorSuite) TestValidateFreeFromAAREhappy(c *C) {
   228  	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"}
   229  
   230  	for _, s := range testCases {
   231  		c.Check(apparmor.ValidateNoAppArmorRegexp(s), IsNil, Commentf("%q raised an error but shouldn't", s))
   232  	}
   233  }