github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/engine/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 if idMapping == nil { 25 idMapping = &idtools.IdentityMapping{} 26 } 27 return &archive.Archiver{ 28 Untar: Untar, 29 IDMapping: idMapping, 30 } 31 } 32 33 // Untar reads a stream of bytes from `archive`, parses it as a tar archive, 34 // and unpacks it into the directory at `dest`. 35 // The archive may be compressed with one of the following algorithms: 36 // identity (uncompressed), gzip, bzip2, xz. 37 func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error { 38 return untarHandler(tarArchive, dest, options, true, dest) 39 } 40 41 // UntarWithRoot is the same as `Untar`, but allows you to pass in a root directory 42 // The root directory is the directory that will be chrooted to. 43 // `dest` must be a path within `root`, if it is not an error will be returned. 44 // 45 // `root` should set to a directory which is not controlled by any potentially 46 // malicious process. 47 // 48 // This should be used to prevent a potential attacker from manipulating `dest` 49 // such that it would provide access to files outside of `dest` through things 50 // like symlinks. Normally `ResolveSymlinksInScope` would handle this, however 51 // sanitizing symlinks in this manner is inherrently racey: 52 // ref: CVE-2018-15664 53 func UntarWithRoot(tarArchive io.Reader, dest string, options *archive.TarOptions, root string) error { 54 return untarHandler(tarArchive, dest, options, true, root) 55 } 56 57 // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive, 58 // and unpacks it into the directory at `dest`. 59 // The archive must be an uncompressed stream. 60 func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error { 61 return untarHandler(tarArchive, dest, options, false, dest) 62 } 63 64 // Handler for teasing out the automatic decompression 65 func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool, root string) error { 66 if tarArchive == nil { 67 return fmt.Errorf("Empty archive") 68 } 69 if options == nil { 70 options = &archive.TarOptions{} 71 } 72 if options.ExcludePatterns == nil { 73 options.ExcludePatterns = []string{} 74 } 75 76 // If dest is inside a root then directory is created within chroot by extractor. 77 // This case is only currently used by cp. 78 if dest == root { 79 idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) 80 rootIDs := idMapping.RootPair() 81 82 dest = filepath.Clean(dest) 83 if _, err := os.Stat(dest); os.IsNotExist(err) { 84 if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { 85 return err 86 } 87 } 88 } 89 90 r := io.NopCloser(tarArchive) 91 if decompress { 92 decompressedArchive, err := archive.DecompressStream(tarArchive) 93 if err != nil { 94 return err 95 } 96 defer decompressedArchive.Close() 97 r = decompressedArchive 98 } 99 100 return invokeUnpack(r, dest, options, root) 101 } 102 103 // Tar tars the requested path while chrooted to the specified root. 104 func Tar(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { 105 if options == nil { 106 options = &archive.TarOptions{} 107 } 108 return invokePack(srcPath, options, root) 109 }