google.golang.org/grpc@v1.62.1/internal/credentials/xds/handshake_info_test.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package xds 20 21 import ( 22 "crypto/x509" 23 "net" 24 "net/url" 25 "regexp" 26 "testing" 27 28 "google.golang.org/grpc/internal/xds/matcher" 29 ) 30 31 func TestDNSMatch(t *testing.T) { 32 tests := []struct { 33 desc string 34 host string 35 pattern string 36 wantMatch bool 37 }{ 38 { 39 desc: "invalid wildcard 1", 40 host: "aa.example.com", 41 pattern: "*a.example.com", 42 wantMatch: false, 43 }, 44 { 45 desc: "invalid wildcard 2", 46 host: "aa.example.com", 47 pattern: "a*.example.com", 48 wantMatch: false, 49 }, 50 { 51 desc: "invalid wildcard 3", 52 host: "abc.example.com", 53 pattern: "a*c.example.com", 54 wantMatch: false, 55 }, 56 { 57 desc: "wildcard in one of the middle components", 58 host: "abc.test.example.com", 59 pattern: "abc.*.example.com", 60 wantMatch: false, 61 }, 62 { 63 desc: "single component wildcard", 64 host: "a.example.com", 65 pattern: "*", 66 wantMatch: false, 67 }, 68 { 69 desc: "short host name", 70 host: "a.com", 71 pattern: "*.example.com", 72 wantMatch: false, 73 }, 74 { 75 desc: "suffix mismatch", 76 host: "a.notexample.com", 77 pattern: "*.example.com", 78 wantMatch: false, 79 }, 80 { 81 desc: "wildcard match across components", 82 host: "sub.test.example.com", 83 pattern: "*.example.com.", 84 wantMatch: false, 85 }, 86 { 87 desc: "host doesn't end in period", 88 host: "test.example.com", 89 pattern: "test.example.com.", 90 wantMatch: true, 91 }, 92 { 93 desc: "pattern doesn't end in period", 94 host: "test.example.com.", 95 pattern: "test.example.com", 96 wantMatch: true, 97 }, 98 { 99 desc: "case insensitive", 100 host: "TEST.EXAMPLE.COM.", 101 pattern: "test.example.com.", 102 wantMatch: true, 103 }, 104 { 105 desc: "simple match", 106 host: "test.example.com", 107 pattern: "test.example.com", 108 wantMatch: true, 109 }, 110 { 111 desc: "good wildcard", 112 host: "a.example.com", 113 pattern: "*.example.com", 114 wantMatch: true, 115 }, 116 } 117 118 for _, test := range tests { 119 t.Run(test.desc, func(t *testing.T) { 120 gotMatch := dnsMatch(test.host, test.pattern) 121 if gotMatch != test.wantMatch { 122 t.Fatalf("dnsMatch(%s, %s) = %v, want %v", test.host, test.pattern, gotMatch, test.wantMatch) 123 } 124 }) 125 } 126 } 127 128 func TestMatchingSANExists_FailureCases(t *testing.T) { 129 url1, err := url.Parse("http://golang.org") 130 if err != nil { 131 t.Fatalf("url.Parse() failed: %v", err) 132 } 133 url2, err := url.Parse("https://github.com/grpc/grpc-go") 134 if err != nil { 135 t.Fatalf("url.Parse() failed: %v", err) 136 } 137 inputCert := &x509.Certificate{ 138 DNSNames: []string{"foo.bar.example.com", "bar.baz.test.com", "*.example.com"}, 139 EmailAddresses: []string{"foobar@example.com", "barbaz@test.com"}, 140 IPAddresses: []net.IP{net.ParseIP("192.0.0.1"), net.ParseIP("2001:db8::68")}, 141 URIs: []*url.URL{url1, url2}, 142 } 143 144 tests := []struct { 145 desc string 146 sanMatchers []matcher.StringMatcher 147 }{ 148 { 149 desc: "exact match", 150 sanMatchers: []matcher.StringMatcher{ 151 matcher.StringMatcherForTesting(newStringP("abcd.test.com"), nil, nil, nil, nil, false), 152 matcher.StringMatcherForTesting(newStringP("http://golang"), nil, nil, nil, nil, false), 153 matcher.StringMatcherForTesting(newStringP("HTTP://GOLANG.ORG"), nil, nil, nil, nil, false), 154 }, 155 }, 156 { 157 desc: "prefix match", 158 sanMatchers: []matcher.StringMatcher{ 159 matcher.StringMatcherForTesting(nil, newStringP("i-aint-the-one"), nil, nil, nil, false), 160 matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false), 161 matcher.StringMatcherForTesting(nil, newStringP("FOO.BAR"), nil, nil, nil, false), 162 }, 163 }, 164 { 165 desc: "suffix match", 166 sanMatchers: []matcher.StringMatcher{ 167 matcher.StringMatcherForTesting(nil, nil, newStringP("i-aint-the-one"), nil, nil, false), 168 matcher.StringMatcherForTesting(nil, nil, newStringP("1::68"), nil, nil, false), 169 matcher.StringMatcherForTesting(nil, nil, newStringP(".COM"), nil, nil, false), 170 }, 171 }, 172 { 173 desc: "regex match", 174 sanMatchers: []matcher.StringMatcher{ 175 matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`.*\.examples\.com`), false), 176 matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false), 177 }, 178 }, 179 { 180 desc: "contains match", 181 sanMatchers: []matcher.StringMatcher{ 182 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("i-aint-the-one"), nil, false), 183 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("2001:db8:1:1::68"), nil, false), 184 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("GRPC"), nil, false), 185 }, 186 }, 187 } 188 189 for _, test := range tests { 190 t.Run(test.desc, func(t *testing.T) { 191 hi := NewHandshakeInfo(nil, nil, test.sanMatchers, false) 192 193 if hi.MatchingSANExists(inputCert) { 194 t.Fatalf("hi.MatchingSANExists(%+v) with SAN matchers +%v succeeded when expected to fail", inputCert, test.sanMatchers) 195 } 196 }) 197 } 198 } 199 200 func TestMatchingSANExists_Success(t *testing.T) { 201 url1, err := url.Parse("http://golang.org") 202 if err != nil { 203 t.Fatalf("url.Parse() failed: %v", err) 204 } 205 url2, err := url.Parse("https://github.com/grpc/grpc-go") 206 if err != nil { 207 t.Fatalf("url.Parse() failed: %v", err) 208 } 209 inputCert := &x509.Certificate{ 210 DNSNames: []string{"baz.test.com", "*.example.com"}, 211 EmailAddresses: []string{"foobar@example.com", "barbaz@test.com"}, 212 IPAddresses: []net.IP{net.ParseIP("192.0.0.1"), net.ParseIP("2001:db8::68")}, 213 URIs: []*url.URL{url1, url2}, 214 } 215 216 tests := []struct { 217 desc string 218 sanMatchers []matcher.StringMatcher 219 }{ 220 { 221 desc: "no san matchers", 222 }, 223 { 224 desc: "exact match dns wildcard", 225 sanMatchers: []matcher.StringMatcher{ 226 matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false), 227 matcher.StringMatcherForTesting(newStringP("https://github.com/grpc/grpc-java"), nil, nil, nil, nil, false), 228 matcher.StringMatcherForTesting(newStringP("abc.example.com"), nil, nil, nil, nil, false), 229 }, 230 }, 231 { 232 desc: "exact match ignore case", 233 sanMatchers: []matcher.StringMatcher{ 234 matcher.StringMatcherForTesting(newStringP("FOOBAR@EXAMPLE.COM"), nil, nil, nil, nil, true), 235 }, 236 }, 237 { 238 desc: "prefix match", 239 sanMatchers: []matcher.StringMatcher{ 240 matcher.StringMatcherForTesting(nil, nil, newStringP(".co.in"), nil, nil, false), 241 matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false), 242 matcher.StringMatcherForTesting(nil, newStringP("baz.test"), nil, nil, nil, false), 243 }, 244 }, 245 { 246 desc: "prefix match ignore case", 247 sanMatchers: []matcher.StringMatcher{ 248 matcher.StringMatcherForTesting(nil, newStringP("BAZ.test"), nil, nil, nil, true), 249 }, 250 }, 251 { 252 desc: "suffix match", 253 sanMatchers: []matcher.StringMatcher{ 254 matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false), 255 matcher.StringMatcherForTesting(nil, nil, newStringP("192.168.1.1"), nil, nil, false), 256 matcher.StringMatcherForTesting(nil, nil, newStringP("@test.com"), nil, nil, false), 257 }, 258 }, 259 { 260 desc: "suffix match ignore case", 261 sanMatchers: []matcher.StringMatcher{ 262 matcher.StringMatcherForTesting(nil, nil, newStringP("@test.COM"), nil, nil, true), 263 }, 264 }, 265 { 266 desc: "regex match", 267 sanMatchers: []matcher.StringMatcher{ 268 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("https://github.com/grpc/grpc-java"), nil, false), 269 matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false), 270 matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`.*\.test\.com`), false), 271 }, 272 }, 273 { 274 desc: "contains match", 275 sanMatchers: []matcher.StringMatcher{ 276 matcher.StringMatcherForTesting(newStringP("https://github.com/grpc/grpc-java"), nil, nil, nil, nil, false), 277 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("2001:68::db8"), nil, false), 278 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("192.0.0"), nil, false), 279 }, 280 }, 281 { 282 desc: "contains match ignore case", 283 sanMatchers: []matcher.StringMatcher{ 284 matcher.StringMatcherForTesting(nil, nil, nil, newStringP("GRPC"), nil, true), 285 }, 286 }, 287 } 288 289 for _, test := range tests { 290 t.Run(test.desc, func(t *testing.T) { 291 hi := NewHandshakeInfo(nil, nil, test.sanMatchers, false) 292 293 if !hi.MatchingSANExists(inputCert) { 294 t.Fatalf("hi.MatchingSANExists(%+v) with SAN matchers +%v failed when expected to succeed", inputCert, test.sanMatchers) 295 } 296 }) 297 } 298 } 299 300 func newStringP(s string) *string { 301 return &s 302 }