dialer.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. package hpkp
  2. import (
  3. "crypto/tls"
  4. "errors"
  5. "net"
  6. "strings"
  7. )
  8. // Storage is threadsafe hpkp storage interface
  9. type Storage interface {
  10. Lookup(host string) *Header
  11. Add(host string, d *Header)
  12. }
  13. // PinOnlyDialer returns a dialer that ignores root trusts in favor of known
  14. // certificate pins
  15. func PinOnlyDialer(s Storage) func(network, addr string) (net.Conn, error) {
  16. return newPinDialer(s, true, nil)
  17. }
  18. // TLSConfigDialer returns a dialer that uses pins in addition to the provided
  19. // tls.Config options
  20. func TLSConfigDialer(s Storage, conf *tls.Config) func(network, addr string) (net.Conn, error) {
  21. return newPinDialer(s, false, conf)
  22. }
  23. // newPinDialer returns a function suitable for use as DialTLS
  24. func newPinDialer(s Storage, pinOnly bool, defaultTLSConfig *tls.Config) func(network, addr string) (net.Conn, error) {
  25. return func(network, addr string) (net.Conn, error) {
  26. // might need to strip ":https" from addr as well
  27. h := s.Lookup(strings.TrimRight(addr, ":443"))
  28. if h != nil {
  29. c, err := tls.Dial(network, addr, &tls.Config{InsecureSkipVerify: pinOnly})
  30. if err != nil {
  31. return c, err
  32. }
  33. validPin := false
  34. // intermediates can be pinned as well, loop through leaf-> root looking
  35. // for pins
  36. for _, peercert := range c.ConnectionState().PeerCertificates {
  37. peerPin := Fingerprint(peercert)
  38. if h.Matches(peerPin) {
  39. validPin = true
  40. break
  41. }
  42. }
  43. if validPin == false {
  44. return nil, errors.New("pin was not valid")
  45. }
  46. return c, nil
  47. }
  48. // do a normal dial
  49. return tls.Dial(network, addr, defaultTLSConfig)
  50. }
  51. }