ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.44k stars 2.52k forks source link

Add FreeBSD default certificate directory to lib/std/crypto/Certificate/Bundle.zig:rescanBSD #20516

Open bbailey1024 opened 3 months ago

bbailey1024 commented 3 months ago

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

As of FreeBSD 14.1, there is no /etc/ssl/cert.pem certificate bundle file by default. This file is typically present after an administrator installs the security/ca_root_nss package.

Without this package, there is no /etc/ssl/certs.pem file, and rescanBSD returns an error.

const std = @import("std");

pub fn main() !void {
    var http = std.http.Client{ .allocator = std.heap.page_allocator };
    defer http.deinit();

    std.debug.print("{any}\n", .{http.fetch(.{ .location = .{ .url = "https://github.com" } })});
}
$ zig build run
error.CertificateBundleLoadFailure

truss snippet

openat(AT_FDCWD,"/etc/ssl/cert.pem",O_RDONLY|O_NOCTTY|O_CLOEXEC,00) ERR#2 'No such file or directory'
error.write(2,"error.",6)                                = 6 (0x6)
CertificateBundleLoadFailurewrite(2,"CertificateBundleLoadFailure",28)   = 28 (0x1c)

write(2,"\n",1)                                  = 1 (0x1)
exit(0x0)
process exit, rval = 0

Expected Behavior

Zig should include appropriate FreeBSD certificate locations in its rescanBSD function. Specifically, that function should include the following directory.

/etc/ssl/certs

This could likely be achieved by mirroring the functionality in the rescanLinux function that includes scanning certificate directories as well as certificate bundle files.

bbailey1024 commented 3 months ago

Here's a diff of what could be done to adjust this. I didn't think a PR was appropriate considering these changes make the rescanLinux and rescanBSD functions effectively identical. There may also be opinions on what files/directories should or should not be included, and in what order they appear. At any rate, these files and directories should encompass the common locations for certificates in BSD derivatives. This change resolved my issue.

--- Bundle.old.zig      2024-07-06 13:52:27.333861000 -0500
+++ Bundle.zig  2024-07-06 14:11:56.002195000 -0500
@@ -60,10 +60,7 @@
     switch (builtin.os.tag) {
         .linux => return rescanLinux(cb, gpa),
         .macos => return rescanMac(cb, gpa),
-        .freebsd, .openbsd => return rescanBSD(cb, gpa, "/etc/ssl/cert.pem"),
-        .netbsd => return rescanBSD(cb, gpa, "/etc/openssl/certs/ca-certificates.crt"),
-        .dragonfly => return rescanBSD(cb, gpa, "/usr/local/etc/ssl/cert.pem"),
-        .solaris, .illumos => return rescanBSD(cb, gpa, "/etc/ssl/cacert.pem"),
+        .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => return rescanBSD(cb, gpa),
         .windows => return rescanWindows(cb, gpa),
         else => {},
     }
@@ -116,12 +113,46 @@
     cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
 }

-const RescanBSDError = AddCertsFromFilePathError;
+const RescanBSDError = AddCertsFromFilePathError || AddCertsFromDirPathError;

-fn rescanBSD(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanBSDError!void {
+fn rescanBSD(cb: *Bundle, gpa: Allocator) RescanBSDError!void {
+    // Possible certificate files; stop after finding one.
+    const cert_file_paths = [_][]const u8{
+        "/etc/ssl/cert.pem",
+        "/etc/openssl/certs/ca-certificates.crt",
+        "/usr/local/etc/ssl/cert.pem",
+        "/usr/local/share/certs/ca-root-nss.crt",
+    };
+
+    // Possible directories with certificate files; all will be read.
+    const cert_dir_paths = [_][]const u8{
+        "/etc/ssl/certs",
+        "/etc/openssl/certs",
+        "/usr/local/etc/ssl/certs",
+        "/usr/local/share/certs",
+    };
+
     cb.bytes.clearRetainingCapacity();
     cb.map.clearRetainingCapacity();
-    try addCertsFromFilePathAbsolute(cb, gpa, cert_file_path);
+
+    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);
 }
cryptocode commented 3 months ago

As a workaround, the following installs the root certificate bundle from the Mozilla Project:

pkg install ca_root_nss (under sudo if necessary)

With this addition, I've been able to build several Zig projects with dependencies.