github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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 "github.com/snapcore/snapd/cmd/snap-update-ns" 28 "github.com/snapcore/snapd/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 }