mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Validate wildcard TLS certificates correctly (#24829)
Validate wildcard certificates as specified in RFC 6125. In particular, `*.example.com` should match `foo.example.com` but NOT `bar.foo.example.com` as it previously did.
This commit is contained in:
parent
6bcdcf85c7
commit
96e4825fbb
@ -344,45 +344,73 @@ pub const Parsed = struct {
|
|||||||
// component or component fragment. E.g., *.a.com matches foo.a.com but
|
// component or component fragment. E.g., *.a.com matches foo.a.com but
|
||||||
// not bar.foo.a.com. f*.com matches foo.com but not bar.com.
|
// not bar.foo.a.com. f*.com matches foo.com but not bar.com.
|
||||||
fn checkHostName(host_name: []const u8, dns_name: []const u8) bool {
|
fn checkHostName(host_name: []const u8, dns_name: []const u8) bool {
|
||||||
|
// Empty strings should not match
|
||||||
|
if (host_name.len == 0 or dns_name.len == 0) return false;
|
||||||
|
|
||||||
|
// RFC 6125 Section 6.4.1: Exact match (case-insensitive)
|
||||||
if (std.ascii.eqlIgnoreCase(dns_name, host_name)) {
|
if (std.ascii.eqlIgnoreCase(dns_name, host_name)) {
|
||||||
return true; // exact match
|
return true; // exact match
|
||||||
}
|
}
|
||||||
|
|
||||||
var it_host = std.mem.splitScalar(u8, host_name, '.');
|
// RFC 6125 Section 6.4.3: Wildcard certificates
|
||||||
var it_dns = std.mem.splitScalar(u8, dns_name, '.');
|
// Wildcard must be leftmost label and in the form "*.rest.of.domain"
|
||||||
|
if (dns_name.len >= 3 and mem.startsWith(u8, dns_name, "*.")) {
|
||||||
|
const wildcard_suffix = dns_name[2..];
|
||||||
|
|
||||||
const len_match = while (true) {
|
// No additional wildcards allowed in the suffix
|
||||||
const host = it_host.next();
|
if (mem.indexOf(u8, wildcard_suffix, "*") != null) return false;
|
||||||
const dns = it_dns.next();
|
|
||||||
|
|
||||||
if (host == null or dns == null) {
|
// Find the first dot in hostname to split first label from rest
|
||||||
break host == null and dns == null;
|
const dot_pos = mem.indexOf(u8, host_name, ".") orelse return false;
|
||||||
|
|
||||||
|
// Wildcard matches exactly one label, so compare the rest
|
||||||
|
const host_suffix = host_name[dot_pos + 1 ..];
|
||||||
|
|
||||||
|
// Match suffixes (case-insensitive per RFC 6125)
|
||||||
|
return std.ascii.eqlIgnoreCase(wildcard_suffix, host_suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not a wildcard and they dont
|
|
||||||
// match then there is no match.
|
|
||||||
if (mem.eql(u8, dns.?, "*") == false and std.ascii.eqlIgnoreCase(dns.?, host.?) == false) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// If the components are not the same
|
|
||||||
// length then there is no match.
|
|
||||||
return len_match;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
test "Parsed.checkHostName" {
|
test "Parsed.checkHostName RFC 6125 compliance" {
|
||||||
const expectEqual = std.testing.expectEqual;
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
|
||||||
|
// Exact match tests
|
||||||
try expectEqual(true, Parsed.checkHostName("ziglang.org", "ziglang.org"));
|
try expectEqual(true, Parsed.checkHostName("ziglang.org", "ziglang.org"));
|
||||||
|
try expectEqual(true, Parsed.checkHostName("ziglang.org", "Ziglang.org")); // case insensitive
|
||||||
|
try expectEqual(true, Parsed.checkHostName("ZIGLANG.ORG", "ziglang.org")); // case insensitive
|
||||||
|
|
||||||
|
// Valid wildcard matches
|
||||||
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.ziglang.org"));
|
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.ziglang.org"));
|
||||||
|
try expectEqual(true, Parsed.checkHostName("BAR.ziglang.org", "*.Ziglang.ORG")); // case insensitive
|
||||||
|
|
||||||
|
// RFC 6125: Wildcard matches exactly one label
|
||||||
try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org"));
|
try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org"));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("ziglang.org", "*.ziglang.org")); // no empty match
|
||||||
|
|
||||||
|
// RFC 6125: No partial wildcards allowed
|
||||||
try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org"));
|
try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org"));
|
||||||
try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));
|
try expectEqual(false, Parsed.checkHostName("ziglang.org", "*lang.org"));
|
||||||
// host name check should be case insensitive
|
try expectEqual(false, Parsed.checkHostName("ziglang.org", "zi*ng.org"));
|
||||||
try expectEqual(true, Parsed.checkHostName("ziglang.org", "Ziglang.org"));
|
|
||||||
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));
|
// RFC 6125: No multiple wildcards
|
||||||
|
try expectEqual(false, Parsed.checkHostName("foo.bar.org", "*.*.org"));
|
||||||
|
|
||||||
|
// RFC 6125: Wildcard must be in leftmost label
|
||||||
|
try expectEqual(false, Parsed.checkHostName("foo.bar.org", "foo.*.org"));
|
||||||
|
|
||||||
|
// Single label hostnames should not match wildcards
|
||||||
|
try expectEqual(false, Parsed.checkHostName("localhost", "*.local"));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("localhost", "*.localhost"));
|
||||||
|
|
||||||
|
// Edge cases
|
||||||
|
try expectEqual(false, Parsed.checkHostName("", ""));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("example.com", ""));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("", "*.example.com"));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("example.com", "*"));
|
||||||
|
try expectEqual(false, Parsed.checkHostName("example.com", "*."));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ParseError = der.Element.ParseError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
pub const ParseError = der.Element.ParseError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user