123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package scan
- import (
- "bytes"
- "crypto/x509"
- "fmt"
- "time"
- "github.com/cloudflare/cfssl/helpers"
- "github.com/cloudflare/cfssl/revoke"
- "github.com/cloudflare/cfssl/scan/crypto/tls"
- )
- // 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
- }
|