gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap-update-ns/secure_bindmount_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 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 main_test
    21  
    22  import (
    23  	"syscall"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	update "gitee.com/mysnapcore/mysnapd/cmd/snap-update-ns"
    28  	"gitee.com/mysnapcore/mysnapd/testutil"
    29  )
    30  
    31  type secureBindMountSuite struct {
    32  	testutil.BaseTest
    33  	sys *testutil.SyscallRecorder
    34  }
    35  
    36  var _ = Suite(&secureBindMountSuite{})
    37  
    38  func (s *secureBindMountSuite) SetUpTest(c *C) {
    39  	s.BaseTest.SetUpTest(c)
    40  	s.sys = &testutil.SyscallRecorder{}
    41  	s.BaseTest.AddCleanup(update.MockSystemCalls(s.sys))
    42  }
    43  
    44  func (s *secureBindMountSuite) TearDownTest(c *C) {
    45  	s.sys.CheckForStrayDescriptors(c)
    46  	s.BaseTest.TearDownTest(c)
    47  }
    48  
    49  func (s *secureBindMountSuite) TestMount(c *C) {
    50  	s.sys.InsertFstatResult(`fstat 5 <ptr>`, syscall.Stat_t{})
    51  	s.sys.InsertFstatResult(`fstat 6 <ptr>`, syscall.Stat_t{})
    52  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND)
    53  	c.Assert(err, IsNil)
    54  	c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
    55  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
    56  		{C: `openat 3 "source" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
    57  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 5},
    58  		{C: `fstat 5 <ptr>`, R: syscall.Stat_t{}},
    59  		{C: `close 4`}, // "/source"
    60  		{C: `close 3`}, // "/"
    61  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
    62  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
    63  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 6},
    64  		{C: `fstat 6 <ptr>`, R: syscall.Stat_t{}},
    65  		{C: `close 4`}, // "/target"
    66  		{C: `close 3`}, // "/"
    67  		{C: `mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND ""`},
    68  		{C: `close 6`}, // "/target/dir"
    69  		{C: `close 5`}, // "/source/dir"
    70  	})
    71  }
    72  
    73  func (s *secureBindMountSuite) TestMountRecursive(c *C) {
    74  	s.sys.InsertFstatResult(`fstat 5 <ptr>`, syscall.Stat_t{})
    75  	s.sys.InsertFstatResult(`fstat 6 <ptr>`, syscall.Stat_t{})
    76  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND|syscall.MS_REC)
    77  	c.Assert(err, IsNil)
    78  	c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
    79  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
    80  		{C: `openat 3 "source" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
    81  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 5},
    82  		{C: `fstat 5 <ptr>`, R: syscall.Stat_t{}},
    83  		{C: `close 4`}, // "/source"
    84  		{C: `close 3`}, // "/"
    85  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
    86  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
    87  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 6},
    88  		{C: `fstat 6 <ptr>`, R: syscall.Stat_t{}},
    89  		{C: `close 4`}, // "/target"
    90  		{C: `close 3`}, // "/"
    91  		{C: `mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND|MS_REC ""`},
    92  		{C: `close 6`}, // "/target/dir"
    93  		{C: `close 5`}, // "/source/dir"
    94  	})
    95  }
    96  
    97  func (s *secureBindMountSuite) TestMountReadOnly(c *C) {
    98  	s.sys.InsertFstatResult(`fstat 5 <ptr>`, syscall.Stat_t{})
    99  	s.sys.InsertFstatResult(`fstat 6 <ptr>`, syscall.Stat_t{})
   100  	s.sys.InsertFstatResult(`fstat 7 <ptr>`, syscall.Stat_t{})
   101  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND|syscall.MS_RDONLY)
   102  	c.Assert(err, IsNil)
   103  	c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
   104  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   105  		{C: `openat 3 "source" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   106  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 5},
   107  		{C: `fstat 5 <ptr>`, R: syscall.Stat_t{}},
   108  		{C: `close 4`}, // "/source"
   109  		{C: `close 3`}, // "/"
   110  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   111  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   112  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 6},
   113  		{C: `fstat 6 <ptr>`, R: syscall.Stat_t{}},
   114  		{C: `close 4`}, // "/target"
   115  		{C: `close 3`}, // "/"
   116  		{C: `mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND ""`},
   117  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   118  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   119  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 7},
   120  		{C: `fstat 7 <ptr>`, R: syscall.Stat_t{}},
   121  		{C: `close 4`}, // "/target"
   122  		{C: `close 3`}, // "/"
   123  		{C: `mount "none" "/proc/self/fd/7" "" MS_REMOUNT|MS_BIND|MS_RDONLY ""`},
   124  		{C: `close 7`}, // "/target/dir"
   125  		{C: `close 6`}, // "/target/dir"
   126  		{C: `close 5`}, // "/source/dir"
   127  	})
   128  }
   129  
   130  func (s *secureBindMountSuite) TestBindFlagRequired(c *C) {
   131  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_REC)
   132  	c.Assert(err, ErrorMatches, "cannot perform non-bind mount operation")
   133  	c.Check(s.sys.RCalls(), HasLen, 0)
   134  }
   135  
   136  func (s *secureBindMountSuite) TestMountReadOnlyRecursive(c *C) {
   137  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_REC)
   138  	c.Assert(err, ErrorMatches, "cannot use MS_RDONLY and MS_REC together")
   139  	c.Check(s.sys.RCalls(), HasLen, 0)
   140  }
   141  
   142  func (s *secureBindMountSuite) TestBindMountFails(c *C) {
   143  	s.sys.InsertFstatResult(`fstat 5 <ptr>`, syscall.Stat_t{})
   144  	s.sys.InsertFstatResult(`fstat 6 <ptr>`, syscall.Stat_t{})
   145  	s.sys.InsertFault(`mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND ""`, errTesting)
   146  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND|syscall.MS_RDONLY)
   147  	c.Assert(err, ErrorMatches, "testing")
   148  	c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
   149  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   150  		{C: `openat 3 "source" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   151  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 5},
   152  		{C: `fstat 5 <ptr>`, R: syscall.Stat_t{}},
   153  		{C: `close 4`}, // "/source"
   154  		{C: `close 3`}, // "/"
   155  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   156  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   157  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 6},
   158  		{C: `fstat 6 <ptr>`, R: syscall.Stat_t{}},
   159  		{C: `close 4`}, // "/target"
   160  		{C: `close 3`}, // "/"
   161  		{C: `mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND ""`, E: errTesting},
   162  		{C: `close 6`}, // "/target/dir"
   163  		{C: `close 5`}, // "/source/dir"
   164  	})
   165  }
   166  
   167  func (s *secureBindMountSuite) TestRemountReadOnlyFails(c *C) {
   168  	s.sys.InsertFstatResult(`fstat 5 <ptr>`, syscall.Stat_t{})
   169  	s.sys.InsertFstatResult(`fstat 6 <ptr>`, syscall.Stat_t{})
   170  	s.sys.InsertFstatResult(`fstat 7 <ptr>`, syscall.Stat_t{})
   171  	s.sys.InsertFault(`mount "none" "/proc/self/fd/7" "" MS_REMOUNT|MS_BIND|MS_RDONLY ""`, errTesting)
   172  	err := update.BindMount("/source/dir", "/target/dir", syscall.MS_BIND|syscall.MS_RDONLY)
   173  	c.Assert(err, ErrorMatches, "testing")
   174  	c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
   175  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   176  		{C: `openat 3 "source" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   177  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 5},
   178  		{C: `fstat 5 <ptr>`, R: syscall.Stat_t{}},
   179  		{C: `close 4`}, // "/source"
   180  		{C: `close 3`}, // "/"
   181  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   182  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   183  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 6},
   184  		{C: `fstat 6 <ptr>`, R: syscall.Stat_t{}},
   185  		{C: `close 4`}, // "/target"
   186  		{C: `close 3`}, // "/"
   187  		{C: `mount "/proc/self/fd/5" "/proc/self/fd/6" "" MS_BIND ""`},
   188  		{C: `open "/" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 3},
   189  		{C: `openat 3 "target" O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH 0`, R: 4},
   190  		{C: `openat 4 "dir" O_NOFOLLOW|O_CLOEXEC|O_PATH 0`, R: 7},
   191  		{C: `fstat 7 <ptr>`, R: syscall.Stat_t{}},
   192  		{C: `close 4`}, // "/target"
   193  		{C: `close 3`}, // "/"
   194  		{C: `mount "none" "/proc/self/fd/7" "" MS_REMOUNT|MS_BIND|MS_RDONLY ""`, E: errTesting},
   195  		{C: `unmount "/proc/self/fd/7" UMOUNT_NOFOLLOW|MNT_DETACH`},
   196  		{C: `close 7`}, // "/target/dir"
   197  		{C: `close 6`}, // "/target/dir"
   198  		{C: `close 5`}, // "/source/dir"
   199  	})
   200  }