mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.crypto.Certificate.Bundle: remove use of File.readAll
This commit is contained in:
parent
fcac8617b4
commit
63801c4b05
2 changed files with 60 additions and 47 deletions
|
|
@ -4,6 +4,20 @@
|
||||||
//! concatenated together in the `bytes` array. The `map` field contains an
|
//! concatenated together in the `bytes` array. The `map` field contains an
|
||||||
//! index from the DER-encoded subject name to the index of the containing
|
//! index from the DER-encoded subject name to the index of the containing
|
||||||
//! certificate within `bytes`.
|
//! certificate within `bytes`.
|
||||||
|
const Bundle = @This();
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const std = @import("../../std.zig");
|
||||||
|
const Io = std.Io;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const fs = std.fs;
|
||||||
|
const mem = std.mem;
|
||||||
|
const crypto = std.crypto;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Certificate = std.crypto.Certificate;
|
||||||
|
const der = Certificate.der;
|
||||||
|
|
||||||
|
const base64 = std.base64.standard.decoderWithIgnore(" \t\r\n");
|
||||||
|
|
||||||
/// The key is the contents slice of the subject.
|
/// The key is the contents slice of the subject.
|
||||||
map: std.HashMapUnmanaged(der.Element.Slice, u32, MapContext, std.hash_map.default_max_load_percentage) = .empty,
|
map: std.HashMapUnmanaged(der.Element.Slice, u32, MapContext, std.hash_map.default_max_load_percentage) = .empty,
|
||||||
|
|
@ -56,17 +70,17 @@ pub const RescanError = RescanLinuxError || RescanMacError || RescanWithPathErro
|
||||||
/// file system standard locations for certificates.
|
/// file system standard locations for certificates.
|
||||||
/// For operating systems that do not have standard CA installations to be
|
/// For operating systems that do not have standard CA installations to be
|
||||||
/// found, this function clears the set of certificates.
|
/// found, this function clears the set of certificates.
|
||||||
pub fn rescan(cb: *Bundle, gpa: Allocator) RescanError!void {
|
pub fn rescan(cb: *Bundle, gpa: Allocator, io: Io) RescanError!void {
|
||||||
switch (builtin.os.tag) {
|
switch (builtin.os.tag) {
|
||||||
.linux => return rescanLinux(cb, gpa),
|
.linux => return rescanLinux(cb, gpa, io),
|
||||||
.macos => return rescanMac(cb, gpa),
|
.macos => return rescanMac(cb, gpa),
|
||||||
.freebsd, .openbsd => return rescanWithPath(cb, gpa, "/etc/ssl/cert.pem"),
|
.freebsd, .openbsd => return rescanWithPath(cb, gpa, io, "/etc/ssl/cert.pem"),
|
||||||
.netbsd => return rescanWithPath(cb, gpa, "/etc/openssl/certs/ca-certificates.crt"),
|
.netbsd => return rescanWithPath(cb, gpa, io, "/etc/openssl/certs/ca-certificates.crt"),
|
||||||
.dragonfly => return rescanWithPath(cb, gpa, "/usr/local/etc/ssl/cert.pem"),
|
.dragonfly => return rescanWithPath(cb, gpa, io, "/usr/local/etc/ssl/cert.pem"),
|
||||||
.illumos => return rescanWithPath(cb, gpa, "/etc/ssl/cacert.pem"),
|
.illumos => return rescanWithPath(cb, gpa, io, "/etc/ssl/cacert.pem"),
|
||||||
.haiku => return rescanWithPath(cb, gpa, "/boot/system/data/ssl/CARootCertificates.pem"),
|
.haiku => return rescanWithPath(cb, gpa, io, "/boot/system/data/ssl/CARootCertificates.pem"),
|
||||||
// https://github.com/SerenityOS/serenity/blob/222acc9d389bc6b490d4c39539761b043a4bfcb0/Ports/ca-certificates/package.sh#L19
|
// https://github.com/SerenityOS/serenity/blob/222acc9d389bc6b490d4c39539761b043a4bfcb0/Ports/ca-certificates/package.sh#L19
|
||||||
.serenity => return rescanWithPath(cb, gpa, "/etc/ssl/certs/ca-certificates.crt"),
|
.serenity => return rescanWithPath(cb, gpa, io, "/etc/ssl/certs/ca-certificates.crt"),
|
||||||
.windows => return rescanWindows(cb, gpa),
|
.windows => return rescanWindows(cb, gpa),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +91,7 @@ const RescanMacError = @import("Bundle/macos.zig").RescanMacError;
|
||||||
|
|
||||||
const RescanLinuxError = AddCertsFromFilePathError || AddCertsFromDirPathError;
|
const RescanLinuxError = AddCertsFromFilePathError || AddCertsFromDirPathError;
|
||||||
|
|
||||||
fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
|
fn rescanLinux(cb: *Bundle, gpa: Allocator, io: Io) RescanLinuxError!void {
|
||||||
// Possible certificate files; stop after finding one.
|
// Possible certificate files; stop after finding one.
|
||||||
const cert_file_paths = [_][]const u8{
|
const cert_file_paths = [_][]const u8{
|
||||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||||
|
|
@ -100,7 +114,7 @@ fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
|
||||||
|
|
||||||
scan: {
|
scan: {
|
||||||
for (cert_file_paths) |cert_file_path| {
|
for (cert_file_paths) |cert_file_path| {
|
||||||
if (addCertsFromFilePathAbsolute(cb, gpa, cert_file_path)) |_| {
|
if (addCertsFromFilePathAbsolute(cb, gpa, io, cert_file_path)) |_| {
|
||||||
break :scan;
|
break :scan;
|
||||||
} else |err| switch (err) {
|
} else |err| switch (err) {
|
||||||
error.FileNotFound => continue,
|
error.FileNotFound => continue,
|
||||||
|
|
@ -109,7 +123,7 @@ fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (cert_dir_paths) |cert_dir_path| {
|
for (cert_dir_paths) |cert_dir_path| {
|
||||||
addCertsFromDirPathAbsolute(cb, gpa, cert_dir_path) catch |err| switch (err) {
|
addCertsFromDirPathAbsolute(cb, gpa, io, cert_dir_path) catch |err| switch (err) {
|
||||||
error.FileNotFound => continue,
|
error.FileNotFound => continue,
|
||||||
else => |e| return e,
|
else => |e| return e,
|
||||||
};
|
};
|
||||||
|
|
@ -121,10 +135,10 @@ fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
|
||||||
|
|
||||||
const RescanWithPathError = AddCertsFromFilePathError;
|
const RescanWithPathError = AddCertsFromFilePathError;
|
||||||
|
|
||||||
fn rescanWithPath(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanWithPathError!void {
|
fn rescanWithPath(cb: *Bundle, gpa: Allocator, io: Io, cert_file_path: []const u8) RescanWithPathError!void {
|
||||||
cb.bytes.clearRetainingCapacity();
|
cb.bytes.clearRetainingCapacity();
|
||||||
cb.map.clearRetainingCapacity();
|
cb.map.clearRetainingCapacity();
|
||||||
try addCertsFromFilePathAbsolute(cb, gpa, cert_file_path);
|
try addCertsFromFilePathAbsolute(cb, gpa, io, cert_file_path);
|
||||||
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,28 +174,30 @@ pub const AddCertsFromDirPathError = fs.File.OpenError || AddCertsFromDirError;
|
||||||
pub fn addCertsFromDirPath(
|
pub fn addCertsFromDirPath(
|
||||||
cb: *Bundle,
|
cb: *Bundle,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
|
io: Io,
|
||||||
dir: fs.Dir,
|
dir: fs.Dir,
|
||||||
sub_dir_path: []const u8,
|
sub_dir_path: []const u8,
|
||||||
) AddCertsFromDirPathError!void {
|
) AddCertsFromDirPathError!void {
|
||||||
var iterable_dir = try dir.openDir(sub_dir_path, .{ .iterate = true });
|
var iterable_dir = try dir.openDir(sub_dir_path, .{ .iterate = true });
|
||||||
defer iterable_dir.close();
|
defer iterable_dir.close();
|
||||||
return addCertsFromDir(cb, gpa, iterable_dir);
|
return addCertsFromDir(cb, gpa, io, iterable_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addCertsFromDirPathAbsolute(
|
pub fn addCertsFromDirPathAbsolute(
|
||||||
cb: *Bundle,
|
cb: *Bundle,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
|
io: Io,
|
||||||
abs_dir_path: []const u8,
|
abs_dir_path: []const u8,
|
||||||
) AddCertsFromDirPathError!void {
|
) AddCertsFromDirPathError!void {
|
||||||
assert(fs.path.isAbsolute(abs_dir_path));
|
assert(fs.path.isAbsolute(abs_dir_path));
|
||||||
var iterable_dir = try fs.openDirAbsolute(abs_dir_path, .{ .iterate = true });
|
var iterable_dir = try fs.openDirAbsolute(abs_dir_path, .{ .iterate = true });
|
||||||
defer iterable_dir.close();
|
defer iterable_dir.close();
|
||||||
return addCertsFromDir(cb, gpa, iterable_dir);
|
return addCertsFromDir(cb, gpa, io, iterable_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AddCertsFromDirError = AddCertsFromFilePathError;
|
pub const AddCertsFromDirError = AddCertsFromFilePathError;
|
||||||
|
|
||||||
pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.Dir) AddCertsFromDirError!void {
|
pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, io: Io, iterable_dir: fs.Dir) AddCertsFromDirError!void {
|
||||||
var it = iterable_dir.iterate();
|
var it = iterable_dir.iterate();
|
||||||
while (try it.next()) |entry| {
|
while (try it.next()) |entry| {
|
||||||
switch (entry.kind) {
|
switch (entry.kind) {
|
||||||
|
|
@ -189,32 +205,37 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.Dir) AddCer
|
||||||
else => continue,
|
else => continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
try addCertsFromFilePath(cb, gpa, iterable_dir, entry.name);
|
try addCertsFromFilePath(cb, gpa, io, iterable_dir.adaptToNewApi(), entry.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError;
|
pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError || Io.Clock.Error;
|
||||||
|
|
||||||
pub fn addCertsFromFilePathAbsolute(
|
pub fn addCertsFromFilePathAbsolute(
|
||||||
cb: *Bundle,
|
cb: *Bundle,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
|
io: Io,
|
||||||
abs_file_path: []const u8,
|
abs_file_path: []const u8,
|
||||||
) AddCertsFromFilePathError!void {
|
) AddCertsFromFilePathError!void {
|
||||||
assert(fs.path.isAbsolute(abs_file_path));
|
const now = try Io.Clock.real.now(io);
|
||||||
var file = try fs.openFileAbsolute(abs_file_path, .{});
|
var file = try fs.openFileAbsolute(abs_file_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
return addCertsFromFile(cb, gpa, file);
|
var file_reader = file.reader(io, &.{});
|
||||||
|
return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addCertsFromFilePath(
|
pub fn addCertsFromFilePath(
|
||||||
cb: *Bundle,
|
cb: *Bundle,
|
||||||
gpa: Allocator,
|
gpa: Allocator,
|
||||||
dir: fs.Dir,
|
io: Io,
|
||||||
|
dir: Io.Dir,
|
||||||
sub_file_path: []const u8,
|
sub_file_path: []const u8,
|
||||||
) AddCertsFromFilePathError!void {
|
) AddCertsFromFilePathError!void {
|
||||||
var file = try dir.openFile(sub_file_path, .{});
|
const now = try Io.Clock.real.now(io);
|
||||||
defer file.close();
|
var file = try dir.openFile(io, sub_file_path, .{});
|
||||||
return addCertsFromFile(cb, gpa, file);
|
defer file.close(io);
|
||||||
|
var file_reader = file.reader(io, &.{});
|
||||||
|
return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AddCertsFromFileError = Allocator.Error ||
|
pub const AddCertsFromFileError = Allocator.Error ||
|
||||||
|
|
@ -222,10 +243,10 @@ pub const AddCertsFromFileError = Allocator.Error ||
|
||||||
fs.File.ReadError ||
|
fs.File.ReadError ||
|
||||||
ParseCertError ||
|
ParseCertError ||
|
||||||
std.base64.Error ||
|
std.base64.Error ||
|
||||||
error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
|
error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker, Streaming };
|
||||||
|
|
||||||
pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {
|
pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file_reader: *Io.File.Reader, now_sec: i64) AddCertsFromFileError!void {
|
||||||
const size = try file.getEndPos();
|
const size = try file_reader.getSize();
|
||||||
|
|
||||||
// We borrow `bytes` as a temporary buffer for the base64-encoded data.
|
// We borrow `bytes` as a temporary buffer for the base64-encoded data.
|
||||||
// This is possible by computing the decoded length and reserving the space
|
// This is possible by computing the decoded length and reserving the space
|
||||||
|
|
@ -236,14 +257,14 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
|
||||||
try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
|
try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
|
||||||
const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
|
const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
|
||||||
const buffer = cb.bytes.allocatedSlice()[end_reserved..];
|
const buffer = cb.bytes.allocatedSlice()[end_reserved..];
|
||||||
const end_index = try file.readAll(buffer);
|
const end_index = file_reader.interface.readSliceShort(buffer) catch |err| switch (err) {
|
||||||
|
error.ReadFailed => return file_reader.err.?,
|
||||||
|
};
|
||||||
const encoded_bytes = buffer[0..end_index];
|
const encoded_bytes = buffer[0..end_index];
|
||||||
|
|
||||||
const begin_marker = "-----BEGIN CERTIFICATE-----";
|
const begin_marker = "-----BEGIN CERTIFICATE-----";
|
||||||
const end_marker = "-----END CERTIFICATE-----";
|
const end_marker = "-----END CERTIFICATE-----";
|
||||||
|
|
||||||
const now_sec = std.time.timestamp();
|
|
||||||
|
|
||||||
var start_index: usize = 0;
|
var start_index: usize = 0;
|
||||||
while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {
|
while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {
|
||||||
const cert_start = begin_marker_start + begin_marker.len;
|
const cert_start = begin_marker_start + begin_marker.len;
|
||||||
|
|
@ -288,19 +309,6 @@ pub fn parseCert(cb: *Bundle, gpa: Allocator, decoded_start: u32, now_sec: i64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Certificate = std.crypto.Certificate;
|
|
||||||
const der = Certificate.der;
|
|
||||||
const Bundle = @This();
|
|
||||||
|
|
||||||
const base64 = std.base64.standard.decoderWithIgnore(" \t\r\n");
|
|
||||||
|
|
||||||
const MapContext = struct {
|
const MapContext = struct {
|
||||||
cb: *const Bundle,
|
cb: *const Bundle,
|
||||||
|
|
||||||
|
|
@ -321,8 +329,11 @@ const MapContext = struct {
|
||||||
test "scan for OS-provided certificates" {
|
test "scan for OS-provided certificates" {
|
||||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||||
|
|
||||||
var bundle: Bundle = .{};
|
const io = std.testing.io;
|
||||||
defer bundle.deinit(std.testing.allocator);
|
const gpa = std.testing.allocator;
|
||||||
|
|
||||||
try bundle.rescan(std.testing.allocator);
|
var bundle: Bundle = .{};
|
||||||
|
defer bundle.deinit(gpa);
|
||||||
|
|
||||||
|
try bundle.rescan(gpa, io);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1666,6 +1666,8 @@ pub fn request(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
options: RequestOptions,
|
options: RequestOptions,
|
||||||
) RequestError!Request {
|
) RequestError!Request {
|
||||||
|
const io = client.io;
|
||||||
|
|
||||||
if (std.debug.runtime_safety) {
|
if (std.debug.runtime_safety) {
|
||||||
for (options.extra_headers) |header| {
|
for (options.extra_headers) |header| {
|
||||||
assert(header.name.len != 0);
|
assert(header.name.len != 0);
|
||||||
|
|
@ -1689,7 +1691,7 @@ pub fn request(
|
||||||
defer client.ca_bundle_mutex.unlock();
|
defer client.ca_bundle_mutex.unlock();
|
||||||
|
|
||||||
if (client.next_https_rescan_certs) {
|
if (client.next_https_rescan_certs) {
|
||||||
client.ca_bundle.rescan(client.allocator) catch
|
client.ca_bundle.rescan(client.allocator, io) catch
|
||||||
return error.CertificateBundleLoadFailure;
|
return error.CertificateBundleLoadFailure;
|
||||||
@atomicStore(bool, &client.next_https_rescan_certs, false, .release);
|
@atomicStore(bool, &client.next_https_rescan_certs, false, .release);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue