authorgravatar for 99486674+yamashitax@users.noreply.github.com山下 <99486674+yamashitax@users.noreply.github.com> 2023-01-20 04:13:42+09:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-19 19:13:42+00:00
log6c98c8d891808bab97a5ccf4f25e016c401dd8c3
tree654c58b28c509b7a33b03fe618043a7927362046
parent989b0e620be22278d32ec848069c17667cbd2f4f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Wildcard certs should only validate one level of sub domain


1 files changed, 36 insertions(+), 12 deletions(-)

lib/std/crypto/Certificate.zig+36-12
...@@ -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 }
318318
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 match329 return true; // exact match
322 }330 }
323331
324 if (mem.startsWith(u8, dns_name, "*.")) {332 var it_host = std.mem.split(u8, host_name, ".");
325 // wildcard certificate, matches any subdomain333 var it_dns = std.mem.split(u8, dns_name, ".");
326 // TODO: I think wildcards are not supposed to match any prefix but334
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 };
337349
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};
341355
356test "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
342pub fn parse(cert: Certificate) !Parsed {366pub 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);