| author | |
| committer | |
| log | c1e7eb738934399737b3a8452ad9b68bb26805d3 |
| tree | 8f1249e0acf98492730adbfe4e5594b84930e9af |
| parent | 2511830442fd96d04f578f2c4251b1994f08c994 |
This makes comparing host name with dns name from certificate case
insensitive.
I found a few domains (from the
[cloudflare](https://radar.cloudflare.com/domains) list of top domains)
for which tls.Client fails to connect. Error is:
```zig
error: TlsInitializationFailed
Code/zig/lib/std/crypto/Certificate.zig:336:9: 0x1177b1f in verifyHostName (http_get_std)
return error.CertificateHostMismatch;
Code/zig/lib/std/crypto/tls23/handshake_client.zig:461:25: 0x11752bd in parseServerCertificate (http_get_std)
try subject.verifyHostName(opt.host);
```
In its certificate this domains have host names which are not strictly
lower case. This is what checkHostName is comparing:
|host_name | dns_name |
|------------------------------------------------|
|ey.com | EY.COM |
|truist.com | Truist.com |
|wscampanhas.bradesco | WSCAMPANHAS.BRADESCO |
|dell.com | Dell.com |
From
[RFC2818](https://datatracker.ietf.org/doc/html/rfc2818#section-2.4):
> Matching is performed using the matching rules specified by
[RFC2459].
From [RFC2459](https://datatracker.ietf.org/doc/html/rfc2459#section-4.2.1.7):
> When comparing URIs, conforming implementations
> MUST compare the scheme and host without regard to case, but assume
> the remainder of the scheme-specific-part is case sensitive.
Testing with:
```
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
if (args.len > 1) {
const domain = args[1];
var client: std.http.Client = .{ .allocator = allocator };
defer client.deinit();
// Add https:// prefix if needed
const url = brk: {
const scheme = "https://";
if (domain.len >= scheme.len and std.mem.eql(u8, domain[0..scheme.len], scheme))
break :brk domain;
var url_buf: [128]u8 = undefined;
break :brk try std.fmt.bufPrint(&url_buf, "https://{s}", .{domain});
};
const uri = try std.Uri.parse(url);
var server_header_buffer: [16 * 1024]u8 = undefined;
var req = try client.open(.GET, uri, .{ .server_header_buffer = &server_header_buffer });
defer req.deinit();
try req.send();
try req.wait();
}
}
```
`$ zig run example/main.zig -- truist.com `1 files changed, 5 insertions(+), 2 deletions(-)
lib/std/crypto/Certificate.zig+5-2| ... | @@ -345,7 +345,7 @@ pub const Parsed = struct { | ... | @@ -345,7 +345,7 @@ pub const Parsed = struct { |
| 345 | // component or component fragment. E.g., *.a.com matches foo.a.com but | 345 | // component or component fragment. E.g., *.a.com matches foo.a.com but |
| 346 | // not bar.foo.a.com. f*.com matches foo.com but not bar.com. | 346 | // not bar.foo.a.com. f*.com matches foo.com but not bar.com. |
| 347 | fn checkHostName(host_name: []const u8, dns_name: []const u8) bool { | 347 | fn checkHostName(host_name: []const u8, dns_name: []const u8) bool { |
| 348 | if (mem.eql(u8, dns_name, host_name)) { | 348 | if (std.ascii.eqlIgnoreCase(dns_name, host_name)) { |
| 349 | return true; // exact match | 349 | return true; // exact match |
| 350 | } | 350 | } |
| 351 | 351 | ||
| ... | @@ -362,7 +362,7 @@ pub const Parsed = struct { | ... | @@ -362,7 +362,7 @@ pub const Parsed = struct { |
| 362 | 362 | ||
| 363 | // If not a wildcard and they dont | 363 | // If not a wildcard and they dont |
| 364 | // match then there is no match. | 364 | // match then there is no match. |
| 365 | if (mem.eql(u8, dns.?, "*") == false and mem.eql(u8, dns.?, host.?) == false) { | 365 | if (mem.eql(u8, dns.?, "*") == false and std.ascii.eqlIgnoreCase(dns.?, host.?) == false) { |
| 366 | return false; | 366 | return false; |
| 367 | } | 367 | } |
| 368 | }; | 368 | }; |
| ... | @@ -381,6 +381,9 @@ test "Parsed.checkHostName" { | ... | @@ -381,6 +381,9 @@ test "Parsed.checkHostName" { |
| 381 | try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org")); | 381 | try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org")); |
| 382 | try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org")); | 382 | try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org")); |
| 383 | try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org")); | 383 | try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org")); |
| 384 | // host name check should be case insensitive | ||
| 385 | try expectEqual(true, Parsed.checkHostName("ziglang.org", "Ziglang.org")); | ||
| 386 | try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG")); | ||
| 384 | } | 387 | } |
| 385 | 388 | ||
| 386 | pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError; | 389 | pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError; |