7e290869e7
Includes69ecbb4d6d
(forward-port of8b5121be2f
), which fixes CVE-2020-7919: - Panic in crypto/x509 certificate parsing and golang.org/x/crypto/cryptobyte On 32-bit architectures, a malformed input to crypto/x509 or the ASN.1 parsing functions of golang.org/x/crypto/cryptobyte can lead to a panic. The malformed certificate can be delivered via a crypto/tls connection to a client, or to a server that accepts client certificates. net/http clients can be made to crash by an HTTPS server, while net/http servers that accept client certificates will recover the panic and are unaffected. Thanks to Project Wycheproof for providing the test cases that led to the discovery of this issue. The issue is CVE-2020-7919 and Go issue golang.org/issue/36837. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package autocert
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// ErrCacheMiss is returned when a certificate is not found in cache.
|
|
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
|
|
|
// Cache is used by Manager to store and retrieve previously obtained certificates
|
|
// and other account data as opaque blobs.
|
|
//
|
|
// Cache implementations should not rely on the key naming pattern. Keys can
|
|
// include any printable ASCII characters, except the following: \/:*?"<>|
|
|
type Cache interface {
|
|
// Get returns a certificate data for the specified key.
|
|
// If there's no such key, Get returns ErrCacheMiss.
|
|
Get(ctx context.Context, key string) ([]byte, error)
|
|
|
|
// Put stores the data in the cache under the specified key.
|
|
// Underlying implementations may use any data storage format,
|
|
// as long as the reverse operation, Get, results in the original data.
|
|
Put(ctx context.Context, key string, data []byte) error
|
|
|
|
// Delete removes a certificate data from the cache under the specified key.
|
|
// If there's no such key in the cache, Delete returns nil.
|
|
Delete(ctx context.Context, key string) error
|
|
}
|
|
|
|
// DirCache implements Cache using a directory on the local filesystem.
|
|
// If the directory does not exist, it will be created with 0700 permissions.
|
|
type DirCache string
|
|
|
|
// Get reads a certificate data from the specified file name.
|
|
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
|
|
name = filepath.Join(string(d), name)
|
|
var (
|
|
data []byte
|
|
err error
|
|
done = make(chan struct{})
|
|
)
|
|
go func() {
|
|
data, err = ioutil.ReadFile(name)
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case <-done:
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return nil, ErrCacheMiss
|
|
}
|
|
return data, err
|
|
}
|
|
|
|
// Put writes the certificate data to the specified file name.
|
|
// The file will be created with 0600 permissions.
|
|
func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
|
if err := os.MkdirAll(string(d), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
var err error
|
|
go func() {
|
|
defer close(done)
|
|
var tmp string
|
|
if tmp, err = d.writeTempFile(name, data); err != nil {
|
|
return
|
|
}
|
|
defer os.Remove(tmp)
|
|
select {
|
|
case <-ctx.Done():
|
|
// Don't overwrite the file if the context was canceled.
|
|
default:
|
|
newName := filepath.Join(string(d), name)
|
|
err = os.Rename(tmp, newName)
|
|
}
|
|
}()
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-done:
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Delete removes the specified file name.
|
|
func (d DirCache) Delete(ctx context.Context, name string) error {
|
|
name = filepath.Join(string(d), name)
|
|
var (
|
|
err error
|
|
done = make(chan struct{})
|
|
)
|
|
go func() {
|
|
err = os.Remove(name)
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-done:
|
|
}
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
|
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
|
|
// TempFile uses 0600 permissions
|
|
f, err := ioutil.TempFile(string(d), prefix)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer func() {
|
|
if reterr != nil {
|
|
os.Remove(f.Name())
|
|
}
|
|
}()
|
|
if _, err := f.Write(b); err != nil {
|
|
f.Close()
|
|
return "", err
|
|
}
|
|
return f.Name(), f.Close()
|
|
}
|