milestone 19: hygiene sweep - dead ecs surface, single-source constants, tls classification, frontend state hazards, docker smoke network fix
This commit is contained in:
@@ -318,12 +318,15 @@ fn load(
|
||||
};
|
||||
defer gpa.free(cert_pem);
|
||||
|
||||
const key_pem = readPem(gpa, io, key_path) catch |err| return switch (err) {
|
||||
const key_pem = readKeyPem(gpa, io, key_path) catch |err| return switch (err) {
|
||||
error.OutOfMemory => error.OutOfMemory,
|
||||
error.TooLarge => error.KeyTooLarge,
|
||||
error.Unreadable => error.KeyUnreadable,
|
||||
};
|
||||
defer gpa.free(key_pem);
|
||||
defer {
|
||||
std.crypto.secureZero(u8, key_pem);
|
||||
gpa.free(key_pem);
|
||||
}
|
||||
|
||||
const entry = try gpa.create(Entry);
|
||||
errdefer gpa.destroy(entry);
|
||||
@@ -342,6 +345,9 @@ fn load(
|
||||
/// Mbed TLS wants PEM with a terminating zero byte counted in the length, so
|
||||
/// the file lands in a sentinel-terminated allocation. The limit admits
|
||||
/// exactly `max_pem_bytes` and rejects the first byte beyond it.
|
||||
///
|
||||
/// The certificate only. A private key goes through `readKeyPem`, which does
|
||||
/// not leave copies behind.
|
||||
fn readPem(
|
||||
gpa: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
@@ -361,6 +367,36 @@ fn readPem(
|
||||
};
|
||||
}
|
||||
|
||||
/// `readPem` for the private key, with the same cap and the same
|
||||
/// sentinel-terminated result. The difference is that no copy of the key
|
||||
/// survives this function: `readFileAllocOptions` grows its buffer as it reads,
|
||||
/// and every intermediate copy it abandons stays legible in freed pages that no
|
||||
/// wipe at the call site can reach. One fixed staging buffer never grows, and it
|
||||
/// is wiped before it goes back to the allocator. The caller wipes the returned
|
||||
/// slice the same way before freeing it (the idiom is auth.zig's `secureZero`
|
||||
/// defers).
|
||||
fn readKeyPem(
|
||||
gpa: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
path: []const u8,
|
||||
) error{ OutOfMemory, TooLarge, Unreadable }![:0]u8 {
|
||||
const staging = try gpa.alloc(u8, max_pem_bytes + 1);
|
||||
defer {
|
||||
std.crypto.secureZero(u8, staging);
|
||||
gpa.free(staging);
|
||||
}
|
||||
|
||||
const bytes = std.Io.Dir.cwd().readFile(io, path, staging) catch return error.Unreadable;
|
||||
// A read that filled the staging buffer is ambiguous — `readFile` cannot
|
||||
// say whether more followed — and one byte past `max_pem_bytes` is over the
|
||||
// cap either way.
|
||||
if (bytes.len == staging.len) return error.TooLarge;
|
||||
|
||||
const key = try gpa.allocSentinel(u8, bytes.len, 0);
|
||||
@memcpy(key, bytes);
|
||||
return key;
|
||||
}
|
||||
|
||||
fn statSig(io: std.Io, path: []const u8) !FileSig {
|
||||
const st = try std.Io.Dir.cwd().statFile(io, path, .{});
|
||||
return .{ .mtime_ns = st.mtime.nanoseconds, .size = st.size };
|
||||
@@ -481,6 +517,35 @@ test "the size cap admits 64 KiB and rejects one byte more" {
|
||||
try testing.expectError(error.TooLarge, readPem(testing.allocator, io, env.cert_path));
|
||||
}
|
||||
|
||||
test "the key PEM path keeps the shape and the cap of the certificate path" {
|
||||
var env: TestEnv = undefined;
|
||||
try env.init();
|
||||
defer env.deinit();
|
||||
const io = env.io();
|
||||
|
||||
const key = try readKeyPem(testing.allocator, io, env.key_path);
|
||||
defer testing.allocator.free(key);
|
||||
try testing.expectEqualStrings(fixtures.key_pem, key);
|
||||
try testing.expectEqual(@as(u8, 0), key[key.len]);
|
||||
|
||||
const at_cap = try testing.allocator.alloc(u8, max_pem_bytes);
|
||||
defer testing.allocator.free(at_cap);
|
||||
@memset(at_cap, 'a');
|
||||
try env.tmp.dir.writeFile(io, .{ .sub_path = "key.pem", .data = at_cap });
|
||||
testing.allocator.free(try readKeyPem(testing.allocator, io, env.key_path));
|
||||
|
||||
const over = try testing.allocator.alloc(u8, max_pem_bytes + 1);
|
||||
defer testing.allocator.free(over);
|
||||
@memset(over, 'a');
|
||||
try env.tmp.dir.writeFile(io, .{ .sub_path = "key.pem", .data = over });
|
||||
try testing.expectError(error.TooLarge, readKeyPem(testing.allocator, io, env.key_path));
|
||||
|
||||
try testing.expectError(
|
||||
error.Unreadable,
|
||||
readKeyPem(testing.allocator, io, "./nxdns-no-such-key-9b31.pem"),
|
||||
);
|
||||
}
|
||||
|
||||
test "init fails typed on a missing file and on garbage PEM" {
|
||||
var env: TestEnv = undefined;
|
||||
try env.init();
|
||||
|
||||
Reference in New Issue
Block a user