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