github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/chrootarchive/archive.go (about) 1 package chrootarchive // import "github.com/demonoid81/moby/pkg/chrootarchive" 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net" 8 "os" 9 "os/user" 10 "path/filepath" 11 12 "github.com/demonoid81/moby/pkg/archive" 13 "github.com/demonoid81/moby/pkg/idtools" 14 ) 15 16 func init() { 17 // initialize nss libraries in Glibc so that the dynamic libraries are loaded in the host 18 // environment not in the chroot from untrusted files. 19 _, _ = user.Lookup("docker") 20 _, _ = net.LookupHost("localhost") 21 } 22 23 // NewArchiver returns a new Archiver which uses chrootarchive.Untar 24 func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver { 25 if idMapping == nil { 26 idMapping = &idtools.IdentityMapping{} 27 } 28 return &archive.Archiver{ 29 Untar: Untar, 30 IDMapping: idMapping, 31 } 32 } 33 34 // Untar reads a stream of bytes from `archive`, parses it as a tar archive, 35 // and unpacks it into the directory at `dest`. 36 // The archive may be compressed with one of the following algorithms: 37 // identity (uncompressed), gzip, bzip2, xz. 38 func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error { 39 return untarHandler(tarArchive, dest, options, true, dest) 40 } 41 42 // UntarWithRoot is the same as `Untar`, but allows you to pass in a root directory 43 // The root directory is the directory that will be chrooted to. 44 // `dest` must be a path within `root`, if it is not an error will be returned. 45 // 46 // `root` should set to a directory which is not controlled by any potentially 47 // malicious process. 48 // 49 // This should be used to prevent a potential attacker from manipulating `dest` 50 // such that it would provide access to files outside of `dest` through things 51 // like symlinks. Normally `ResolveSymlinksInScope` would handle this, however 52 // sanitizing symlinks in this manner is inherrently racey: 53 // ref: CVE-2018-15664 54 func UntarWithRoot(tarArchive io.Reader, dest string, options *archive.TarOptions, root string) error { 55 return untarHandler(tarArchive, dest, options, true, root) 56 } 57 58 // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive, 59 // and unpacks it into the directory at `dest`. 60 // The archive must be an uncompressed stream. 61 func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error { 62 return untarHandler(tarArchive, dest, options, false, dest) 63 } 64 65 // Handler for teasing out the automatic decompression 66 func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool, root string) error { 67 if tarArchive == nil { 68 return fmt.Errorf("Empty archive") 69 } 70 if options == nil { 71 options = &archive.TarOptions{} 72 } 73 if options.ExcludePatterns == nil { 74 options.ExcludePatterns = []string{} 75 } 76 77 idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) 78 rootIDs := idMapping.RootPair() 79 80 dest = filepath.Clean(dest) 81 if _, err := os.Stat(dest); os.IsNotExist(err) { 82 if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { 83 return err 84 } 85 } 86 87 r := ioutil.NopCloser(tarArchive) 88 if decompress { 89 decompressedArchive, err := archive.DecompressStream(tarArchive) 90 if err != nil { 91 return err 92 } 93 defer decompressedArchive.Close() 94 r = decompressedArchive 95 } 96 97 return invokeUnpack(r, dest, options, root) 98 } 99 100 // Tar tars the requested path while chrooted to the specified root. 101 func Tar(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { 102 if options == nil { 103 options = &archive.TarOptions{} 104 } 105 return invokePack(srcPath, options, root) 106 }