github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/pkg/tar/chroot.go (about) 1 // Copyright 2015 The rkt 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 tar 16 17 import ( 18 "archive/tar" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "strconv" 27 "syscall" 28 29 "github.com/hashicorp/errwrap" 30 "github.com/rkt/rkt/pkg/multicall" 31 "github.com/rkt/rkt/pkg/sys" 32 "github.com/rkt/rkt/pkg/user" 33 ) 34 35 const ( 36 multicallName = "extracttar" 37 fileMapFdNum = 3 38 ) 39 40 var mcEntrypoint multicall.Entrypoint 41 42 func init() { 43 mcEntrypoint = multicall.Add(multicallName, extractTarCommand) 44 } 45 46 // Because this function is executed by multicall in a different process, it is not possible to use errwrap to return errors 47 func extractTarCommand() error { 48 if len(os.Args) != 5 { 49 return fmt.Errorf("incorrect number of arguments. Usage: %s DIR {true|false} uidShift uidCount", multicallName) 50 } 51 if !sys.HasChrootCapability() { 52 return fmt.Errorf("chroot capability not available.") 53 } 54 dir := os.Args[1] 55 if !filepath.IsAbs(dir) { 56 return fmt.Errorf("dir %s must be an absolute path", dir) 57 } 58 overwrite, err := strconv.ParseBool(os.Args[2]) 59 if err != nil { 60 return fmt.Errorf("error parsing overwrite argument: %v", err) 61 } 62 63 us, err := strconv.ParseUint(os.Args[3], 10, 32) 64 if err != nil { 65 return fmt.Errorf("error parsing uidShift argument: %v", err) 66 } 67 uc, err := strconv.ParseUint(os.Args[4], 10, 32) 68 if err != nil { 69 return fmt.Errorf("error parsing uidCount argument: %v", err) 70 } 71 72 uidRange := &user.UidRange{Shift: uint32(us), Count: uint32(uc)} 73 74 if err := syscall.Chroot(dir); err != nil { 75 return fmt.Errorf("failed to chroot in %s: %v", dir, err) 76 } 77 if err := syscall.Chdir("/"); err != nil { 78 return fmt.Errorf("failed to chdir: %v", err) 79 } 80 fileMapFile := os.NewFile(uintptr(fileMapFdNum), "fileMap") 81 82 fileMap := map[string]struct{}{} 83 if err := json.NewDecoder(fileMapFile).Decode(&fileMap); err != nil { 84 return fmt.Errorf("error decoding fileMap: %v", err) 85 } 86 editor, err := NewUidShiftingFilePermEditor(uidRange) 87 if err != nil { 88 return fmt.Errorf("error determining current user: %v", err) 89 } 90 if err := ExtractTarInsecure(tar.NewReader(os.Stdin), "/", overwrite, fileMap, editor); err != nil { 91 return fmt.Errorf("error extracting tar: %v", err) 92 } 93 94 // flush remaining bytes 95 io.Copy(ioutil.Discard, os.Stdin) 96 97 return nil 98 } 99 100 // ExtractTar extracts a tarball (from a io.Reader) into the given directory 101 // if pwl is not nil, only the paths in the map are extracted. 102 // If overwrite is true, existing files will be overwritten. 103 // The extraction is executed by fork/exec()ing a new process. The new process 104 // needs the CAP_SYS_CHROOT capability. 105 func ExtractTar(rs io.Reader, dir string, overwrite bool, uidRange *user.UidRange, pwl PathWhitelistMap) error { 106 r, w, err := os.Pipe() 107 if err != nil { 108 return err 109 } 110 defer w.Close() 111 enc := json.NewEncoder(w) 112 cmd := mcEntrypoint.Cmd(dir, strconv.FormatBool(overwrite), 113 strconv.FormatUint(uint64(uidRange.Shift), 10), 114 strconv.FormatUint(uint64(uidRange.Count), 10)) 115 cmd.ExtraFiles = []*os.File{r} 116 117 cmd.Stdin = rs 118 encodeCh := make(chan error) 119 go func() { 120 encodeCh <- enc.Encode(pwl) 121 }() 122 123 out, err := cmd.CombinedOutput() 124 125 // read from blocking encodeCh to release the goroutine 126 encodeErr := <-encodeCh 127 if err != nil { 128 return fmt.Errorf("extracttar error: %v, output: %s", err, out) 129 } 130 if encodeErr != nil { 131 return errwrap.Wrap(errors.New("extracttar failed to json encode filemap"), encodeErr) 132 } 133 return nil 134 }