github.com/wozhu6104/docker@v20.10.10+incompatible/pkg/chrootarchive/archive.go (about) 1 package chrootarchive // import "github.com/docker/docker/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/docker/docker/pkg/archive" 13 "github.com/docker/docker/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 // If dest is inside a root then directory is created within chroot by extractor. 78 // This case is only currently used by cp. 79 if dest == root { 80 idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) 81 rootIDs := idMapping.RootPair() 82 83 dest = filepath.Clean(dest) 84 if _, err := os.Stat(dest); os.IsNotExist(err) { 85 if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { 86 return err 87 } 88 } 89 } 90 91 r := ioutil.NopCloser(tarArchive) 92 if decompress { 93 decompressedArchive, err := archive.DecompressStream(tarArchive) 94 if err != nil { 95 return err 96 } 97 defer decompressedArchive.Close() 98 r = decompressedArchive 99 } 100 101 return invokeUnpack(r, dest, options, root) 102 } 103 104 // Tar tars the requested path while chrooted to the specified root. 105 func Tar(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { 106 if options == nil { 107 options = &archive.TarOptions{} 108 } 109 return invokePack(srcPath, options, root) 110 }