Vendor cfssl and cfssljson

This commit is contained in:
Christoph Blecker
2018-08-06 16:30:17 -07:00
parent 1c5b968152
commit 952fc9f6f8
245 changed files with 251725 additions and 4 deletions

36
vendor/github.com/cloudflare/cfssl/scan/BUILD generated vendored Normal file
View File

@@ -0,0 +1,36 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"broad.go",
"connectivity.go",
"pki.go",
"scan_common.go",
"tls_handshake.go",
"tls_session.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/scan",
importpath = "github.com/cloudflare/cfssl/scan",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/bundler:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/revoke:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

84
vendor/github.com/cloudflare/cfssl/scan/broad.go generated vendored Normal file
View File

@@ -0,0 +1,84 @@
package scan
import (
"crypto/tls"
"crypto/x509"
"net"
"sync"
"time"
"github.com/cloudflare/cfssl/bundler"
)
// Broad contains scanners for large swaths of TLS hosts on the internet.
var Broad = &Family{
Description: "Large scale scans of TLS hosts",
Scanners: map[string]*Scanner{
"IntermediateCAs": {
"Scans a CIDR IP range for unknown Intermediate CAs",
intermediateCAScan,
},
},
}
func incrementBytes(bytes []byte) {
lsb := len(bytes) - 1
bytes[lsb]++
if bytes[lsb] == 0 {
incrementBytes(bytes[:lsb])
}
}
var (
caBundleFile = "/etc/cfssl/ca-bundle.crt"
intBundleFile = "/etc/cfssl/int-bundle.crt"
numWorkers = 32
timeout = time.Second
)
// intermediateCAScan scans for new intermediate CAs not in the trust store.
func intermediateCAScan(addr, hostname string) (grade Grade, output Output, err error) {
cidr, port, _ := net.SplitHostPort(addr)
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return Skipped, nil, nil
}
b, err := bundler.NewBundler(caBundleFile, intBundleFile)
if err != nil {
return
}
var wg sync.WaitGroup
wg.Add(numWorkers)
dialer := &net.Dialer{Timeout: timeout}
config := &tls.Config{InsecureSkipVerify: true}
addrs := make(chan string)
chains := make(chan []*x509.Certificate, numWorkers)
go func() {
for chain := range chains {
b.Bundle(chain, nil, bundler.Force)
}
}()
for i := 0; i < numWorkers; i++ {
go func() {
for addr := range addrs {
conn, err := tls.DialWithDialer(dialer, Network, addr, config)
if err != nil {
continue
}
conn.Close()
if conn.ConnectionState().HandshakeComplete {
chains <- conn.ConnectionState().PeerCertificates
}
}
wg.Done()
}()
}
for ip := ipnet.IP.To16(); ipnet.Contains(ip); incrementBytes(ip) {
addrs <- net.JoinHostPort(ip.String(), port)
}
close(addrs)
wg.Wait()
close(chains)
grade = Good
return
}

161
vendor/github.com/cloudflare/cfssl/scan/connectivity.go generated vendored Normal file
View File

@@ -0,0 +1,161 @@
package scan
import (
"bufio"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
)
// Connectivity contains scanners testing basic connectivity to the host
var Connectivity = &Family{
Description: "Scans for basic connectivity with the host through DNS and TCP/TLS dials",
Scanners: map[string]*Scanner{
"DNSLookup": {
"Host can be resolved through DNS",
dnsLookupScan,
},
"CloudFlareStatus": {
"Host is on CloudFlare",
onCloudFlareScan,
},
"TCPDial": {
"Host accepts TCP connection",
tcpDialScan,
},
"TLSDial": {
"Host can perform TLS handshake",
tlsDialScan,
},
},
}
// dnsLookupScan tests that DNS resolution of the host returns at least one address
func dnsLookupScan(addr, hostname string) (grade Grade, output Output, err error) {
addrs, err := net.LookupHost(hostname)
if err != nil {
return
}
if len(addrs) == 0 {
err = errors.New("no addresses found for host")
}
grade, output = Good, addrs
return
}
var (
cfNets []*net.IPNet
cfNetsErr error
)
func initOnCloudFlareScan() ([]*net.IPNet, error) {
// Propogate previous errors and don't attempt to re-download.
if cfNetsErr != nil {
return nil, cfNetsErr
}
// Don't re-download ranges if we already have them.
if len(cfNets) > 0 {
return cfNets, nil
}
// Download CloudFlare CIDR ranges and parse them.
v4resp, err := Client.Get("https://www.cloudflare.com/ips-v4")
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't download CloudFlare IPs: %v", err)
return nil, cfNetsErr
}
defer v4resp.Body.Close()
v6resp, err := Client.Get("https://www.cloudflare.com/ips-v6")
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't download CloudFlare IPs: %v", err)
return nil, cfNetsErr
}
defer v6resp.Body.Close()
scanner := bufio.NewScanner(io.MultiReader(v4resp.Body, v6resp.Body))
for scanner.Scan() {
_, ipnet, err := net.ParseCIDR(scanner.Text())
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't parse CIDR range: %v", err)
return nil, cfNetsErr
}
cfNets = append(cfNets, ipnet)
}
if err := scanner.Err(); err != nil {
cfNetsErr = fmt.Errorf("Couldn't read IP bodies: %v", err)
return nil, cfNetsErr
}
return cfNets, nil
}
func onCloudFlareScan(addr, hostname string) (grade Grade, output Output, err error) {
var cloudflareNets []*net.IPNet
if cloudflareNets, err = initOnCloudFlareScan(); err != nil {
grade = Skipped
return
}
_, addrs, err := dnsLookupScan(addr, hostname)
if err != nil {
return
}
cfStatus := make(map[string]bool)
grade = Good
for _, addr := range addrs.([]string) {
ip := net.ParseIP(addr)
for _, cfNet := range cloudflareNets {
if cfNet.Contains(ip) {
cfStatus[addr] = true
break
}
}
if !cfStatus[addr] {
cfStatus[addr] = false
grade = Bad
}
}
output = cfStatus
return
}
// tcpDialScan tests that the host can be connected to through TCP.
func tcpDialScan(addr, hostname string) (grade Grade, output Output, err error) {
conn, err := Dialer.Dial(Network, addr)
if err != nil {
return
}
conn.Close()
grade = Good
return
}
// tlsDialScan tests that the host can perform a TLS Handshake
// and warns if the server's certificate can't be verified.
func tlsDialScan(addr, hostname string) (grade Grade, output Output, err error) {
var conn *tls.Conn
config := defaultTLSConfig(hostname)
if conn, err = tls.DialWithDialer(Dialer, Network, addr, config); err != nil {
return
}
conn.Close()
config.InsecureSkipVerify = false
if conn, err = tls.DialWithDialer(Dialer, Network, addr, config); err != nil {
grade = Warning
return
}
conn.Close()
grade = Good
return
}

161
vendor/github.com/cloudflare/cfssl/scan/pki.go generated vendored Normal file
View File

@@ -0,0 +1,161 @@
package scan
import (
"bytes"
"crypto/tls"
"crypto/x509"
"fmt"
"time"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/revoke"
)
// PKI contains scanners for the Public Key Infrastructure.
var PKI = &Family{
Description: "Scans for the Public Key Infrastructure",
Scanners: map[string]*Scanner{
"ChainExpiration": {
"Host's chain hasn't expired and won't expire in the next 30 days",
chainExpiration,
},
"ChainValidation": {
"All certificates in host's chain are valid",
chainValidation,
},
"MultipleCerts": {
"Host serves same certificate chain across all IPs",
multipleCerts,
},
},
}
// getChain is a helper function that retreives the host's certificate chain.
func getChain(addr string, config *tls.Config) (chain []*x509.Certificate, err error) {
var conn *tls.Conn
conn, err = tls.DialWithDialer(Dialer, Network, addr, config)
if err != nil {
return
}
err = conn.Close()
if err != nil {
return
}
chain = conn.ConnectionState().PeerCertificates
if len(chain) == 0 {
err = fmt.Errorf("%s returned empty certificate chain", addr)
}
return
}
type expiration time.Time
func (e expiration) String() string {
return time.Time(e).Format("Jan 2 15:04:05 2006 MST")
}
func chainExpiration(addr, hostname string) (grade Grade, output Output, err error) {
chain, err := getChain(addr, defaultTLSConfig(hostname))
if err != nil {
return
}
expirationTime := helpers.ExpiryTime(chain)
output = expirationTime
if time.Now().After(expirationTime) {
return
}
// Warn if cert will expire in the next 30 days
if time.Now().Add(time.Hour * 24 * 30).After(expirationTime) {
grade = Warning
return
}
grade = Good
return
}
func chainValidation(addr, hostname string) (grade Grade, output Output, err error) {
chain, err := getChain(addr, defaultTLSConfig(hostname))
if err != nil {
return
}
var warnings []string
for i := 0; i < len(chain)-1; i++ {
cert, parent := chain[i], chain[i+1]
valid := helpers.ValidExpiry(cert)
if !valid {
warnings = append(warnings, fmt.Sprintf("Certificate for %s is valid for too long", cert.Subject.CommonName))
}
revoked, ok := revoke.VerifyCertificate(cert)
if !ok {
warnings = append(warnings, fmt.Sprintf("couldn't check if %s is revoked", cert.Subject.CommonName))
}
if revoked {
err = fmt.Errorf("%s is revoked", cert.Subject.CommonName)
return
}
if !parent.IsCA {
err = fmt.Errorf("%s is not a CA", parent.Subject.CommonName)
return
}
if !bytes.Equal(cert.AuthorityKeyId, parent.SubjectKeyId) {
err = fmt.Errorf("%s AuthorityKeyId differs from %s SubjectKeyId", cert.Subject.CommonName, parent.Subject.CommonName)
return
}
if err = cert.CheckSignatureFrom(parent); err != nil {
return
}
switch cert.SignatureAlgorithm {
case x509.ECDSAWithSHA1:
warnings = append(warnings, fmt.Sprintf("%s is signed by ECDSAWithSHA1", cert.Subject.CommonName))
case x509.SHA1WithRSA:
warnings = append(warnings, fmt.Sprintf("%s is signed by RSAWithSHA1", cert.Subject.CommonName))
}
}
if len(warnings) == 0 {
grade = Good
} else {
grade = Warning
output = warnings
}
return
}
func multipleCerts(addr, hostname string) (grade Grade, output Output, err error) {
config := defaultTLSConfig(hostname)
firstChain, err := getChain(addr, config)
if err != nil {
return
}
grade, _, err = multiscan(addr, func(addrport string) (g Grade, o Output, e error) {
g = Good
chain, e1 := getChain(addrport, config)
if e1 != nil {
return
}
if !chain[0].Equal(firstChain[0]) {
e = fmt.Errorf("%s not equal to %s", chain[0].Subject.CommonName, firstChain[0].Subject.CommonName)
g = Bad
return
}
return
})
return
}

281
vendor/github.com/cloudflare/cfssl/scan/scan_common.go generated vendored Normal file
View File

@@ -0,0 +1,281 @@
package scan
import (
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"regexp"
"sync"
"time"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
)
var (
// Network is the default network to use.
Network = "tcp"
// Dialer is the default dialer to use, with a 1s timeout.
Dialer = &net.Dialer{Timeout: time.Second}
// Client is the default HTTP Client.
Client = &http.Client{Transport: &http.Transport{Dial: Dialer.Dial}}
// RootCAs defines the default root certificate authorities to be used for scan.
RootCAs *x509.CertPool
)
// Grade gives a subjective rating of the host's success in a scan.
type Grade int
const (
// Bad describes a host with serious misconfiguration or vulnerability.
Bad Grade = iota
// Warning describes a host with non-ideal configuration that maintains support for Warning clients.
Warning
// Good describes host performing the expected state-of-the-art.
Good
// Skipped descibes the "grade" of a scan that has been skipped.
Skipped
)
// String gives the name of the Grade as a string.
func (g Grade) String() string {
switch g {
case Bad:
return "Bad"
case Warning:
return "Warning"
case Good:
return "Good"
case Skipped:
return "Skipped"
default:
return "Invalid"
}
}
// Output is the result of a scan, to be stored for potential use by later Scanners.
type Output interface{}
// multiscan scans all DNS addresses returned for the host, returning the lowest grade
// and the concatenation of all the output.
func multiscan(host string, scan func(string) (Grade, Output, error)) (grade Grade, output Output, err error) {
domain, port, _ := net.SplitHostPort(host)
var addrs []string
addrs, err = net.LookupHost(domain)
if err != nil {
return
}
grade = Good
out := make(map[string]Output)
for _, addr := range addrs {
var g Grade
var o Output
g, o, err = scan(net.JoinHostPort(addr, port))
if err != nil {
grade = Bad
return
}
if g < grade {
grade = g
}
out[addr] = o
}
output = out
return
}
// Scanner describes a type of scan to perform on a host.
type Scanner struct {
// Description describes the nature of the scan to be performed.
Description string `json:"description"`
// scan is the function that scans the given host and provides a Grade and Output.
scan func(string, string) (Grade, Output, error)
}
// Scan performs the scan to be performed on the given host and stores its result.
func (s *Scanner) Scan(addr, hostname string) (Grade, Output, error) {
grade, output, err := s.scan(addr, hostname)
if err != nil {
log.Debugf("scan: %v", err)
return grade, output, err
}
return grade, output, err
}
// Family defines a set of related scans meant to be run together in sequence.
type Family struct {
// Description gives a short description of the scans performed scan/scan_common.goon the host.
Description string `json:"description"`
// Scanners is a list of scanners that are to be run in sequence.
Scanners map[string]*Scanner `json:"scanners"`
}
// FamilySet contains a set of Families to run Scans from.
type FamilySet map[string]*Family
// Default contains each scan Family that is defined
var Default = FamilySet{
"Connectivity": Connectivity,
"TLSHandshake": TLSHandshake,
"TLSSession": TLSSession,
"PKI": PKI,
"Broad": Broad,
}
// ScannerResult contains the result for a single scan.
type ScannerResult struct {
Grade string `json:"grade"`
Output Output `json:"output,omitempty"`
Error string `json:"error,omitempty"`
}
// FamilyResult contains a scan response for a single Family
type FamilyResult map[string]ScannerResult
// A Result contains a ScannerResult along with it's scanner and family names.
type Result struct {
Family, Scanner string
ScannerResult
}
type context struct {
sync.WaitGroup
addr, hostname string
familyRegexp, scannerRegexp *regexp.Regexp
resultChan chan *Result
}
func newContext(addr, hostname string, familyRegexp, scannerRegexp *regexp.Regexp, numFamilies int) *context {
ctx := &context{
addr: addr,
hostname: hostname,
familyRegexp: familyRegexp,
scannerRegexp: scannerRegexp,
resultChan: make(chan *Result),
}
ctx.Add(numFamilies)
go func() {
ctx.Wait()
close(ctx.resultChan)
}()
return ctx
}
type familyContext struct {
sync.WaitGroup
ctx *context
}
func (ctx *context) newfamilyContext(numScanners int) *familyContext {
familyCtx := &familyContext{ctx: ctx}
familyCtx.Add(numScanners)
go func() {
familyCtx.Wait()
familyCtx.ctx.Done()
}()
return familyCtx
}
func (ctx *context) copyResults(timeout time.Duration) map[string]FamilyResult {
results := make(map[string]FamilyResult)
for {
var result *Result
select {
case <-time.After(timeout):
log.Warningf("Scan timed out after %v", timeout)
return results
case result = <-ctx.resultChan:
if result == nil {
return results
}
}
if results[result.Family] == nil {
results[result.Family] = make(FamilyResult)
}
results[result.Family][result.Scanner] = result.ScannerResult
}
}
func (familyCtx *familyContext) runScanner(familyName, scannerName string, scanner *Scanner) {
if familyCtx.ctx.familyRegexp.MatchString(familyName) && familyCtx.ctx.scannerRegexp.MatchString(scannerName) {
grade, output, err := scanner.Scan(familyCtx.ctx.addr, familyCtx.ctx.hostname)
result := &Result{
familyName,
scannerName,
ScannerResult{
Grade: grade.String(),
Output: output,
},
}
if err != nil {
result.Error = err.Error()
}
familyCtx.ctx.resultChan <- result
}
familyCtx.Done()
}
// RunScans iterates over AllScans, running each scan that matches the family
// and scanner regular expressions concurrently.
func (fs FamilySet) RunScans(host, ip, family, scanner string, timeout time.Duration) (map[string]FamilyResult, error) {
hostname, port, err := net.SplitHostPort(host)
if err != nil {
hostname = host
port = "443"
}
var addr string
if net.ParseIP(ip) != nil {
addr = net.JoinHostPort(ip, port)
} else {
addr = net.JoinHostPort(hostname, port)
}
familyRegexp, err := regexp.Compile(family)
if err != nil {
return nil, err
}
scannerRegexp, err := regexp.Compile(scanner)
if err != nil {
return nil, err
}
ctx := newContext(addr, hostname, familyRegexp, scannerRegexp, len(fs))
for familyName, family := range fs {
familyCtx := ctx.newfamilyContext(len(family.Scanners))
for scannerName, scanner := range family.Scanners {
go familyCtx.runScanner(familyName, scannerName, scanner)
}
}
return ctx.copyResults(timeout), nil
}
// LoadRootCAs loads the default root certificate authorities from file.
func LoadRootCAs(caBundleFile string) (err error) {
if caBundleFile != "" {
log.Debugf("Loading scan RootCAs: %s", caBundleFile)
RootCAs, err = helpers.LoadPEMCertPool(caBundleFile)
}
return
}
func defaultTLSConfig(hostname string) *tls.Config {
return &tls.Config{
ServerName: hostname,
RootCAs: RootCAs,
InsecureSkipVerify: true,
}
}

View File

@@ -0,0 +1,427 @@
package scan
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"net"
"strings"
"github.com/cloudflare/cfssl/helpers"
)
// Sentinel for failures in sayHello. Should always be caught.
var errHelloFailed = errors.New("Handshake failed in sayHello")
// TLSHandshake contains scanners testing host cipher suite negotiation
var TLSHandshake = &Family{
Description: "Scans for host's SSL/TLS version and cipher suite negotiation",
Scanners: map[string]*Scanner{
"CipherSuite": {
"Determines host's cipher suites accepted and prefered order",
cipherSuiteScan,
},
"SigAlgs": {
"Determines host's accepted signature and hash algorithms",
sigAlgsScan,
},
"CertsBySigAlgs": {
"Determines host's certificate signature algorithm matching client's accepted signature and hash algorithms",
certSigAlgsScan,
},
"CertsByCiphers": {
"Determines host's certificate signature algorithm matching client's accepted ciphers",
certSigAlgsScanByCipher,
},
"ECCurves": {
"Determines the host's ec curve support for TLS 1.2",
ecCurveScan,
},
},
}
func getCipherIndex(ciphers []uint16, serverCipher uint16) (cipherIndex int, err error) {
//func getCipherIndex(ciphers []uint16, serverCipher uint16) (cipherIndex int, err error) {
// fmt.Println(serverCipher, ciphers)
var cipherID uint16
for cipherIndex, cipherID = range ciphers {
if serverCipher == cipherID {
return
}
}
err = fmt.Errorf("server negotiated ciphersuite we didn't send: %s", tls.CipherSuites[serverCipher])
return
}
func getCurveIndex(curves []tls.CurveID, serverCurve tls.CurveID) (curveIndex int, err error) {
var curveID tls.CurveID
for curveIndex, curveID = range curves {
if serverCurve == curveID {
return
}
}
err = fmt.Errorf("server negotiated elliptic curve we didn't send: %s", tls.Curves[serverCurve])
return
}
func sayHello(addr, hostname string, ciphers []uint16, curves []tls.CurveID, vers uint16, sigAlgs []tls.SignatureAndHash) (cipherIndex, curveIndex int, certs [][]byte, err error) {
tcpConn, err := net.Dial(Network, addr)
if err != nil {
return
}
config := defaultTLSConfig(hostname)
config.MinVersion = vers
config.MaxVersion = vers
if ciphers == nil {
ciphers = allCiphersIDs()
}
config.CipherSuites = ciphers
if curves == nil {
curves = allCurvesIDs()
}
config.CurvePreferences = curves
if sigAlgs == nil {
sigAlgs = tls.AllSignatureAndHashAlgorithms
}
conn := tls.Client(tcpConn, config)
serverCipher, serverCurveType, serverCurve, serverVersion, certificates, err := conn.SayHello(sigAlgs)
certs = certificates
conn.Close()
if err != nil {
err = errHelloFailed
return
}
if serverVersion != vers {
err = fmt.Errorf("server negotiated protocol version we didn't send: %s", tls.Versions[serverVersion])
return
}
cipherIndex, err = getCipherIndex(ciphers, serverCipher)
if tls.CipherSuites[serverCipher].EllipticCurve {
if curves == nil {
curves = allCurvesIDs()
}
if serverCurveType != 3 {
err = fmt.Errorf("server negotiated non-named ECDH parameters; we didn't analyze them. Server curve type: %d", serverCurveType)
return
}
curveIndex, err = getCurveIndex(curves, serverCurve)
}
return
}
func allCiphersIDs() []uint16 {
ciphers := make([]uint16, 0, len(tls.CipherSuites))
for cipherID := range tls.CipherSuites {
ciphers = append(ciphers, cipherID)
}
return ciphers
}
func allECDHECiphersIDs() []uint16 {
var ecdheCiphers = map[uint16]tls.CipherSuite{
0XC006: {Name: "TLS_ECDHE_ECDSA_WITH_NULL_SHA", ForwardSecret: true, EllipticCurve: true},
0XC007: {Name: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", ShortName: "ECDHE-ECDSA-RC4-SHA", ForwardSecret: true, EllipticCurve: true},
0XC008: {Name: "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", ShortName: "ECDHE-ECDSA-DES-CBC3-SHA", ForwardSecret: true, EllipticCurve: true},
0XC009: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", ShortName: "ECDHE-ECDSA-AES128-SHA", ForwardSecret: true, EllipticCurve: true},
0XC00A: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", ShortName: "ECDHE-ECDSA-AES256-SHA", ForwardSecret: true, EllipticCurve: true},
0XC010: {Name: "TLS_ECDHE_RSA_WITH_NULL_SHA", ForwardSecret: true, EllipticCurve: true},
0XC011: {Name: "TLS_ECDHE_RSA_WITH_RC4_128_SHA", ShortName: "ECDHE-RSA-RC4-SHA", ForwardSecret: true, EllipticCurve: true},
0XC012: {Name: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", ShortName: "ECDHE-RSA-DES-CBC3-SHA", ForwardSecret: true, EllipticCurve: true},
0XC013: {Name: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", ShortName: "ECDHE-RSA-AES128-SHA", ForwardSecret: true, EllipticCurve: true},
0XC014: {Name: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", ShortName: "ECDHE-RSA-AES256-SHA", ForwardSecret: true, EllipticCurve: true},
0XC023: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", ShortName: "ECDHE-ECDSA-AES128-SHA256", ForwardSecret: true, EllipticCurve: true},
0XC024: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", ShortName: "ECDHE-ECDSA-AES256-SHA384", ForwardSecret: true, EllipticCurve: true},
0XC027: {Name: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", ShortName: "ECDHE-RSA-AES128-SHA256", ForwardSecret: true, EllipticCurve: true},
0XC028: {Name: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", ShortName: "ECDHE-RSA-AES256-SHA384", ForwardSecret: true, EllipticCurve: true},
0XC02B: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ShortName: "ECDHE-ECDSA-AES128-GCM-SHA256", ForwardSecret: true, EllipticCurve: true},
0XC02C: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", ShortName: "ECDHE-ECDSA-AES256-GCM-SHA384", ForwardSecret: true, EllipticCurve: true},
0XC02F: {Name: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", ShortName: "ECDHE-RSA-AES128-GCM-SHA256", ForwardSecret: true, EllipticCurve: true},
0XC030: {Name: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", ShortName: "ECDHE-RSA-AES256-GCM-SHA384", ForwardSecret: true, EllipticCurve: true},
0XC048: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC049: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC04C: {Name: "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC04D: {Name: "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC05D: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC060: {Name: "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC061: {Name: "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC072: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC073: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC076: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC077: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC086: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC087: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC08A: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
0XC08B: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
0XC08C: {Name: "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", EllipticCurve: true},
0XC0AC: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM", ForwardSecret: true, EllipticCurve: true},
0XC0AD: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM", ForwardSecret: true, EllipticCurve: true},
0XC0AE: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", ForwardSecret: true, EllipticCurve: true},
0XC0AF: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", ForwardSecret: true, EllipticCurve: true},
// Non-IANA standardized cipher suites:
// ChaCha20, Poly1305 cipher suites are defined in
// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
0XCC13: {Name: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", ForwardSecret: true, EllipticCurve: true},
0XCC14: {Name: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", ForwardSecret: true, EllipticCurve: true},
}
ciphers := make([]uint16, 0, len(ecdheCiphers))
for cipherID := range ecdheCiphers {
ciphers = append(ciphers, cipherID)
}
return ciphers
}
func allCurvesIDs() []tls.CurveID {
curves := make([]tls.CurveID, 0, len(tls.Curves))
for curveID := range tls.Curves {
// No unassigned or explicit curves in the scan, per http://tools.ietf.org/html/rfc4492#section-5.4
if curveID == 0 || curveID == 65281 || curveID == 65282 {
continue
} else {
curves = append(curves, curveID)
}
}
return curves
}
type cipherDatum struct {
versionID uint16
curves []tls.CurveID
}
// cipherVersions contains lists of host's supported cipher suites based on SSL/TLS Version.
// If a cipher suite uses ECC, also contains a list of supported curves by SSL/TLS Version.
type cipherVersions struct {
cipherID uint16
data []cipherDatum
}
type cipherVersionList []cipherVersions
func (cvList cipherVersionList) String() string {
cvStrings := make([]string, len(cvList))
for i, c := range cvList {
versStrings := make([]string, len(c.data))
for j, d := range c.data {
curveStrings := make([]string, len(d.curves))
for k, c := range d.curves {
curveStrings[k] = tls.Curves[c]
}
versStrings[j] = fmt.Sprintf("%s: [ %s ]", tls.Versions[d.versionID], strings.Join(curveStrings, ","))
}
cvStrings[i] = fmt.Sprintf("%s\t%s", tls.CipherSuites[c.cipherID], strings.Join(versStrings, ","))
}
return strings.Join(cvStrings, "\n")
}
func (cvList cipherVersionList) MarshalJSON() ([]byte, error) {
b := new(bytes.Buffer)
cvStrs := make([]string, len(cvList))
for i, cv := range cvList {
versStrings := make([]string, len(cv.data))
for j, d := range cv.data {
curveStrings := make([]string, len(d.curves))
if len(d.curves) > 0 {
for k, c := range d.curves {
curveStrings[k] = fmt.Sprintf("\"%s\"", tls.Curves[c])
}
versStrings[j] = fmt.Sprintf("{\"%s\":[%s]}", tls.Versions[d.versionID], strings.Join(curveStrings, ","))
} else {
versStrings[j] = fmt.Sprintf("\"%s\"", tls.Versions[d.versionID])
}
}
cvStrs[i] = fmt.Sprintf("{\"%s\":[%s]}", tls.CipherSuites[cv.cipherID].String(), strings.Join(versStrings, ","))
}
fmt.Fprintf(b, "[%s]", strings.Join(cvStrs, ","))
return b.Bytes(), nil
}
func doCurveScan(addr, hostname string, vers, cipherID uint16, ciphers []uint16) (supportedCurves []tls.CurveID, err error) {
allCurves := allCurvesIDs()
curves := make([]tls.CurveID, len(allCurves))
copy(curves, allCurves)
for len(curves) > 0 {
var curveIndex int
_, curveIndex, _, err = sayHello(addr, hostname, []uint16{cipherID}, curves, vers, nil)
if err != nil {
// This case is expected, because eventually we ask only for curves the server doesn't support
if err == errHelloFailed {
err = nil
break
}
return
}
curveID := curves[curveIndex]
supportedCurves = append(supportedCurves, curveID)
curves = append(curves[:curveIndex], curves[curveIndex+1:]...)
}
return
}
// cipherSuiteScan returns, by TLS Version, the sort list of cipher suites
// supported by the host
func cipherSuiteScan(addr, hostname string) (grade Grade, output Output, err error) {
var cvList cipherVersionList
allCiphers := allCiphersIDs()
var vers uint16
for vers = tls.VersionTLS12; vers >= tls.VersionSSL30; vers-- {
ciphers := make([]uint16, len(allCiphers))
copy(ciphers, allCiphers)
for len(ciphers) > 0 {
var cipherIndex int
cipherIndex, _, _, err = sayHello(addr, hostname, ciphers, nil, vers, nil)
if err != nil {
if err == errHelloFailed {
err = nil
break
}
return
}
if vers == tls.VersionSSL30 {
grade = Warning
}
cipherID := ciphers[cipherIndex]
// If this is an EC cipher suite, do a second scan for curve support
var supportedCurves []tls.CurveID
if tls.CipherSuites[cipherID].EllipticCurve {
supportedCurves, err = doCurveScan(addr, hostname, vers, cipherID, ciphers)
if len(supportedCurves) == 0 {
err = errors.New("couldn't negotiate any curves")
}
}
for i, c := range cvList {
if cipherID == c.cipherID {
cvList[i].data = append(c.data, cipherDatum{vers, supportedCurves})
goto exists
}
}
cvList = append(cvList, cipherVersions{cipherID, []cipherDatum{{vers, supportedCurves}}})
exists:
ciphers = append(ciphers[:cipherIndex], ciphers[cipherIndex+1:]...)
}
}
if len(cvList) == 0 {
err = errors.New("couldn't negotiate any cipher suites")
return
}
if grade != Warning {
grade = Good
}
output = cvList
return
}
// sigAlgsScan returns the accepted signature and hash algorithms of the host
func sigAlgsScan(addr, hostname string) (grade Grade, output Output, err error) {
var supportedSigAlgs []tls.SignatureAndHash
for _, sigAlg := range tls.AllSignatureAndHashAlgorithms {
_, _, _, e := sayHello(addr, hostname, nil, nil, tls.VersionTLS12, []tls.SignatureAndHash{sigAlg})
if e == nil {
supportedSigAlgs = append(supportedSigAlgs, sigAlg)
}
}
if len(supportedSigAlgs) > 0 {
grade = Good
output = supportedSigAlgs
} else {
err = errors.New("no SigAlgs supported")
}
return
}
// certSigAlgScan returns the server certificate with various sigature and hash algorithms in the ClientHello
func certSigAlgsScan(addr, hostname string) (grade Grade, output Output, err error) {
var certSigAlgs = make(map[string]string)
for _, sigAlg := range tls.AllSignatureAndHashAlgorithms {
_, _, derCerts, e := sayHello(addr, hostname, nil, nil, tls.VersionTLS12, []tls.SignatureAndHash{sigAlg})
if e == nil {
if len(derCerts) == 0 {
return Bad, nil, errors.New("no certs returned")
}
certs, _, err := helpers.ParseCertificatesDER(derCerts[0], "")
if err != nil {
return Bad, nil, err
}
certSigAlgs[sigAlg.String()] = helpers.SignatureString(certs[0].SignatureAlgorithm)
//certSigAlgs = append(certSigAlgs, certs[0].SignatureAlgorithm)
}
}
if len(certSigAlgs) > 0 {
grade = Good
output = certSigAlgs
} else {
err = errors.New("no SigAlgs supported")
}
return
}
// certSigAlgScan returns the server certificate with various ciphers in the ClientHello
func certSigAlgsScanByCipher(addr, hostname string) (grade Grade, output Output, err error) {
var certSigAlgs = make(map[string]string)
for cipherID := range tls.CipherSuites {
_, _, derCerts, e := sayHello(addr, hostname, []uint16{cipherID}, nil, tls.VersionTLS12, []tls.SignatureAndHash{})
if e == nil {
if len(derCerts) == 0 {
return Bad, nil, errors.New("no certs returned")
}
certs, _, err := helpers.ParseCertificatesDER(derCerts[0], "")
if err != nil {
return Bad, nil, err
}
certSigAlgs[tls.CipherSuites[cipherID].Name] = helpers.SignatureString(certs[0].SignatureAlgorithm)
//certSigAlgs = append(certSigAlgs, certs[0].SignatureAlgorithm)
}
}
if len(certSigAlgs) > 0 {
grade = Good
output = certSigAlgs
} else {
err = errors.New("no cipher supported")
}
return
}
// ecCurveScan returns the elliptic curves supported by the host.
func ecCurveScan(addr, hostname string) (grade Grade, output Output, err error) {
allCurves := allCurvesIDs()
curves := make([]tls.CurveID, len(allCurves))
copy(curves, allCurves)
var supportedCurves []string
for len(curves) > 0 {
var curveIndex int
_, curveIndex, _, err = sayHello(addr, hostname, allECDHECiphersIDs(), curves, tls.VersionTLS12, nil)
if err != nil {
// This case is expected, because eventually we ask only for curves the server doesn't support
if err == errHelloFailed {
err = nil
break
}
return
}
curveID := curves[curveIndex]
supportedCurves = append(supportedCurves, tls.Curves[curveID])
curves = append(curves[:curveIndex], curves[curveIndex+1:]...)
}
output = supportedCurves
grade = Good
return
}

42
vendor/github.com/cloudflare/cfssl/scan/tls_session.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
package scan
import "crypto/tls"
// TLSSession contains tests of host TLS Session Resumption via
// Session Tickets and Session IDs
var TLSSession = &Family{
Description: "Scans host's implementation of TLS session resumption using session tickets/session IDs",
Scanners: map[string]*Scanner{
"SessionResume": {
"Host is able to resume sessions across all addresses",
sessionResumeScan,
},
},
}
// SessionResumeScan tests that host is able to resume sessions across all addresses.
func sessionResumeScan(addr, hostname string) (grade Grade, output Output, err error) {
config := defaultTLSConfig(hostname)
config.ClientSessionCache = tls.NewLRUClientSessionCache(1)
conn, err := tls.DialWithDialer(Dialer, Network, addr, config)
if err != nil {
return
}
if err = conn.Close(); err != nil {
return
}
return multiscan(addr, func(addrport string) (g Grade, o Output, e error) {
var conn *tls.Conn
if conn, e = tls.DialWithDialer(Dialer, Network, addrport, config); e != nil {
return
}
conn.Close()
if o = conn.ConnectionState().DidResume; o.(bool) {
g = Good
}
return
})
}