Files
rkt/pkg/tar/chroot.go
Simone Gotti 2c66843cc1 aci: extract tar in chroot
this introduces tar extraction inside a chroot. This is done spawning a
new process. Instead of creating a new executable (difficult to find,
package etc...) it adds the concept of a multicall process (like busybox).

Now a new multicall command "extracttar" is added.
tar.ExtractTar executes this new command passing a pipe to send the
filesMap and the tar contents to stdin.

The incomplete "insecure link" checks are removed from the the tar
functions.  Additionally these functions are now not exported and the
extraction directory is always the root directory.
2015-05-26 23:40:36 +02:00

114 lines
3.1 KiB
Go

// Copyright 2015 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tar
import (
"archive/tar"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"syscall"
"github.com/coreos/rkt/pkg/multicall"
"github.com/coreos/rkt/pkg/sys"
)
const (
multicallName = "extracttar"
fileMapFdNum = 3
)
var mcEntrypoint multicall.Entrypoint
func init() {
mcEntrypoint = multicall.Add(multicallName, extractTarCommand)
}
func extractTarCommand() error {
if len(os.Args) != 3 {
return fmt.Errorf("incorrect number of arguments. Usage: %s DIR {true|false}", multicallName)
}
if !sys.HasChrootCapability() {
return fmt.Errorf("chroot capability not available.")
}
dir := os.Args[1]
if !filepath.IsAbs(dir) {
return fmt.Errorf("dir %s must be an absolute path", dir)
}
overwrite, err := strconv.ParseBool(os.Args[2])
if err != nil {
return fmt.Errorf("error parsing overwrite argument: %v", err)
}
if err := syscall.Chroot(dir); err != nil {
return fmt.Errorf("failed to chroot in %s: %v", dir, err)
}
if err := syscall.Chdir("/"); err != nil {
return fmt.Errorf("failed to chdir: %v", err)
}
fileMapFile := os.NewFile(uintptr(fileMapFdNum), "fileMap")
fileMap := map[string]struct{}{}
if err := json.NewDecoder(fileMapFile).Decode(&fileMap); err != nil {
return fmt.Errorf("error decoding fileMap: %v", err)
}
if err := extractTar(tar.NewReader(os.Stdin), overwrite, fileMap); err != nil {
return fmt.Errorf("error extracting tar: %v", err)
}
// flush remaining bytes
io.Copy(ioutil.Discard, os.Stdin)
return nil
}
// ExtractTar extracts a tarball (from a io.Reader) into the given directory
// if pwl is not nil, only the paths in the map are extracted.
// If overwrite is true, existing files will be overwritten.
// The extraction is executed by fork/exec()ing a new process. The new process
// needs the CAP_SYS_CHROOT capability.
func ExtractTar(rs io.Reader, dir string, overwrite bool, pwl PathWhitelistMap) error {
r, w, err := os.Pipe()
if err != nil {
return err
}
defer w.Close()
enc := json.NewEncoder(w)
cmd := mcEntrypoint.Cmd(dir, strconv.FormatBool(overwrite))
cmd.ExtraFiles = []*os.File{r}
cmd.Stdin = rs
encodeCh := make(chan error)
go func() {
encodeCh <- enc.Encode(pwl)
}()
out, err := cmd.CombinedOutput()
// read from blocking encodeCh to release the goroutine
encodeErr := <-encodeCh
if err != nil {
return fmt.Errorf("extracttar error: %v, output: %s", err, out)
}
if encodeErr != nil {
return fmt.Errorf("extracttar failed to json encode filemap: %v", encodeErr)
}
return nil
}