From 1bee345f5b0bd49c0b432d6d919dbb91c812a3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Tue, 26 Jan 2021 10:25:07 +0100 Subject: [PATCH] Use hostname check from verify.go to handle patterns in TLS certs (#23661) (#23675) Previously, DNSNames in x509 certs with wildcards were not accepted. The function from Golang's `verify.go` is taken, so the check remains the same between Golang versions. (cherry picked from commit 629141994afe07f0b26ba299eef8031bf204edbe) --- .../transport/tlscommon/testdata/ca.crt | 47 +++++++++----- .../transport/tlscommon/testdata/ca.key | 51 +++++++++++++++ .../transport/tlscommon/testdata/server.crt | 22 +++++++ .../transport/tlscommon/testdata/server.key | 15 +++++ .../common/transport/tlscommon/tls_config.go | 2 +- .../transport/tlscommon/tls_config_test.go | 15 ++++- .../transport/tlscommon/validhostname.go | 64 ++++++++++++++++++- 7 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 libbeat/common/transport/tlscommon/testdata/ca.key create mode 100644 libbeat/common/transport/tlscommon/testdata/server.crt create mode 100644 libbeat/common/transport/tlscommon/testdata/server.key diff --git a/libbeat/common/transport/tlscommon/testdata/ca.crt b/libbeat/common/transport/tlscommon/testdata/ca.crt index da2bce043f7..f08fd34367e 100644 --- a/libbeat/common/transport/tlscommon/testdata/ca.crt +++ b/libbeat/common/transport/tlscommon/testdata/ca.crt @@ -1,19 +1,32 @@ -----BEGIN CERTIFICATE----- -MIIC/zCCAeegAwIBAgIJAIVZ8xw3LMNkMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV -BAMMC21vcmVsbG8ub3ZoMB4XDTE5MDgwOTA5MzQwMFoXDTI5MDgwNjA5MzQwMFow -FjEUMBIGA1UEAwwLbW9yZWxsby5vdmgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCoM2HYyuTTlu41SlgVO0Hdx7eUQevGSKO6pjPjN49/KKY1z/3DoKzr -seWaGOjiWUAqx/GHX8AsR9ToVoKGBbSNeDxT33pt3I9aCnnOPTt3yDIOlr4ZWnKq -NnNHwfydsMBfBAYgdU/L506KuNHJQ18Zey5+A0roTWyHUT48mQBsjetXg77RfDMB -MYVOWETfl70GKAaAlVGZfJHCkfBzYnPcEjqtcuU/7d27WZrSMhXifzHAEmm0KPER -EWdo4UHTK23wLY6dvkp2O5i0bKHv+PuLpqYrm7R7SWGhhwD651n5S5W20FHDow+d -js0yW2gqYsZZN6S1uAsJ8rdYAEPhK9J9AgMBAAGjUDBOMB0GA1UdDgQWBBQ6Lsen -0HbE+7M6iV9r8n5rZrbl4jAfBgNVHSMEGDAWgBQ6Lsen0HbE+7M6iV9r8n5rZrbl -4jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAgrLJnK4s/OVnh8CRk -GmikP+ZxhDs4k1nlr7+rTYkU0huoHK8p802w4zd74szYsHpo8kON/zSmFD7JpU4L -o2kseENqMsgrCPhF3+TDwf/Li43pbK162iAq8ZEpYnSXbQsRyP+Tz0lzoEoli6o7 -6KVn4VNookLMyhGIAOmhfbNm0jG+B2zz+bvoTAe9CiDfvq1k0fnuKFzRtRsj09NJ -FNMhSc02N4EDrGpL5CYmEXjPZS3lUsoYPwbYlmUt3Bzuf5hI0mDHCt3BYKH1vFI4 -W8/h9wwGn/yytsH21dkj41KEQK6N65gT9i0fBBiubuS2H1SVMMJ/J7PUqol278Ar -zGpS +MIIFhzCCA2+gAwIBAgIUL0vc8AdVKIcjap/RSpH21trR70swDQYJKoZIhvcNAQEL +BQAwUzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xFzAVBgNVBAMMDmNhQGV4YW1wbGUuY29tMB4XDTIxMDEy +NTE2MzQ0OFoXDTMxMDEyMzE2MzQ0OFowUzELMAkGA1UEBhMCVVMxEzARBgNVBAgM +CkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFzAVBgNVBAMMDmNh +QGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx0rP +p+sMWe3RehThE5Mh1s8uKsujG0q+Q62s4G4mBE5tQnmSS0LoezWuGMKNyjWQR4dt +IvicPZQfEhqOvdYAIA5fsQE8CMoXW50Q43kQlBUbvZH0yldUFtFtRLPD4RRtwB26 +sUhWLUUCdk4mZBUmAuhMbIoov+TZ8/EZBdqjRBqM9p+k/C9xfitqXKmBWvWOmc0i +NUpxMjJ0C18vVcoAneiMQbB4iBNFviSLxrhnH9sno6IKG/WSCmOaPirmGzMr/PYQ +Wa4j69xQfGd4VBwolShI+fkoCmMQMk06XENUXo9V75sgbV0U0PAjBv4Kqye/r6s2 +1wJKNnS8Ib4rBJAeh5PqebVmpgJUc8lAeC/4SE3Edw6yGILwuGnfZjZJeRgX+OMd +u5K29gvx4Kf0ZZ5F34vzsDwa8CGTTvdth8aNDhO4ETThxUtjqXSA91ewf93Tf3X5 +Rzbg1K5hSHFVcd53Hec6/5Aqiw5PBARa2Ekj1ZW9PHHrSf/x+axyOyK+akUOoI8X +FlgImdr21pKZPSFNpvrYURRYDz8/ftFlcbsx32D3/uQZJW6FpvyguFWnVrGFm7He +ptWvYP2wM0XSOsHQXhogv09sgZhxgViHbc7/PZXOpTFlQt1MXygXVuf0eBUTiJI4 +a595gF4F6Kx/ppBjWge+ZUUsnFjhHVhHvhzvncUCAwEAAaNTMFEwHQYDVR0OBBYE +FHg4mXfbBjMpE8mJUh/yPrfuD2yBMB8GA1UdIwQYMBaAFHg4mXfbBjMpE8mJUh/y +PrfuD2yBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAA+yu1mF +QoMeL5MwWr7O8q41Fu1n6BpRMm6KD0JOVWCJezW7anOJmcuySk6j2FRMPl3Z2fMH +p1I4420LlxN9H7QD5TVUJWCcujb2W9vhH9/R0qj9G9gkixfI0H/cGWd+Pe71ub5b +wxBTIe7U20uQ9imje8rShiZvgg3EocbWgPZcDnfHFjXVw/A1ocyIwpqjxooU8jiN +n1479sYR+R5TMc0zgZrTOKspcbNq5TEK138sFt79VB2d4oJNV/D0p0GktKpwisiZ ++xjr6iD2gZ9GGi0l0nQmtmLs+QAMuj+yOZX8CPwJlg7JuJYJ/nu0I5tBB1kOBml6 +Jk2o5o3gU6FbfLc3j7aQ/kRP14ByfXqXPTVNbPxrVzFEsAx/NVWaVqbH9iwSye1G +M4kpvZ9RvEHHegNxoN3spKaJkpM056gTBJhWQIHGCOAqv7Izm68NqjSX6+wx92iZ +ujR1PR9pJdOYtjhdmQrWGLK7a06AaOo1v5iQOJ9SN48ucyN2hY2wIZ5IMdQC2I9P +IhIRTSX28cT0WRnH9Sdv9fWQLSfNwrcYWiTDd5+0ImspCC3HzwcTjqTCoT6utrmU +eHAzLPjoUu9FvnrZJW3eMOffvHSh3lK8yW3dv2HKFoXaBD5dL2irk4yacSAIIo2f +4T44UqQSs2U1ip1CHbP64vI1FRNfhDdZRU8w -----END CERTIFICATE----- diff --git a/libbeat/common/transport/tlscommon/testdata/ca.key b/libbeat/common/transport/tlscommon/testdata/ca.key new file mode 100644 index 00000000000..9061f4479dc --- /dev/null +++ b/libbeat/common/transport/tlscommon/testdata/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/libbeat/common/transport/tlscommon/testdata/server.crt b/libbeat/common/transport/tlscommon/testdata/server.crt new file mode 100644 index 00000000000..50ca5ce8b32 --- /dev/null +++ b/libbeat/common/transport/tlscommon/testdata/server.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDlTCCAX0CAQEwDQYJKoZIhvcNAQELBQAwUzELMAkGA1UEBhMCVVMxEzARBgNV +BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFzAVBgNVBAMM +DmNhQGV4YW1wbGUuY29tMB4XDTIxMDEyNTE2MzQ0OVoXDTMxMDEyMzE2MzQ0OVow +UjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh +biBGcmFuY2lzY28xFjAUBgNVBAMMDSouZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBALFuNygrGLLSnD//JRfU6xMDqgizeVdQqDlLaP/HxQ84 +9RPWnjfbyx2M25JYcLvewPqKQ80lOYnMRhpvujmuKP7gQHNDWOsyXH5JljTX78Wb +I+nuVMeYjbUOh+6EgYNY59G5rH7xqgeu3y1YERfNdchEG8xjSxYeIZ7Ev6VMFF8r +AgMBAAEwDQYJKoZIhvcNAQELBQADggIBALyHDjVcY6Po1eHWTUCLLOW1ZzzkX4qu +gsfJM6qTIZIqh/O6tROGqH9kRw8SarIIZvtztfzuYtmQBE0qkBMzPzdN3x+3C4pz +jf2vsEKRqva9mf9y+JM0Mv0WUuPfusHxPKOCl1on71kP1GL1bYylKqazgVa2tAVa +78xs35YIuCM5apt0X+QO+Tnz/qfqJ7t3F7mP1aeCjYm8J20S8vKTYgkRkFX/8VJB +1zRPl0CAMyoHOMcrmb7wX8V1CIER7VBQ7h580B7/7okrw+Hr3xyMOA0w1DiRUQJE +biHBuDTRDmRg6W5nAwNLFLp/RfHttny0nEEcnzcjEStEKyDGbNg1W2ieWuIhgUza +L3W3ld9LDD9pMnQ8yYTMcL+J2Ir6ErhpGL3Hks42W2c/qYhvo3we6B2ADfsS7P+m +ku5W7/G2fDIlj6rtzaAeur+LSgsjU6kc1et2SJxjcJMPrS4xHxpAhJzD7h7f5N/B +RBc5cT2sE2vuUBRGkz0wC9AC2/kxmv4RwjsrYTY8rEOqHRkxDF18lfFocAoq7Hvr +lO6ft9/knzTQzKiizc6unXsLhUCvBzt50bA/gVLXmUmr1sncATKHWOLbvfRWat4I +0m52jlowgqnJPsXtl+wwNYHaw9gF71RTx/Ov2vZ8xm5SeBNkO8cpdAftETAEqpgp +fDlIVeywLvoN +-----END CERTIFICATE----- diff --git a/libbeat/common/transport/tlscommon/testdata/server.key b/libbeat/common/transport/tlscommon/testdata/server.key new file mode 100644 index 00000000000..8bb153a9006 --- /dev/null +++ b/libbeat/common/transport/tlscommon/testdata/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCxbjcoKxiy0pw//yUX1OsTA6oIs3lXUKg5S2j/x8UPOPUT1p43 +28sdjNuSWHC73sD6ikPNJTmJzEYab7o5rij+4EBzQ1jrMlx+SZY01+/FmyPp7lTH +mI21DofuhIGDWOfRuax+8aoHrt8tWBEXzXXIRBvMY0sWHiGexL+lTBRfKwIDAQAB +AoGAaBKW5cfJl/JzVhJphn4MWL3YeXwUW4Pi+KBj+UwLKW+mSTmk2mzgyfd6P3AC +yB/Tn+GD/YutIUehgxYv7G9ceZC85EsPM6+1s887olgKNKbCiZZvrLBcBCzEhzkN +QpC2/cuOOVYdYYQJZp9RX7herAJ5aqxZHUUtCrudgfCiAckCQQDo37NhBBfUlLc4 +LW3ryxydsh7MrTMU63+5IVtXosV3TFdWN9LC6CCarkILcOG5tmEmM6v1UQRAgCkm +lb+/3SrXAkEAwwz9+mcAU1lTTiy+dCJkKepviT4Ex+BFl0yJPfSN5+/Wg15DjwsN +vdE0H5nAT65aECiYy8V9DKNwHNcTIaZXzQJBAMvoPOBhPiCVC410MgC6e9cVRWTA +766Muuy26Y1l6HQac4r6HGEv8oSeuxPbhrsfmBdkPVjz1L5Juj6f9yOgHEcCQHMH +pHkaaay+D00ZQjDHX38AzUqJEtS1xRTXhFDPeyj/3uiWnQ0tHauGR1EjobDcSC0j +ZAk4rOjZMnMvvA6qRTkCQQCT6B0edwnMc9q/4XcdF+LptWRiYNbSKkrisb304N+d +lqbB76fGQY22onWcZEvcOmifmzmgj56QXSUot+fkNlVK +-----END RSA PRIVATE KEY----- diff --git a/libbeat/common/transport/tlscommon/tls_config.go b/libbeat/common/transport/tlscommon/tls_config.go index 9e7eb4548db..718dbe42db9 100644 --- a/libbeat/common/transport/tlscommon/tls_config.go +++ b/libbeat/common/transport/tlscommon/tls_config.go @@ -288,7 +288,7 @@ func verifyHostname(cert *x509.Certificate, hostname string) error { } for _, name := range dnsnames { - if len(name) > 0 && len(hostname) > 0 && name == hostname { + if matchHostnames(name, hostname) { if !validHostname(name, true) { return fmt.Errorf("invalid hostname in cert") } diff --git a/libbeat/common/transport/tlscommon/tls_config_test.go b/libbeat/common/transport/tlscommon/tls_config_test.go index 1490664d3d3..76dfa61497f 100644 --- a/libbeat/common/transport/tlscommon/tls_config_test.go +++ b/libbeat/common/transport/tlscommon/tls_config_test.go @@ -34,7 +34,10 @@ func TestMakeVerifyServerConnection(t *testing.T) { t.Fatalf("failed to open test certs: %+v", err) } - testCA, errs := LoadCertificateAuthorities([]string{filepath.Join("testdata", "cacert.crt")}) + testCA, errs := LoadCertificateAuthorities([]string{ + filepath.Join("testdata", "ca.crt"), + filepath.Join("testdata", "cacert.crt"), + }) if len(errs) > 0 { t.Fatalf("failed to load test certificate authorities: %+v", errs) } @@ -83,6 +86,15 @@ func TestMakeVerifyServerConnection(t *testing.T) { expectedCallback: true, expectedError: nil, }, + "default verification with certificates when required with correct wildcard cert": { + verificationMode: VerifyFull, + clientAuth: tls.RequireAndVerifyClientCert, + certAuthorities: testCA, + peerCerts: []*x509.Certificate{testCerts["wildcard"]}, + serverName: "hello.example.com", + expectedCallback: true, + expectedError: nil, + }, "certificate verification with certificates when required with correct cert": { verificationMode: VerifyCertificate, clientAuth: tls.RequireAndVerifyClientCert, @@ -181,6 +193,7 @@ func openTestCerts() (map[string]*x509.Certificate, error) { "expired": "tls.crt", "unknown authority": "unsigned_tls.crt", "correct": "client1.crt", + "wildcard": "server.crt", } { certBytes, err := ioutil.ReadFile(filepath.Join("testdata", certname)) diff --git a/libbeat/common/transport/tlscommon/validhostname.go b/libbeat/common/transport/tlscommon/validhostname.go index 15370b4d4f9..a6b2af7fb7c 100644 --- a/libbeat/common/transport/tlscommon/validhostname.go +++ b/libbeat/common/transport/tlscommon/validhostname.go @@ -47,7 +47,69 @@ package tlscommon -import "strings" +import ( + "strings" + "unicode/utf8" +) + +func matchHostnames(pattern, host string) bool { + pattern = toLowerCaseASCII(pattern) + host = toLowerCaseASCII(strings.TrimSuffix(host, ".")) + + if len(pattern) == 0 || len(host) == 0 { + return false + } + + patternParts := strings.Split(pattern, ".") + hostParts := strings.Split(host, ".") + + if len(patternParts) != len(hostParts) { + return false + } + + for i, patternPart := range patternParts { + if i == 0 && patternPart == "*" { + continue + } + if patternPart != hostParts[i] { + return false + } + } + + return true +} + +// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use +// an explicitly ASCII function to avoid any sharp corners resulting from +// performing Unicode operations on DNS labels. +func toLowerCaseASCII(in string) string { + // If the string is already lower-case then there's nothing to do. + isAlreadyLowerCase := true + for _, c := range in { + if c == utf8.RuneError { + // If we get a UTF-8 error then there might be + // upper-case ASCII bytes in the invalid sequence. + isAlreadyLowerCase = false + break + } + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + + if isAlreadyLowerCase { + return in + } + + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} // validHostname reports whether host is a valid hostname that can be matched or // matched against according to RFC 6125 2.2, with some leniency to accommodate