| ... | @@ -316,29 +316,53 @@ pub const Parsed = struct { | ... | @@ -316,29 +316,53 @@ pub const Parsed = struct { |
| 316 | return error.CertificateHostMismatch; | 316 | return error.CertificateHostMismatch; |
| 317 | } | 317 | } |
| 318 | | 318 | |
| | 319 | // Check hostname according to RFC2818 specification: |
| | 320 | // |
| | 321 | // If more than one identity of a given type is present in |
| | 322 | // the certificate (e.g., more than one DNSName name, a match in any one |
| | 323 | // of the set is considered acceptable.) Names may contain the wildcard |
| | 324 | // character * which is considered to match any single domain name |
| | 325 | // component or component fragment. E.g., *.a.com matches foo.a.com but |
| | 326 | // not bar.foo.a.com. f*.com matches foo.com but not bar.com. |
| 319 | fn checkHostName(host_name: []const u8, dns_name: []const u8) bool { | 327 | fn checkHostName(host_name: []const u8, dns_name: []const u8) bool { |
| 320 | if (mem.eql(u8, dns_name, host_name)) { | 328 | if (mem.eql(u8, dns_name, host_name)) { |
| 321 | return true; // exact match | 329 | return true; // exact match |
| 322 | } | 330 | } |
| 323 | | 331 | |
| 324 | if (mem.startsWith(u8, dns_name, "*.")) { | 332 | var it_host = std.mem.split(u8, host_name, "."); |
| 325 | // wildcard certificate, matches any subdomain | 333 | var it_dns = std.mem.split(u8, dns_name, "."); |
| 326 | // TODO: I think wildcards are not supposed to match any prefix but | 334 | |
| 327 | // only match exactly one subdomain. | 335 | const len_match = while (true) { |
| 328 | if (mem.endsWith(u8, host_name, dns_name[1..])) { | 336 | const host = it_host.next(); |
| 329 | // The host_name has a subdomain, but the important part matches. | 337 | const dns = it_dns.next(); |
| 330 | return true; | 338 | |
| | 339 | if (host == null or dns == null) { |
| | 340 | break host == null and dns == null; |
| 331 | } | 341 | } |
| 332 | if (mem.eql(u8, dns_name[2..], host_name)) { | 342 | |
| 333 | // The host_name has no subdomain and matches exactly. | 343 | // If not a wildcard and they dont |
| 334 | return true; | 344 | // match then there is no match. |
| | 345 | if (mem.eql(u8, dns.?, "*") == false and mem.eql(u8, dns.?, host.?) == false) { |
| | 346 | return false; |
| 335 | } | 347 | } |
| 336 | } | 348 | }; |
| 337 | | 349 | |
| 338 | return false; | 350 | // If the components are not the same |
| | 351 | // length then there is no match. |
| | 352 | return len_match; |
| 339 | } | 353 | } |
| 340 | }; | 354 | }; |
| 341 | | 355 | |
| | 356 | test "Parsed.checkHostName" { |
| | 357 | const expectEqual = std.testing.expectEqual; |
| | 358 | |
| | 359 | try expectEqual(true, Parsed.checkHostName("ziglang.org", "ziglang.org")); |
| | 360 | try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.ziglang.org")); |
| | 361 | try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org")); |
| | 362 | try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org")); |
| | 363 | try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org")); |
| | 364 | } |
| | 365 | |
| 342 | pub fn parse(cert: Certificate) !Parsed { | 366 | pub fn parse(cert: Certificate) !Parsed { |
| 343 | const cert_bytes = cert.buffer; | 367 | const cert_bytes = cert.buffer; |
| 344 | const certificate = try der.Element.parse(cert_bytes, cert.index); | 368 | const certificate = try der.Element.parse(cert_bytes, cert.index); |