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