github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/core/chroot/chroot.go (about) 1 // Copyright 2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build !windows !plan9 6 7 package main 8 9 import ( 10 "bytes" 11 "flag" 12 "fmt" 13 "log" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "strconv" 18 "strings" 19 "syscall" 20 ) 21 22 type userSpec struct { 23 uid uint32 24 gid uint32 25 } 26 27 func (u *userSpec) Set(s string) error { 28 var err error 29 userspecSplit := strings.Split(s, ":") 30 if len(userspecSplit) != 2 || userspecSplit[1] == "" { 31 return fmt.Errorf("expected user spec flag to be \":\" separated values received %s", s) 32 } 33 34 u.uid, err = stringToUint32(userspecSplit[0]) 35 if err != nil { 36 return err 37 } 38 39 u.gid, err = stringToUint32(userspecSplit[1]) 40 if err != nil { 41 return err 42 } 43 44 return nil 45 } 46 47 func (u *userSpec) Get() interface{} { 48 return *u 49 } 50 51 func (u *userSpec) String() string { 52 return fmt.Sprintf("%d:%d", u.uid, u.gid) 53 } 54 55 func defaultUser() userSpec { 56 return userSpec{ 57 uid: uint32(os.Getuid()), 58 gid: uint32(os.Getgid()), 59 } 60 } 61 62 type groupsSpec struct { 63 groups []uint32 64 } 65 66 func (g *groupsSpec) Set(s string) error { 67 groupStrs := strings.Split(s, ",") 68 g.groups = make([]uint32, len(groupStrs)) 69 70 for index, group := range groupStrs { 71 72 gid, err := stringToUint32(group) 73 if err != nil { 74 return err 75 } 76 77 g.groups[index] = gid 78 } 79 80 return nil 81 } 82 83 func (g *groupsSpec) Get() interface{} { 84 return *g 85 } 86 87 func (g *groupsSpec) String() string { 88 var buffer bytes.Buffer 89 90 for index, gid := range g.groups { 91 buffer.WriteString(fmt.Sprint(gid)) 92 if index < len(g.groups)-1 { 93 buffer.WriteString(",") 94 } 95 } 96 97 return buffer.String() 98 } 99 100 var ( 101 skipchdirFlag bool 102 user = defaultUser() 103 groups = groupsSpec{} 104 ) 105 106 func init() { 107 flag.Var(&user, "u", "specify user and group (ID only) as USER:GROUP") 108 flag.Var(&groups, "g", "specify supplementary group ids as g1,g2,..,gN") 109 flag.BoolVar(&skipchdirFlag, "s", false, fmt.Sprint("Use this option to not change", 110 "the working directory to / after changing the root directory to newroot, i.e., ", 111 "inside the chroot. This option is only permitted when newroot is the old / directory.")) 112 } 113 114 func stringToUint32(str string) (uint32, error) { 115 ret, err := strconv.ParseUint(str, 10, 32) 116 if err != nil { 117 return 0, err 118 } 119 return uint32(ret), nil 120 } 121 122 func parseCommand(args []string) []string { 123 if len(args) > 1 { 124 return args[1:] 125 } 126 return []string{"/bin/sh", "-i"} 127 } 128 129 func parseRoot(args []string) (root string, err error) { 130 if len(args) < 1 { 131 return "", fmt.Errorf("missing operand") 132 } 133 134 return filepath.Abs(args[0]) 135 } 136 137 func isRoot(dir string) (bool, error) { 138 realPath, err := filepath.EvalSymlinks(dir) 139 if err != nil { 140 return false, err 141 } 142 absolutePath, err := filepath.Abs(realPath) 143 if err != nil { 144 return false, err 145 } 146 if absolutePath == "/" { 147 return true, nil 148 } 149 return false, nil 150 } 151 152 func main() { 153 var ( 154 newRoot string 155 isOldroot bool 156 err error 157 ) 158 159 flag.Parse() 160 161 if flag.NFlag() == 0 && flag.NArg() == 0 { 162 flag.PrintDefaults() 163 os.Exit(1) 164 } 165 166 newRoot, err = parseRoot(flag.Args()) 167 if err != nil { 168 log.Fatal(err) 169 } 170 isOldroot, err = isRoot(newRoot) 171 if err != nil { 172 log.Fatal(err) 173 } 174 175 if !skipchdirFlag { 176 err = os.Chdir(newRoot) 177 if err != nil { 178 log.Fatal(err) 179 } 180 } else if !isOldroot { 181 log.Fatal("The -s option is only permitted when newroot is the old / directory") 182 } 183 184 argv := parseCommand(flag.Args()) 185 186 cmd := exec.Command(argv[0], argv[1:]...) 187 188 cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr 189 cmd.SysProcAttr = &syscall.SysProcAttr{ 190 Credential: &syscall.Credential{ 191 Uid: user.uid, 192 Gid: user.gid, 193 Groups: groups.groups, 194 }, 195 Chroot: newRoot, 196 } 197 198 if err = cmd.Run(); err != nil { 199 log.Fatal(err) 200 } 201 }