github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/user/user_test.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package user 16 17 import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "github.com/SagerNet/gvisor/pkg/abi/linux" 23 "github.com/SagerNet/gvisor/pkg/context" 24 "github.com/SagerNet/gvisor/pkg/sentry/fs" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs/tmpfs" 26 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 27 "github.com/SagerNet/gvisor/pkg/sentry/kernel/contexttest" 28 "github.com/SagerNet/gvisor/pkg/usermem" 29 ) 30 31 // createEtcPasswd creates /etc/passwd with the given contents and mode. If 32 // mode is empty, then no file will be created. If mode is not a regular file 33 // mode, then contents is ignored. 34 func createEtcPasswd(ctx context.Context, root *fs.Dirent, contents string, mode linux.FileMode) error { 35 if err := root.CreateDirectory(ctx, root, "etc", fs.FilePermsFromMode(0755)); err != nil { 36 return err 37 } 38 etc, err := root.Walk(ctx, root, "etc") 39 if err != nil { 40 return err 41 } 42 defer etc.DecRef(ctx) 43 switch mode.FileType() { 44 case 0: 45 // Don't create anything. 46 return nil 47 case linux.S_IFREG: 48 passwd, err := etc.Create(ctx, root, "passwd", fs.FileFlags{Write: true}, fs.FilePermsFromMode(mode)) 49 if err != nil { 50 return err 51 } 52 defer passwd.DecRef(ctx) 53 if _, err := passwd.Writev(ctx, usermem.BytesIOSequence([]byte(contents))); err != nil { 54 return err 55 } 56 return nil 57 case linux.S_IFDIR: 58 return etc.CreateDirectory(ctx, root, "passwd", fs.FilePermsFromMode(mode)) 59 case linux.S_IFIFO: 60 return etc.CreateFifo(ctx, root, "passwd", fs.FilePermsFromMode(mode)) 61 default: 62 return fmt.Errorf("unknown file type %x", mode.FileType()) 63 } 64 } 65 66 // TestGetExecUserHome tests the getExecUserHome function. 67 func TestGetExecUserHome(t *testing.T) { 68 tests := map[string]struct { 69 uid auth.KUID 70 passwdContents string 71 passwdMode linux.FileMode 72 expected string 73 }{ 74 "success": { 75 uid: 1000, 76 passwdContents: "adin::1000:1111::/home/adin:/bin/sh", 77 passwdMode: linux.S_IFREG | 0666, 78 expected: "/home/adin", 79 }, 80 "no_perms": { 81 uid: 1000, 82 passwdContents: "adin::1000:1111::/home/adin:/bin/sh", 83 passwdMode: linux.S_IFREG, 84 expected: "/", 85 }, 86 "no_passwd": { 87 uid: 1000, 88 expected: "/", 89 }, 90 "directory": { 91 uid: 1000, 92 passwdMode: linux.S_IFDIR | 0666, 93 expected: "/", 94 }, 95 // Currently we don't allow named pipes. 96 "named_pipe": { 97 uid: 1000, 98 passwdMode: linux.S_IFIFO | 0666, 99 expected: "/", 100 }, 101 } 102 103 for name, tc := range tests { 104 t.Run(name, func(t *testing.T) { 105 ctx := contexttest.Context(t) 106 msrc := fs.NewPseudoMountSource(ctx) 107 rootInode, err := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, nil /* parent */) 108 if err != nil { 109 t.Fatalf("tmpfs.NewDir failed: %v", err) 110 } 111 112 mns, err := fs.NewMountNamespace(ctx, rootInode) 113 if err != nil { 114 t.Fatalf("NewMountNamespace failed: %v", err) 115 } 116 defer mns.DecRef(ctx) 117 root := mns.Root() 118 defer root.DecRef(ctx) 119 ctx = fs.WithRoot(ctx, root) 120 121 if err := createEtcPasswd(ctx, root, tc.passwdContents, tc.passwdMode); err != nil { 122 t.Fatalf("createEtcPasswd failed: %v", err) 123 } 124 125 got, err := getExecUserHome(ctx, mns, tc.uid) 126 if err != nil { 127 t.Fatalf("failed to get user home: %v", err) 128 } 129 130 if got != tc.expected { 131 t.Fatalf("expected %v, got: %v", tc.expected, got) 132 } 133 }) 134 } 135 } 136 137 // TestFindHomeInPasswd tests the findHomeInPasswd function's passwd file parsing. 138 func TestFindHomeInPasswd(t *testing.T) { 139 tests := map[string]struct { 140 uid uint32 141 passwd string 142 expected string 143 def string 144 }{ 145 "empty": { 146 uid: 1000, 147 passwd: "", 148 expected: "/", 149 def: "/", 150 }, 151 "whitespace": { 152 uid: 1000, 153 passwd: " ", 154 expected: "/", 155 def: "/", 156 }, 157 "full": { 158 uid: 1000, 159 passwd: "adin::1000:1111::/home/adin:/bin/sh", 160 expected: "/home/adin", 161 def: "/", 162 }, 163 // For better or worse, this is how runc works. 164 "partial": { 165 uid: 1000, 166 passwd: "adin::1000:1111:", 167 expected: "", 168 def: "/", 169 }, 170 "multiple": { 171 uid: 1001, 172 passwd: "adin::1000:1111::/home/adin:/bin/sh\nian::1001:1111::/home/ian:/bin/sh", 173 expected: "/home/ian", 174 def: "/", 175 }, 176 "duplicate": { 177 uid: 1000, 178 passwd: "adin::1000:1111::/home/adin:/bin/sh\nian::1000:1111::/home/ian:/bin/sh", 179 expected: "/home/adin", 180 def: "/", 181 }, 182 "empty_lines": { 183 uid: 1001, 184 passwd: "adin::1000:1111::/home/adin:/bin/sh\n\n\nian::1001:1111::/home/ian:/bin/sh", 185 expected: "/home/ian", 186 def: "/", 187 }, 188 } 189 190 for name, tc := range tests { 191 t.Run(name, func(t *testing.T) { 192 got, err := findHomeInPasswd(tc.uid, strings.NewReader(tc.passwd), tc.def) 193 if err != nil { 194 t.Fatalf("error parsing passwd: %v", err) 195 } 196 if tc.expected != got { 197 t.Fatalf("expected %v, got: %v", tc.expected, got) 198 } 199 }) 200 } 201 }