mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #14329 from ziglang/v1-ssl-certificates
support v1/v2 SSL certificates and search more Linux directories for certificates
This commit is contained in:
commit
d389dba04f
@ -3,6 +3,8 @@ index: u32,
|
||||
|
||||
pub const Bundle = @import("Certificate/Bundle.zig");
|
||||
|
||||
pub const Version = enum { v1, v2, v3 };
|
||||
|
||||
pub const Algorithm = enum {
|
||||
sha1WithRSAEncryption,
|
||||
sha224WithRSAEncryption,
|
||||
@ -130,6 +132,7 @@ pub const Parsed = struct {
|
||||
message_slice: Slice,
|
||||
subject_alt_name_slice: Slice,
|
||||
validity: Validity,
|
||||
version: Version,
|
||||
|
||||
pub const PubKeyAlgo = union(AlgorithmCategory) {
|
||||
rsaEncryption: void,
|
||||
@ -299,9 +302,12 @@ pub fn parse(cert: Certificate) !Parsed {
|
||||
const cert_bytes = cert.buffer;
|
||||
const certificate = try der.Element.parse(cert_bytes, cert.index);
|
||||
const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);
|
||||
const version = try der.Element.parse(cert_bytes, tbs_certificate.slice.start);
|
||||
try checkVersion(cert_bytes, version);
|
||||
const serial_number = try der.Element.parse(cert_bytes, version.slice.end);
|
||||
const version_elem = try der.Element.parse(cert_bytes, tbs_certificate.slice.start);
|
||||
const version = try parseVersion(cert_bytes, version_elem);
|
||||
const serial_number = if (@bitCast(u8, version_elem.identifier) == 0xa0)
|
||||
try der.Element.parse(cert_bytes, version_elem.slice.end)
|
||||
else
|
||||
version_elem;
|
||||
// RFC 5280, section 4.1.2.3:
|
||||
// "This field MUST contain the same algorithm identifier as
|
||||
// the signatureAlgorithm field in the sequence Certificate."
|
||||
@ -370,6 +376,9 @@ pub fn parse(cert: Certificate) !Parsed {
|
||||
// Extensions
|
||||
var subject_alt_name_slice = der.Element.Slice.empty;
|
||||
ext: {
|
||||
if (version == .v1)
|
||||
break :ext;
|
||||
|
||||
if (pub_key_info.slice.end >= tbs_certificate.slice.end)
|
||||
break :ext;
|
||||
|
||||
@ -415,6 +424,7 @@ pub fn parse(cert: Certificate) !Parsed {
|
||||
.not_after = not_after_utc,
|
||||
},
|
||||
.subject_alt_name_slice = subject_alt_name_slice,
|
||||
.version = version,
|
||||
};
|
||||
}
|
||||
|
||||
@ -588,12 +598,24 @@ fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) !E {
|
||||
return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
|
||||
}
|
||||
|
||||
pub fn checkVersion(bytes: []const u8, version: der.Element) !void {
|
||||
if (@bitCast(u8, version.identifier) != 0xa0 or
|
||||
!mem.eql(u8, bytes[version.slice.start..version.slice.end], "\x02\x01\x02"))
|
||||
{
|
||||
return error.UnsupportedCertificateVersion;
|
||||
pub fn parseVersion(bytes: []const u8, version_elem: der.Element) !Version {
|
||||
if (@bitCast(u8, version_elem.identifier) != 0xa0)
|
||||
return .v1;
|
||||
|
||||
if (version_elem.slice.end - version_elem.slice.start != 3)
|
||||
return error.CertificateFieldHasInvalidLength;
|
||||
|
||||
const encoded_version = bytes[version_elem.slice.start..version_elem.slice.end];
|
||||
|
||||
if (mem.eql(u8, encoded_version, "\x02\x01\x02")) {
|
||||
return .v3;
|
||||
} else if (mem.eql(u8, encoded_version, "\x02\x01\x01")) {
|
||||
return .v2;
|
||||
} else if (mem.eql(u8, encoded_version, "\x02\x01\x00")) {
|
||||
return .v1;
|
||||
}
|
||||
|
||||
return error.UnsupportedCertificateVersion;
|
||||
}
|
||||
|
||||
fn verifyRsa(
|
||||
|
||||
@ -68,29 +68,93 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
|
||||
}
|
||||
|
||||
pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
|
||||
var dir = fs.openIterableDirAbsolute("/etc/ssl/certs", .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return,
|
||||
else => |e| return e,
|
||||
// Possible certificate files; stop after finding one.
|
||||
const cert_file_paths = [_][]const u8{
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||
"/etc/ssl/cert.pem", // Alpine Linux
|
||||
};
|
||||
|
||||
// Possible directories with certificate files; all will be read.
|
||||
const cert_dir_paths = [_][]const u8{
|
||||
"/etc/ssl/certs", // SLES10/SLES11
|
||||
"/etc/pki/tls/certs", // Fedora/RHEL
|
||||
"/system/etc/security/cacerts", // Android
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
cb.bytes.clearRetainingCapacity();
|
||||
cb.map.clearRetainingCapacity();
|
||||
|
||||
var it = dir.iterate();
|
||||
scan: {
|
||||
for (cert_file_paths) |cert_file_path| {
|
||||
if (addCertsFromFilePathAbsolute(cb, gpa, cert_file_path)) |_| {
|
||||
break :scan;
|
||||
} else |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
}
|
||||
}
|
||||
|
||||
for (cert_dir_paths) |cert_dir_path| {
|
||||
addCertsFromDirPathAbsolute(cb, gpa, cert_dir_path) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
||||
}
|
||||
|
||||
pub fn addCertsFromDirPath(
|
||||
cb: *Bundle,
|
||||
gpa: Allocator,
|
||||
dir: fs.Dir,
|
||||
sub_dir_path: []const u8,
|
||||
) !void {
|
||||
var iterable_dir = try dir.openIterableDir(sub_dir_path, .{});
|
||||
defer iterable_dir.close();
|
||||
return addCertsFromDir(cb, gpa, iterable_dir);
|
||||
}
|
||||
|
||||
pub fn addCertsFromDirPathAbsolute(
|
||||
cb: *Bundle,
|
||||
gpa: Allocator,
|
||||
abs_dir_path: []const u8,
|
||||
) !void {
|
||||
assert(fs.path.isAbsolute(abs_dir_path));
|
||||
var iterable_dir = try fs.openIterableDirAbsolute(abs_dir_path, .{});
|
||||
defer iterable_dir.close();
|
||||
return addCertsFromDir(cb, gpa, iterable_dir);
|
||||
}
|
||||
|
||||
pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir) !void {
|
||||
var it = iterable_dir.iterate();
|
||||
while (try it.next()) |entry| {
|
||||
switch (entry.kind) {
|
||||
.File, .SymLink => {},
|
||||
else => continue,
|
||||
}
|
||||
|
||||
try addCertsFromFile(cb, gpa, dir.dir, entry.name);
|
||||
try addCertsFromFilePath(cb, gpa, iterable_dir.dir, entry.name);
|
||||
}
|
||||
|
||||
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
||||
}
|
||||
|
||||
pub fn addCertsFromFile(
|
||||
pub fn addCertsFromFilePathAbsolute(
|
||||
cb: *Bundle,
|
||||
gpa: Allocator,
|
||||
abs_file_path: []const u8,
|
||||
) !void {
|
||||
assert(fs.path.isAbsolute(abs_file_path));
|
||||
var file = try fs.openFileAbsolute(abs_file_path, .{});
|
||||
defer file.close();
|
||||
return addCertsFromFile(cb, gpa, file);
|
||||
}
|
||||
|
||||
pub fn addCertsFromFilePath(
|
||||
cb: *Bundle,
|
||||
gpa: Allocator,
|
||||
dir: fs.Dir,
|
||||
@ -98,7 +162,10 @@ pub fn addCertsFromFile(
|
||||
) !void {
|
||||
var file = try dir.openFile(sub_file_path, .{});
|
||||
defer file.close();
|
||||
return addCertsFromFile(cb, gpa, file);
|
||||
}
|
||||
|
||||
pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {
|
||||
const size = try file.getEndPos();
|
||||
|
||||
// We borrow `bytes` as a temporary buffer for the base64-encoded data.
|
||||
@ -152,6 +219,7 @@ pub fn addCertsFromFile(
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("../../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const crypto = std.crypto;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user