mirror of
https://github.com/ziglang/zig.git
synced 2026-01-31 03:33:37 +00:00
Merge pull request #14229 from star-tek-mb/wincerts
windows root certificate scanning
This commit is contained in:
commit
f4b4e570d8
@ -15,6 +15,8 @@ pub const Algorithm = enum {
|
||||
ecdsa_with_SHA256,
|
||||
ecdsa_with_SHA384,
|
||||
ecdsa_with_SHA512,
|
||||
md2WithRSAEncryption,
|
||||
md5WithRSAEncryption,
|
||||
|
||||
pub const map = std.ComptimeStringMap(Algorithm, .{
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
|
||||
@ -26,6 +28,8 @@ pub const Algorithm = enum {
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
|
||||
});
|
||||
|
||||
pub fn Hash(comptime algorithm: Algorithm) type {
|
||||
@ -35,6 +39,8 @@ pub const Algorithm = enum {
|
||||
.ecdsa_with_SHA256, .sha256WithRSAEncryption => crypto.hash.sha2.Sha256,
|
||||
.ecdsa_with_SHA384, .sha384WithRSAEncryption => crypto.hash.sha2.Sha384,
|
||||
.ecdsa_with_SHA512, .sha512WithRSAEncryption => crypto.hash.sha2.Sha512,
|
||||
.md2WithRSAEncryption => @compileError("unimplemented"),
|
||||
.md5WithRSAEncryption => crypto.hash.Md5,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -55,10 +61,13 @@ pub const Attribute = enum {
|
||||
countryName,
|
||||
localityName,
|
||||
stateOrProvinceName,
|
||||
streetAddress,
|
||||
organizationName,
|
||||
organizationalUnitName,
|
||||
postalCode,
|
||||
organizationIdentifier,
|
||||
pkcs9_emailAddress,
|
||||
domainComponent,
|
||||
|
||||
pub const map = std.ComptimeStringMap(Attribute, .{
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
@ -66,19 +75,24 @@ pub const Attribute = enum {
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x09 }, .streetAddress },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x11 }, .postalCode },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
|
||||
.{ &[_]u8{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
|
||||
});
|
||||
};
|
||||
|
||||
pub const NamedCurve = enum {
|
||||
secp384r1,
|
||||
secp521r1,
|
||||
X9_62_prime256v1,
|
||||
|
||||
pub const map = std.ComptimeStringMap(NamedCurve, .{
|
||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
|
||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
|
||||
});
|
||||
};
|
||||
@ -93,17 +107,40 @@ pub const ExtensionId = enum {
|
||||
crl_number,
|
||||
certificate_policies,
|
||||
authority_key_identifier,
|
||||
msCertsrvCAVersion,
|
||||
commonName,
|
||||
ext_key_usage,
|
||||
crl_distribution_points,
|
||||
info_access,
|
||||
entrustVersInfo,
|
||||
enroll_certtype,
|
||||
pe_logotype,
|
||||
netscape_cert_type,
|
||||
netscape_comment,
|
||||
|
||||
pub const map = std.ComptimeStringMap(ExtensionId, .{
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0F }, .key_usage },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0A }, .basic_constraints },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x13 }, .basic_constraints },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x14 }, .crl_number },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x20 }, .certificate_policies },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
|
||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
|
||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
|
||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
|
||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
|
||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
|
||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
|
||||
});
|
||||
};
|
||||
|
||||
@ -238,6 +275,10 @@ pub const Parsed = struct {
|
||||
parsed_issuer.pub_key_algo,
|
||||
parsed_issuer.pubKey(),
|
||||
),
|
||||
|
||||
.md2WithRSAEncryption, .md5WithRSAEncryption => {
|
||||
return error.CertificateSignatureAlgorithmUnsupported;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,13 +395,16 @@ pub fn parse(cert: Certificate) !Parsed {
|
||||
var atav_i = atav.slice.start;
|
||||
while (atav_i < atav.slice.end) {
|
||||
const ty_elem = try der.Element.parse(cert_bytes, atav_i);
|
||||
const ty = try parseAttribute(cert_bytes, ty_elem);
|
||||
const val = try der.Element.parse(cert_bytes, ty_elem.slice.end);
|
||||
atav_i = val.slice.end;
|
||||
const ty = parseAttribute(cert_bytes, ty_elem) catch |err| switch (err) {
|
||||
error.CertificateHasUnrecognizedObjectId => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
switch (ty) {
|
||||
.commonName => common_name = val.slice,
|
||||
else => {},
|
||||
}
|
||||
atav_i = val.slice.end;
|
||||
}
|
||||
rdn_i = atav.slice.end;
|
||||
}
|
||||
@ -712,6 +756,9 @@ fn verify_ecdsa(
|
||||
};
|
||||
|
||||
switch (sig_named_curve) {
|
||||
.secp521r1 => {
|
||||
return error.CertificateSignatureNamedCurveUnsupported;
|
||||
},
|
||||
.secp384r1 => {
|
||||
const P = crypto.ecc.P384;
|
||||
const Ecdsa = crypto.sign.ecdsa.Ecdsa(P, Hash);
|
||||
|
||||
@ -57,10 +57,8 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void {
|
||||
pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
|
||||
switch (builtin.os.tag) {
|
||||
.linux => return rescanLinux(cb, gpa),
|
||||
.windows => {
|
||||
// TODO
|
||||
},
|
||||
.macos => return rescanMac(cb, gpa),
|
||||
.windows => return rescanWindows(cb, gpa),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@ -109,6 +107,38 @@ pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
|
||||
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
||||
}
|
||||
|
||||
pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {
|
||||
cb.bytes.clearRetainingCapacity();
|
||||
cb.map.clearRetainingCapacity();
|
||||
|
||||
const w = std.os.windows;
|
||||
const GetLastError = w.kernel32.GetLastError;
|
||||
const root = [4:0]u16{ 'R', 'O', 'O', 'T' };
|
||||
const store = w.crypt32.CertOpenSystemStoreW(null, &root) orelse switch (GetLastError()) {
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
else => |err| return w.unexpectedError(err),
|
||||
};
|
||||
defer _ = w.crypt32.CertCloseStore(store, 0);
|
||||
|
||||
var ctx = w.crypt32.CertEnumCertificatesInStore(store, null);
|
||||
while (ctx) |context| : (ctx = w.crypt32.CertEnumCertificatesInStore(store, ctx)) {
|
||||
const decoded_start = @intCast(u32, cb.bytes.items.len);
|
||||
const encoded_cert = context.pbCertEncoded[0..context.cbCertEncoded];
|
||||
try cb.bytes.appendSlice(gpa, encoded_cert);
|
||||
const parsed_cert = try Certificate.parse(.{
|
||||
.buffer = cb.bytes.items,
|
||||
.index = decoded_start,
|
||||
});
|
||||
const gop = try cb.map.getOrPutContext(gpa, parsed_cert.subject_slice, .{ .cb = cb });
|
||||
if (gop.found_existing) {
|
||||
cb.bytes.items.len = decoded_start;
|
||||
} else {
|
||||
gop.value_ptr.* = decoded_start;
|
||||
}
|
||||
}
|
||||
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
||||
}
|
||||
|
||||
pub fn addCertsFromDirPath(
|
||||
cb: *Bundle,
|
||||
gpa: Allocator,
|
||||
|
||||
@ -746,7 +746,79 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
|
||||
const arena = result.arena.allocator();
|
||||
errdefer result.deinit();
|
||||
|
||||
if (builtin.target.os.tag == .windows or builtin.link_libc) {
|
||||
if (builtin.target.os.tag == .windows) {
|
||||
const name_c = try std.cstr.addNullByte(allocator, name);
|
||||
defer allocator.free(name_c);
|
||||
|
||||
const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
|
||||
defer allocator.free(port_c);
|
||||
|
||||
const ws2_32 = os.windows.ws2_32;
|
||||
const hints = os.addrinfo{
|
||||
.flags = ws2_32.AI.NUMERICSERV,
|
||||
.family = os.AF.UNSPEC,
|
||||
.socktype = os.SOCK.STREAM,
|
||||
.protocol = os.IPPROTO.TCP,
|
||||
.canonname = null,
|
||||
.addr = null,
|
||||
.addrlen = 0,
|
||||
.next = null,
|
||||
};
|
||||
var res: *os.addrinfo = undefined;
|
||||
var first = true;
|
||||
while (true) {
|
||||
const rc = ws2_32.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);
|
||||
switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
|
||||
@intToEnum(os.windows.ws2_32.WinsockError, 0) => break,
|
||||
.WSATRY_AGAIN => return error.TemporaryNameServerFailure,
|
||||
.WSANO_RECOVERY => return error.NameServerFailure,
|
||||
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
|
||||
.WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
|
||||
.WSAHOST_NOT_FOUND => return error.UnknownHostName,
|
||||
.WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
|
||||
.WSAEINVAL => unreachable,
|
||||
.WSAESOCKTNOSUPPORT => unreachable,
|
||||
.WSANOTINITIALISED => {
|
||||
if (!first) return error.Unexpected;
|
||||
first = false;
|
||||
try os.windows.callWSAStartup();
|
||||
continue;
|
||||
},
|
||||
else => |err| return os.windows.unexpectedWSAError(err),
|
||||
}
|
||||
}
|
||||
defer ws2_32.freeaddrinfo(res);
|
||||
|
||||
const addr_count = blk: {
|
||||
var count: usize = 0;
|
||||
var it: ?*os.addrinfo = res;
|
||||
while (it) |info| : (it = info.next) {
|
||||
if (info.addr != null) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
break :blk count;
|
||||
};
|
||||
result.addrs = try arena.alloc(Address, addr_count);
|
||||
|
||||
var it: ?*os.addrinfo = res;
|
||||
var i: usize = 0;
|
||||
while (it) |info| : (it = info.next) {
|
||||
const addr = info.addr orelse continue;
|
||||
result.addrs[i] = Address.initPosix(@alignCast(4, addr));
|
||||
|
||||
if (info.canonname) |n| {
|
||||
if (result.canon_name == null) {
|
||||
result.canon_name = try arena.dupe(u8, mem.sliceTo(n, 0));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (builtin.link_libc) {
|
||||
const name_c = try std.cstr.addNullByte(allocator, name);
|
||||
defer allocator.free(name_c);
|
||||
|
||||
@ -765,19 +837,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
|
||||
.next = null,
|
||||
};
|
||||
var res: *os.addrinfo = undefined;
|
||||
const rc = sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);
|
||||
if (builtin.target.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
|
||||
@intToEnum(os.windows.ws2_32.WinsockError, 0) => {},
|
||||
.WSATRY_AGAIN => return error.TemporaryNameServerFailure,
|
||||
.WSANO_RECOVERY => return error.NameServerFailure,
|
||||
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
|
||||
.WSA_NOT_ENOUGH_MEMORY => return error.OutOfMemory,
|
||||
.WSAHOST_NOT_FOUND => return error.UnknownHostName,
|
||||
.WSATYPE_NOT_FOUND => return error.ServiceUnavailable,
|
||||
.WSAEINVAL => unreachable,
|
||||
.WSAESOCKTNOSUPPORT => unreachable,
|
||||
else => |err| return os.windows.unexpectedWSAError(err),
|
||||
} else switch (rc) {
|
||||
switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
|
||||
@intToEnum(sys.EAI, 0) => {},
|
||||
.ADDRFAMILY => return error.HostLacksNetworkAddresses,
|
||||
.AGAIN => return error.TemporaryNameServerFailure,
|
||||
@ -824,6 +884,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (builtin.target.os.tag == .linux) {
|
||||
const flags = std.c.AI.NUMERICSERV;
|
||||
const family = os.AF.UNSPEC;
|
||||
|
||||
@ -28,6 +28,7 @@ pub const user32 = @import("windows/user32.zig");
|
||||
pub const ws2_32 = @import("windows/ws2_32.zig");
|
||||
pub const gdi32 = @import("windows/gdi32.zig");
|
||||
pub const winmm = @import("windows/winmm.zig");
|
||||
pub const crypt32 = @import("windows/crypt32.zig");
|
||||
|
||||
pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
|
||||
|
||||
@ -1295,6 +1296,23 @@ pub fn WSACleanup() !void {
|
||||
|
||||
var wsa_startup_mutex: std.Thread.Mutex = .{};
|
||||
|
||||
pub fn callWSAStartup() !void {
|
||||
wsa_startup_mutex.lock();
|
||||
defer wsa_startup_mutex.unlock();
|
||||
|
||||
// Here we could use a flag to prevent multiple threads to prevent
|
||||
// multiple calls to WSAStartup, but it doesn't matter. We're globally
|
||||
// leaking the resource intentionally, and the mutex already prevents
|
||||
// data races within the WSAStartup function.
|
||||
_ = WSAStartup(2, 2) catch |err| switch (err) {
|
||||
error.SystemNotAvailable => return error.SystemResources,
|
||||
error.VersionNotSupported => return error.Unexpected,
|
||||
error.BlockingOperationInProgress => return error.Unexpected,
|
||||
error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
|
||||
error.Unexpected => return error.Unexpected,
|
||||
};
|
||||
}
|
||||
|
||||
/// Microsoft requires WSAStartup to be called to initialize, or else
|
||||
/// WSASocketW will return WSANOTINITIALISED.
|
||||
/// Since this is a standard library, we do not have the luxury of
|
||||
@ -1337,21 +1355,7 @@ pub fn WSASocketW(
|
||||
.WSANOTINITIALISED => {
|
||||
if (!first) return error.Unexpected;
|
||||
first = false;
|
||||
|
||||
wsa_startup_mutex.lock();
|
||||
defer wsa_startup_mutex.unlock();
|
||||
|
||||
// Here we could use a flag to prevent multiple threads to prevent
|
||||
// multiple calls to WSAStartup, but it doesn't matter. We're globally
|
||||
// leaking the resource intentionally, and the mutex already prevents
|
||||
// data races within the WSAStartup function.
|
||||
_ = WSAStartup(2, 2) catch |err| switch (err) {
|
||||
error.SystemNotAvailable => return error.SystemResources,
|
||||
error.VersionNotSupported => return error.Unexpected,
|
||||
error.BlockingOperationInProgress => return error.Unexpected,
|
||||
error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
|
||||
error.Unexpected => return error.Unexpected,
|
||||
};
|
||||
try callWSAStartup();
|
||||
continue;
|
||||
},
|
||||
else => |err| return unexpectedWSAError(err),
|
||||
|
||||
32
lib/std/os/windows/crypt32.zig
Normal file
32
lib/std/os/windows/crypt32.zig
Normal file
@ -0,0 +1,32 @@
|
||||
const std = @import("../../std.zig");
|
||||
const windows = std.os.windows;
|
||||
const BOOL = windows.BOOL;
|
||||
const DWORD = windows.DWORD;
|
||||
const BYTE = windows.BYTE;
|
||||
const LPCWSTR = windows.LPCWSTR;
|
||||
const WINAPI = windows.WINAPI;
|
||||
|
||||
pub const CERT_INFO = *opaque {};
|
||||
pub const HCERTSTORE = *opaque {};
|
||||
pub const CERT_CONTEXT = extern struct {
|
||||
dwCertEncodingType: DWORD,
|
||||
pbCertEncoded: [*]BYTE,
|
||||
cbCertEncoded: DWORD,
|
||||
pCertInfo: CERT_INFO,
|
||||
hCertStore: HCERTSTORE,
|
||||
};
|
||||
|
||||
pub extern "crypt32" fn CertOpenSystemStoreW(
|
||||
_: ?*const anyopaque,
|
||||
szSubsystemProtocol: LPCWSTR,
|
||||
) callconv(WINAPI) ?HCERTSTORE;
|
||||
|
||||
pub extern "crypt32" fn CertCloseStore(
|
||||
hCertStore: HCERTSTORE,
|
||||
dwFlags: DWORD,
|
||||
) callconv(WINAPI) BOOL;
|
||||
|
||||
pub extern "crypt32" fn CertEnumCertificatesInStore(
|
||||
hCertStore: HCERTSTORE,
|
||||
pPrevCertContext: ?*CERT_CONTEXT,
|
||||
) callconv(WINAPI) ?*CERT_CONTEXT;
|
||||
Loading…
x
Reference in New Issue
Block a user