lib/upgrade: Enforce limits on download archives (fixes #3045)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3046
This commit is contained in:
Jakob Borg 2016-05-06 13:58:34 +00:00 committed by Audrius Butkevicius
parent d6a7ffe0d4
commit 38166e976f
1 changed files with 44 additions and 5 deletions

View File

@ -25,6 +25,7 @@ import (
"runtime"
"sort"
"strings"
"time"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/signature"
@ -32,12 +33,38 @@ import (
const DisabledByCompilation = false
const (
// Current binary size hovers around 10 MB. We give it some room to grow
// and say that we never expect the binary to be larger than 64 MB.
maxBinarySize = 64 << 20 // 64 MiB
// The max expected size of the signature file.
maxSignatureSize = 1 << 10 // 1 KiB
// We set the same limit on the archive. The binary will compress and we
// include some other stuff - currently the release archive size is
// around 6 MB.
maxArchiveSize = maxBinarySize
// When looking through the archive for the binary and signature, stop
// looking once we've searched this many files.
maxArchiveMembers = 100
// Archive reads, or metadata checks, that take longer than this will be
// rejected.
readTimeout = 30 * time.Minute
// The limit on the size of metadata that we accept.
maxMetadataSize = 100 << 10 // 100 KiB
)
// This is an HTTP/HTTPS client that does *not* perform certificate
// validation. We do this because some systems where Syncthing runs have
// issues with old or missing CA roots. It doesn't actually matter that we
// load the upgrade insecurely as we verify an ECDSA signature of the actual
// binary contents before accepting the upgrade.
var insecureHTTP = &http.Client{
Timeout: readTimeout,
Transport: &http.Transport{
Dial: dialer.Dial,
Proxy: http.ProxyFromEnvironment,
@ -61,7 +88,7 @@ func FetchLatestReleases(releasesURL, version string) []Release {
}
var rels []Release
json.NewDecoder(resp.Body).Decode(&rels)
json.NewDecoder(io.LimitReader(resp.Body, maxMetadataSize)).Decode(&rels)
resp.Body.Close()
return rels
@ -164,9 +191,9 @@ func readRelease(archiveName, dir, url string) (string, error) {
switch runtime.GOOS {
case "windows":
return readZip(archiveName, dir, resp.Body)
return readZip(archiveName, dir, io.LimitReader(resp.Body, maxArchiveSize))
default:
return readTarGz(archiveName, dir, resp.Body)
return readTarGz(archiveName, dir, io.LimitReader(resp.Body, maxArchiveSize))
}
}
@ -182,7 +209,13 @@ func readTarGz(archiveName, dir string, r io.Reader) (string, error) {
var sig []byte
// Iterate through the files in the archive.
i := 0
for {
if i >= maxArchiveMembers {
break
}
i++
hdr, err := tr.Next()
if err == io.EOF {
// end of tar archive
@ -224,7 +257,13 @@ func readZip(archiveName, dir string, r io.Reader) (string, error) {
var sig []byte
// Iterate through the files in the archive.
i := 0
for _, file := range archive.File {
if i >= maxArchiveMembers {
break
}
i++
inFile, err := file.Open()
if err != nil {
return "", err
@ -264,14 +303,14 @@ func archiveFileVisitor(dir string, tempFile *string, signature *[]byte, archive
return nil
}
l.Debugf("found upgrade binary %s", archivePath)
*tempFile, err = writeBinary(dir, filedata)
*tempFile, err = writeBinary(dir, io.LimitReader(filedata, maxBinarySize))
if err != nil {
return err
}
case "release.sig":
l.Debugf("found signature %s", archivePath)
*signature, err = ioutil.ReadAll(filedata)
*signature, err = ioutil.ReadAll(io.LimitReader(filedata, maxSignatureSize))
if err != nil {
return err
}