lib/upgrade: Enforce limits on download archives (fixes #3045)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3046
This commit is contained in:
parent
d6a7ffe0d4
commit
38166e976f
@ -25,6 +25,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/dialer"
|
"github.com/syncthing/syncthing/lib/dialer"
|
||||||
"github.com/syncthing/syncthing/lib/signature"
|
"github.com/syncthing/syncthing/lib/signature"
|
||||||
@ -32,12 +33,38 @@ import (
|
|||||||
|
|
||||||
const DisabledByCompilation = false
|
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
|
// This is an HTTP/HTTPS client that does *not* perform certificate
|
||||||
// validation. We do this because some systems where Syncthing runs have
|
// 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
|
// 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
|
// load the upgrade insecurely as we verify an ECDSA signature of the actual
|
||||||
// binary contents before accepting the upgrade.
|
// binary contents before accepting the upgrade.
|
||||||
var insecureHTTP = &http.Client{
|
var insecureHTTP = &http.Client{
|
||||||
|
Timeout: readTimeout,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Dial: dialer.Dial,
|
Dial: dialer.Dial,
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
@ -61,7 +88,7 @@ func FetchLatestReleases(releasesURL, version string) []Release {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rels []Release
|
var rels []Release
|
||||||
json.NewDecoder(resp.Body).Decode(&rels)
|
json.NewDecoder(io.LimitReader(resp.Body, maxMetadataSize)).Decode(&rels)
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
return rels
|
return rels
|
||||||
@ -164,9 +191,9 @@ func readRelease(archiveName, dir, url string) (string, error) {
|
|||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
return readZip(archiveName, dir, resp.Body)
|
return readZip(archiveName, dir, io.LimitReader(resp.Body, maxArchiveSize))
|
||||||
default:
|
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
|
var sig []byte
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
|
i := 0
|
||||||
for {
|
for {
|
||||||
|
if i >= maxArchiveMembers {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// end of tar archive
|
// end of tar archive
|
||||||
@ -224,7 +257,13 @@ func readZip(archiveName, dir string, r io.Reader) (string, error) {
|
|||||||
var sig []byte
|
var sig []byte
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
|
i := 0
|
||||||
for _, file := range archive.File {
|
for _, file := range archive.File {
|
||||||
|
if i >= maxArchiveMembers {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
|
||||||
inFile, err := file.Open()
|
inFile, err := file.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -264,14 +303,14 @@ func archiveFileVisitor(dir string, tempFile *string, signature *[]byte, archive
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
l.Debugf("found upgrade binary %s", archivePath)
|
l.Debugf("found upgrade binary %s", archivePath)
|
||||||
*tempFile, err = writeBinary(dir, filedata)
|
*tempFile, err = writeBinary(dir, io.LimitReader(filedata, maxBinarySize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case "release.sig":
|
case "release.sig":
|
||||||
l.Debugf("found signature %s", archivePath)
|
l.Debugf("found signature %s", archivePath)
|
||||||
*signature, err = ioutil.ReadAll(filedata)
|
*signature, err = ioutil.ReadAll(io.LimitReader(filedata, maxSignatureSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user