2019-01-02 10:23:25 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2019-02-11 11:17:32 +00:00
|
|
|
package tlsopts
|
2019-01-02 10:23:25 +00:00
|
|
|
|
|
|
|
import (
|
2019-02-11 11:17:32 +00:00
|
|
|
"crypto/tls"
|
2019-03-25 21:52:12 +00:00
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
2019-01-02 10:23:25 +00:00
|
|
|
"io/ioutil"
|
|
|
|
|
2019-02-11 11:17:32 +00:00
|
|
|
"github.com/zeebo/errs"
|
2019-08-20 15:15:43 +01:00
|
|
|
"gopkg.in/spacemonkeygo/monkit.v2"
|
2019-01-02 10:23:25 +00:00
|
|
|
|
|
|
|
"storj.io/storj/pkg/identity"
|
|
|
|
"storj.io/storj/pkg/peertls"
|
2019-03-25 21:52:12 +00:00
|
|
|
"storj.io/storj/pkg/peertls/extensions"
|
2019-02-07 18:40:28 +00:00
|
|
|
"storj.io/storj/pkg/pkcrypto"
|
2019-01-02 10:23:25 +00:00
|
|
|
)
|
|
|
|
|
2019-02-11 11:17:32 +00:00
|
|
|
var (
|
|
|
|
mon = monkit.Package()
|
|
|
|
// Error is error for tlsopts
|
|
|
|
Error = errs.Class("tlsopts error")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Options holds config, identity, and peer verification function data for use with tls.
|
2019-01-02 10:23:25 +00:00
|
|
|
type Options struct {
|
2019-03-04 20:40:18 +00:00
|
|
|
Config Config
|
|
|
|
Ident *identity.FullIdentity
|
2019-08-19 23:10:38 +01:00
|
|
|
RevDB extensions.RevocationDB
|
2019-03-25 21:52:12 +00:00
|
|
|
PeerCAWhitelist []*x509.Certificate
|
2019-03-06 14:42:34 +00:00
|
|
|
VerificationFuncs *VerificationFuncs
|
2019-03-04 20:40:18 +00:00
|
|
|
Cert *tls.Certificate
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-06 14:42:34 +00:00
|
|
|
// VerificationFuncs keeps track of of client and server peer certificate verification
|
|
|
|
// functions for use in tls handshakes.
|
|
|
|
type VerificationFuncs struct {
|
|
|
|
client []peertls.PeerCertVerificationFunc
|
|
|
|
server []peertls.PeerCertVerificationFunc
|
|
|
|
}
|
|
|
|
|
2019-03-25 21:52:12 +00:00
|
|
|
// ExtensionMap maps `pkix.Extension`s to their respective asn1 object ID string.
|
|
|
|
type ExtensionMap map[string]pkix.Extension
|
|
|
|
|
2019-08-19 23:10:38 +01:00
|
|
|
// NewOptions is a constructor for `tls options` given an identity, config, and
|
|
|
|
// revocation DB. A caller may pass a nil revocation DB if the revocation
|
|
|
|
// extension is disabled.
|
|
|
|
func NewOptions(i *identity.FullIdentity, c Config, revDB extensions.RevocationDB) (*Options, error) {
|
2019-01-02 10:23:25 +00:00
|
|
|
opts := &Options{
|
2019-03-06 14:42:34 +00:00
|
|
|
Config: c,
|
2019-08-19 23:10:38 +01:00
|
|
|
RevDB: revDB,
|
2019-03-06 14:42:34 +00:00
|
|
|
Ident: i,
|
|
|
|
VerificationFuncs: new(VerificationFuncs),
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:52:12 +00:00
|
|
|
err := opts.configure()
|
2019-01-02 10:23:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return opts, nil
|
|
|
|
}
|
|
|
|
|
2019-03-25 21:52:12 +00:00
|
|
|
// NewExtensionsMap builds an `ExtensionsMap` from the extensions in the passed certificate(s).
|
|
|
|
func NewExtensionsMap(chain ...*x509.Certificate) ExtensionMap {
|
|
|
|
extensionMap := make(ExtensionMap)
|
|
|
|
for _, cert := range chain {
|
2019-04-03 16:03:53 +01:00
|
|
|
for _, ext := range cert.Extensions {
|
2019-03-25 21:52:12 +00:00
|
|
|
extensionMap[ext.Id.String()] = ext
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return extensionMap
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtensionOptions converts options for use in extension handling.
|
|
|
|
func (opts *Options) ExtensionOptions() *extensions.Options {
|
|
|
|
return &extensions.Options{
|
|
|
|
PeerCAWhitelist: opts.PeerCAWhitelist,
|
|
|
|
RevDB: opts.RevDB,
|
2019-04-08 19:15:19 +01:00
|
|
|
PeerIDVersions: opts.Config.PeerIDVersions,
|
2019-03-25 21:52:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 19:15:19 +01:00
|
|
|
// configure adds peer certificate verification functions and data structures
|
|
|
|
// required for completing TLS handshakes to the options.
|
2019-03-25 21:52:12 +00:00
|
|
|
func (opts *Options) configure() (err error) {
|
|
|
|
if opts.Config.UsePeerCAWhitelist {
|
2019-01-17 17:36:45 +00:00
|
|
|
whitelist := []byte(DefaultPeerCAWhitelist)
|
2019-03-25 21:52:12 +00:00
|
|
|
if opts.Config.PeerCAWhitelistPath != "" {
|
|
|
|
whitelist, err = ioutil.ReadFile(opts.Config.PeerCAWhitelistPath)
|
2019-01-17 17:36:45 +00:00
|
|
|
if err != nil {
|
2019-03-25 21:52:12 +00:00
|
|
|
return Error.New("unable to find whitelist file %v: %v", opts.Config.PeerCAWhitelistPath, err)
|
2019-01-17 17:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-25 21:52:12 +00:00
|
|
|
opts.PeerCAWhitelist, err = pkcrypto.CertsFromPEM(whitelist)
|
2019-01-02 10:23:25 +00:00
|
|
|
if err != nil {
|
2019-01-17 17:36:45 +00:00
|
|
|
return Error.Wrap(err)
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|
2019-03-25 21:52:12 +00:00
|
|
|
opts.VerificationFuncs.ClientAdd(peertls.VerifyCAWhitelist(opts.PeerCAWhitelist))
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 15:15:43 +01:00
|
|
|
handlers := make(extensions.HandlerFactories, len(extensions.DefaultHandlers))
|
|
|
|
copy(handlers, extensions.DefaultHandlers)
|
|
|
|
|
|
|
|
if opts.Config.Extensions.Revocation {
|
|
|
|
handlers.Register(
|
|
|
|
extensions.RevocationCheckHandler,
|
|
|
|
extensions.RevocationUpdateHandler,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Config.Extensions.WhitelistSignedLeaf {
|
|
|
|
handlers.Register(extensions.CAWhitelistSignedLeafHandler)
|
|
|
|
}
|
|
|
|
|
|
|
|
opts.handleExtensions(handlers)
|
2019-03-06 14:42:34 +00:00
|
|
|
|
|
|
|
opts.Cert, err = peertls.TLSCert(opts.Ident.RawChain(), opts.Ident.Leaf, opts.Ident.Key)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-25 21:52:12 +00:00
|
|
|
// handleExtensions combines and wraps all extension handler functions into a peer
|
|
|
|
// certificate verification function. This allows extension handling via the
|
|
|
|
// `VerifyPeerCertificate` field in a `tls.Config` during a TLS handshake.
|
|
|
|
func (opts *Options) handleExtensions(handlers extensions.HandlerFactories) {
|
|
|
|
if len(handlers) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
handlerFuncMap := handlers.WithOptions(opts.ExtensionOptions())
|
|
|
|
|
|
|
|
combinedHandlerFunc := func(_ [][]byte, parsedChains [][]*x509.Certificate) error {
|
|
|
|
extensionMap := NewExtensionsMap(parsedChains[0]...)
|
|
|
|
return extensionMap.HandleExtensions(handlerFuncMap, parsedChains)
|
|
|
|
}
|
|
|
|
|
|
|
|
opts.VerificationFuncs.Add(combinedHandlerFunc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleExtensions calls each `extensions.HandlerFunc` with its respective extension
|
|
|
|
// and the certificate chain where its object ID string matches the extension's.
|
|
|
|
func (extensionMap ExtensionMap) HandleExtensions(handlerFuncMap extensions.HandlerFuncMap, chain [][]*x509.Certificate) error {
|
|
|
|
for idStr, extension := range extensionMap {
|
|
|
|
for id, handlerFunc := range handlerFuncMap {
|
|
|
|
if idStr == id.String() {
|
|
|
|
err := handlerFunc(extension, chain)
|
|
|
|
if err != nil {
|
|
|
|
return Error.Wrap(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-06 14:42:34 +00:00
|
|
|
// Client returns the client verification functions.
|
|
|
|
func (vf *VerificationFuncs) Client() []peertls.PeerCertVerificationFunc {
|
|
|
|
return vf.client
|
|
|
|
}
|
|
|
|
|
|
|
|
// Server returns the server verification functions.
|
|
|
|
func (vf *VerificationFuncs) Server() []peertls.PeerCertVerificationFunc {
|
|
|
|
return vf.server
|
|
|
|
}
|
2019-01-02 10:23:25 +00:00
|
|
|
|
2019-03-06 14:42:34 +00:00
|
|
|
// Add adds verification functions so the client and server lists.
|
|
|
|
func (vf *VerificationFuncs) Add(verificationFuncs ...peertls.PeerCertVerificationFunc) {
|
|
|
|
vf.ClientAdd(verificationFuncs...)
|
|
|
|
vf.ServerAdd(verificationFuncs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientAdd adds verification functions so the client list.
|
|
|
|
func (vf *VerificationFuncs) ClientAdd(verificationFuncs ...peertls.PeerCertVerificationFunc) {
|
|
|
|
verificationFuncs = removeNils(verificationFuncs)
|
|
|
|
vf.client = append(vf.client, verificationFuncs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServerAdd adds verification functions so the server list.
|
|
|
|
func (vf *VerificationFuncs) ServerAdd(verificationFuncs ...peertls.PeerCertVerificationFunc) {
|
|
|
|
verificationFuncs = removeNils(verificationFuncs)
|
|
|
|
vf.server = append(vf.server, verificationFuncs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeNils(verificationFuncs []peertls.PeerCertVerificationFunc) []peertls.PeerCertVerificationFunc {
|
2019-04-10 14:07:51 +01:00
|
|
|
result := verificationFuncs[:0]
|
|
|
|
for _, f := range verificationFuncs {
|
|
|
|
if f != nil {
|
|
|
|
result = append(result, f)
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 14:07:51 +01:00
|
|
|
return result
|
2019-01-02 10:23:25 +00:00
|
|
|
}
|