github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/user/path.go (about) 1 // Copyright 2020 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 "path" 20 "strings" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 23 "github.com/nicocha30/gvisor-ligolo/pkg/context" 24 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 25 "github.com/nicocha30/gvisor-ligolo/pkg/fspath" 26 "github.com/nicocha30/gvisor-ligolo/pkg/log" 27 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 28 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 29 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 30 ) 31 32 // ExecutableResolveError represents a failure to resolve the executable 33 // in ResolveExecutablePath. 34 type ExecutableResolveError struct{ error } 35 36 // ResolveExecutablePath resolves the given executable name given the working 37 // dir and environment. 38 // Returns *ExecutableResolveError when the executable cannot be resolved. 39 func ResolveExecutablePath(ctx context.Context, args *kernel.CreateProcessArgs) (string, error) { 40 name := args.Filename 41 if len(name) == 0 { 42 if len(args.Argv) == 0 { 43 return "", fmt.Errorf("no filename or command provided") 44 } 45 name = args.Argv[0] 46 } 47 48 // Absolute paths can be used directly. 49 if path.IsAbs(name) { 50 return name, nil 51 } 52 53 // Paths with '/' in them should be joined to the working directory, or 54 // to the root if working directory is not set. 55 if strings.IndexByte(name, '/') > 0 { 56 wd := args.WorkingDirectory 57 if wd == "" { 58 wd = "/" 59 } 60 if !path.IsAbs(wd) { 61 return "", fmt.Errorf("working directory %q must be absolute", wd) 62 } 63 return path.Join(wd, name), nil 64 } 65 66 // Otherwise, We must lookup the name in the paths. 67 paths := getPath(args.Envv) 68 f, err := resolve(ctx, args.Credentials, args.MountNamespace, paths, name) 69 if err != nil { 70 return "", &ExecutableResolveError{fmt.Errorf("error finding executable %q in PATH %v: %v", name, paths, err)} 71 } 72 return f, nil 73 } 74 75 func resolve(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, paths []string, name string) (string, error) { 76 root := mns.Root() 77 root.IncRef() 78 defer root.DecRef(ctx) 79 for _, p := range paths { 80 if !path.IsAbs(p) { 81 // Relative paths aren't safe, no one should be using them. 82 log.Warningf("Skipping relative path %q in $PATH", p) 83 continue 84 } 85 86 binPath := path.Join(p, name) 87 pop := &vfs.PathOperation{ 88 Root: root, 89 Start: root, 90 Path: fspath.Parse(binPath), 91 FollowFinalSymlink: true, 92 } 93 opts := &vfs.OpenOptions{ 94 FileExec: true, 95 Flags: linux.O_RDONLY, 96 } 97 dentry, err := root.Mount().Filesystem().VirtualFilesystem().OpenAt(ctx, creds, pop, opts) 98 if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.EACCES, err) { 99 // Didn't find it here. 100 continue 101 } 102 if err != nil { 103 return "", err 104 } 105 dentry.DecRef(ctx) 106 107 return binPath, nil 108 } 109 110 // Couldn't find it. 111 return "", linuxerr.ENOENT 112 } 113 114 // getPath returns the PATH as a slice of strings given the environment 115 // variables. 116 func getPath(env []string) []string { 117 const prefix = "PATH=" 118 for _, e := range env { 119 if strings.HasPrefix(e, prefix) { 120 return strings.Split(strings.TrimPrefix(e, prefix), ":") 121 } 122 } 123 return nil 124 }