dialer.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package hpkp
  2. import (
  3. "crypto/tls"
  4. "errors"
  5. "net"
  6. "strconv"
  7. "strings"
  8. )
  9. // Storage is threadsafe hpkp storage interface
  10. type Storage interface {
  11. Lookup(host string) *Header
  12. Add(host string, d *Header)
  13. }
  14. // StorageReader is threadsafe hpkp storage interface
  15. type StorageReader interface {
  16. Lookup(host string) *Header
  17. }
  18. // PinFailureReporter callback function to keep track and report on
  19. // PIN failures
  20. type PinFailureReporter func(p *PinFailure, reportUri string)
  21. // DialerConfig describes how to verify hpkp info and report failures
  22. type DialerConfig struct {
  23. Storage StorageReader
  24. PinOnly bool
  25. TLSConfig *tls.Config
  26. Reporter PinFailureReporter
  27. Dial func(string, string) (net.Conn, error)
  28. }
  29. // NewDialer returns a dialer for making TLS connections with hpkp support
  30. func (c *DialerConfig) NewDialer() func(network, addr string) (net.Conn, error) {
  31. reporter := c.Reporter
  32. if reporter == nil {
  33. reporter = emptyReporter
  34. }
  35. return newPinDialer(c.Storage, reporter, c.PinOnly, c.TLSConfig, c.Dial)
  36. }
  37. // emptyReporter does nothing with a pin failure message
  38. var emptyReporter = func(p *PinFailure, reportUri string) {
  39. return
  40. }
  41. // newPinDialer returns a function suitable for use as DialTLS
  42. func newPinDialer(s StorageReader, r PinFailureReporter, pinOnly bool, defaultTLSConfig *tls.Config, dial func(string, string) (net.Conn, error)) func(network, addr string) (net.Conn, error) {
  43. return func(network, addr string) (net.Conn, error) {
  44. dialTLS := func(network, addr string, cfg *tls.Config) (*tls.Conn, error) {
  45. if dial == nil {
  46. return tls.Dial(network, addr, cfg)
  47. }
  48. if cfg == nil {
  49. cfg = &tls.Config{}
  50. } else {
  51. cfg = cloneTLSConfig(cfg)
  52. }
  53. if cfg.ServerName == "" {
  54. colonPos := strings.LastIndex(addr, ":")
  55. if colonPos == -1 {
  56. colonPos = len(addr)
  57. }
  58. cfg.ServerName = addr[:colonPos]
  59. }
  60. netConn, err := dial(network, addr)
  61. if err != nil {
  62. return nil, err
  63. }
  64. c := tls.Client(netConn, cfg)
  65. if err = c.Handshake(); err != nil {
  66. c.Close()
  67. return nil, err
  68. }
  69. return c, nil
  70. }
  71. host, portStr, err := net.SplitHostPort(addr)
  72. if err != nil {
  73. return nil, err
  74. }
  75. port, err := strconv.Atoi(portStr)
  76. if err != nil {
  77. return nil, err
  78. }
  79. if h := s.Lookup(host); h != nil {
  80. tlsCfg := &tls.Config{InsecureSkipVerify: pinOnly}
  81. var c *tls.Conn
  82. // initial dial
  83. c, err := dialTLS(network, addr, tlsCfg)
  84. if err != nil {
  85. return nil, err
  86. }
  87. // intermediates can be pinned as well, loop through leaf-> root looking
  88. // for pin matches
  89. validPin := false
  90. for _, peercert := range c.ConnectionState().PeerCertificates {
  91. peerPin := Fingerprint(peercert)
  92. if h.Matches(peerPin) {
  93. validPin = true
  94. break
  95. }
  96. }
  97. // was a valid pin found?
  98. if !validPin {
  99. // notify failure callback
  100. r(NewPinFailure(host, port, h, c.ConnectionState()))
  101. c.Close()
  102. return nil, errors.New("pin was not valid")
  103. }
  104. return c, nil
  105. }
  106. // do a normal dial, address isn't in hpkp cache
  107. return dialTLS(network, addr, defaultTLSConfig)
  108. }
  109. }