about summary refs log tree commit diff
path: root/pkgs/data/misc
diff options
context:
space:
mode:
authorLuke Granger-Brown <git@lukegb.com>2021-06-12 19:14:37 +0000
committerLuke Granger-Brown <git@lukegb.com>2021-10-08 00:56:49 +0000
commit906f44cef305d59b743758bfb30678433130a5de (patch)
tree7bdb26bec94aab4e945197e5115aef618a438890 /pkgs/data/misc
parent44c4fd09a16757808036152f3ae505e78b626e4b (diff)
cacert: port to use buildcatrust
This introduces the ability to have additional certificates in the trust
store using an override, similar to how the blacklist is done. If the
certificates are provided in OpenSSL TRUSTED CERTIFICATE form, then
those trust bits will be respected.

It also adds a p11-kit compatible trust store output.
Diffstat (limited to 'pkgs/data/misc')
-rw-r--r--pkgs/data/misc/cacert/default.nix215
-rw-r--r--pkgs/data/misc/cacert/test-cert-file.crt13
2 files changed, 148 insertions, 80 deletions
diff --git a/pkgs/data/misc/cacert/default.nix b/pkgs/data/misc/cacert/default.nix
index 404fba91d9d8f..da781f310f4c1 100644
--- a/pkgs/data/misc/cacert/default.nix
+++ b/pkgs/data/misc/cacert/default.nix
@@ -1,27 +1,23 @@
 { lib
 , stdenv
+, writeText
 , fetchurl
 , nss
-, python3
-, blacklist ? [ ]
+, buildcatrust
+, blacklist ? []
+, extraCertificateFiles ? []
+, extraCertificateStrings ? []
 
-  # Used for tests only
+# Used for tests only
 , runCommand
 , cacert
 , openssl
 }:
 
 let
-  certdata2pem = fetchurl {
-    name = "certdata2pem.py";
-    urls = [
-      "https://salsa.debian.org/debian/ca-certificates/raw/debian/20170717/mozilla/certdata2pem.py"
-      "https://git.launchpad.net/ubuntu/+source/ca-certificates/plain/mozilla/certdata2pem.py?id=47e49e1e0a8a1ca74deda27f88fe181191562957"
-    ];
-    sha256 = "1d4q27j1gss0186a5m8bs5dk786w07ccyq0qi6xmd2zr1a8q16wy";
-  };
+  blocklist = writeText "cacert-blocklist.txt" (lib.concatStringsSep "\n" blacklist);
+  extraCertificatesBundle = writeText "cacert-extra-certificates-bundle.crt" (lib.concatStringsSep "\n\n" extraCertificateStrings);
 in
-
 stdenv.mkDerivation rec {
   pname = "nss-cacert";
   version = "3.71";
@@ -31,93 +27,152 @@ stdenv.mkDerivation rec {
     sha256 = "0ly2l3dv6z5hlxs72h5x6796ni3x1bq60saavaf42ddgv4ax7b4r";
   };
 
-  outputs = [ "out" "unbundled" ];
+  outputs = [ "out" "unbundled" "p11kit" ];
 
-  nativeBuildInputs = [ python3 ];
+  nativeBuildInputs = [ buildcatrust ];
 
   configurePhase = ''
     ln -s nss/lib/ckfw/builtins/certdata.txt
-
-    cat << EOF > blacklist.txt
-    ${lib.concatStringsSep "\n" (map (c: ''"${c}"'') blacklist)}
-    EOF
-
-    # copy from the store, otherwise python will scan it for imports
-    cat "${certdata2pem}" > certdata2pem.py
   '';
 
   buildPhase = ''
-    python certdata2pem.py | grep -vE '^(!|UNTRUSTED)'
-
-    for cert in *.crt; do
-      echo $cert | cut -d. -f1 | sed -e 's,_, ,g' >> ca-bundle.crt
-      cat $cert >> ca-bundle.crt
-      echo >> ca-bundle.crt
-    done
+    mkdir unbundled
+    buildcatrust \
+      --certdata_input certdata.txt \
+      --ca_bundle_input "${extraCertificatesBundle}" ${lib.escapeShellArgs (map (arg: "${arg}") extraCertificateFiles)} \
+      --blocklist "${blocklist}" \
+      --ca_bundle_output ca-bundle.crt \
+      --ca_unpacked_output unbundled \
+      --p11kit_output ca-bundle.trust.p11-kit
   '';
 
   installPhase = ''
-    mkdir -pv $out/etc/ssl/certs
-    cp -v ca-bundle.crt $out/etc/ssl/certs
+    install -D -t "$out/etc/ssl/certs" ca-bundle.crt
+
+    # install p11-kit specific output to p11kit output
+    install -D -t "$p11kit/etc/ssl/trust-source" ca-bundle.trust.p11-kit
+
     # install individual certs in unbundled output
-    mkdir -pv $unbundled/etc/ssl/certs
-    cp -v *.crt $unbundled/etc/ssl/certs
-    rm $unbundled/etc/ssl/certs/ca-bundle.crt  # not wanted in unbundled
+    install -D -t "$unbundled/etc/ssl/certs" unbundled/*.crt
   '';
 
   setupHook = ./setup-hook.sh;
 
   passthru = {
     updateScript = ./update.sh;
-    tests = {
-      # Test that building this derivation with a blacklist works, and that UTF-8 is supported.
-      blacklist-utf8 =
-        let
-          blacklistCAToFingerprint = {
-            # "blacklist" uses the CA name from the NSS bundle, but we check for presence using the SHA256 fingerprint.
-            "CFCA EV ROOT" = "5C:C3:D7:8E:4E:1D:5E:45:54:7A:04:E6:87:3E:64:F9:0C:F9:53:6D:1C:CC:2E:F8:00:F3:55:C4:C5:FD:70:FD";
-            "NetLock Arany (Class Gold) Főtanúsítvány" = "6C:61:DA:C3:A2:DE:F0:31:50:6B:E0:36:D2:A6:FE:40:19:94:FB:D1:3D:F9:C8:D4:66:59:92:74:C4:46:EC:98";
-          };
-          mapBlacklist = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f blacklistCAToFingerprint);
-        in
-        runCommand "verify-the-cacert-filter-output"
-          {
-            cacert = cacert.unbundled;
-            cacertWithExcludes = (cacert.override {
-              blacklist = builtins.attrNames blacklistCAToFingerprint;
-            }).unbundled;
-
-            nativeBuildInputs = [ openssl ];
-          } ''
-          isPresent() {
-            # isPresent <unbundled-dir> <ca name> <ca sha256 fingerprint>
-            for f in $1/etc/ssl/certs/*.crt; do
-              fingerprint="$(openssl x509 -in "$f" -noout -fingerprint -sha256 | cut -f2 -d=)"
-              if [[ "x$fingerprint" == "x$3" ]]; then
-                return 0
+    tests = let
+      isTrusted = ''
+        isTrusted() {
+          # isTrusted <unbundled-dir> <ca name> <ca sha256 fingerprint>
+          for f in $1/etc/ssl/certs/*.crt; do
+            if ! [[ -s "$f" ]]; then continue; fi
+            fingerprint="$(openssl x509 -in "$f" -noout -fingerprint -sha256 | cut -f2 -d=)"
+            if [[ "x$fingerprint" == "x$3" ]]; then
+              # If the certificate is treated as rejected for TLS Web Server, then we consider it untrusted.
+              if openssl x509 -in "$f" -noout -text | grep -q '^Rejected Uses:'; then
+                if openssl x509 -in "$f" -noout -text | grep -A1 '^Rejected Uses:' | grep -q 'TLS Web Server'; then
+                  return 1
+                fi
               fi
-            done
-            return 1
-          }
-
-          # Ensure that each certificate is in the main "cacert".
-          ${mapBlacklist (caName: caFingerprint: ''
-            isPresent "$cacert" "${caName}" "${caFingerprint}" || ({
-              echo "CA fingerprint ${caFingerprint} (${caName}) is missing from the CA bundle. Consider picking a different CA for the blacklist test." >&2
-              exit 1
-            })
-          '')}
-
-          # Ensure that each certificate is NOT in the "cacertWithExcludes".
-          ${mapBlacklist (caName: caFingerprint: ''
-            isPresent "$cacertWithExcludes" "${caName}" "${caFingerprint}" && ({
-              echo "CA fingerprint ${caFingerprint} (${caName}) is present in the cacertWithExcludes bundle." >&2
-              exit 1
-            })
-          '')}
-
-          touch $out
+              return 0
+            fi
+          done
+          return 1
+        }
+      '';
+    in {
+      # Test that building this derivation with a blacklist works, and that UTF-8 is supported.
+      blacklist-utf8 = let
+        blacklistCAToFingerprint = {
+          # "blacklist" uses the CA name from the NSS bundle, but we check for presence using the SHA256 fingerprint.
+          "CFCA EV ROOT" = "5C:C3:D7:8E:4E:1D:5E:45:54:7A:04:E6:87:3E:64:F9:0C:F9:53:6D:1C:CC:2E:F8:00:F3:55:C4:C5:FD:70:FD";
+          "NetLock Arany (Class Gold) Főtanúsítvány" = "6C:61:DA:C3:A2:DE:F0:31:50:6B:E0:36:D2:A6:FE:40:19:94:FB:D1:3D:F9:C8:D4:66:59:92:74:C4:46:EC:98";
+        };
+        mapBlacklist = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f blacklistCAToFingerprint);
+      in runCommand "verify-the-cacert-filter-output" {
+        cacert = cacert.unbundled;
+        cacertWithExcludes = (cacert.override {
+          blacklist = builtins.attrNames blacklistCAToFingerprint;
+        }).unbundled;
+
+        nativeBuildInputs = [ openssl ];
+      } ''
+        ${isTrusted}
+
+        # Ensure that each certificate is in the main "cacert".
+        ${mapBlacklist (caName: caFingerprint: ''
+          isTrusted "$cacert" "${caName}" "${caFingerprint}" || ({
+            echo "CA fingerprint ${caFingerprint} (${caName}) is missing from the CA bundle. Consider picking a different CA for the blacklist test." >&2
+            exit 1
+          })
+        '')}
+
+        # Ensure that each certificate is NOT in the "cacertWithExcludes".
+        ${mapBlacklist (caName: caFingerprint: ''
+          isTrusted "$cacertWithExcludes" "${caName}" "${caFingerprint}" && ({
+            echo "CA fingerprint ${caFingerprint} (${caName}) is present in the cacertWithExcludes bundle." >&2
+            exit 1
+          })
+        '')}
+
+        touch "$out"
+      '';
+
+      # Test that we can add additional certificates to the store, and have them be trusted.
+      extra-certificates = let
+        extraCertificateStr = ''
+          -----BEGIN CERTIFICATE-----
+          MIIB5DCCAWqgAwIBAgIUItvsAYEIdYDkOIo5sdDYMcUaNuIwCgYIKoZIzj0EAwIw
+          KTEnMCUGA1UEAwweTml4T1MgY2FjZXJ0IGV4dHJhIGNlcnRpZmljYXRlMB4XDTIx
+          MDYxMjE5MDQzMFoXDTIyMDYxMjE5MDQzMFowKTEnMCUGA1UEAwweTml4T1MgY2Fj
+          ZXJ0IGV4dHJhIGNlcnRpZmljYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuP8y
+          lAm6ZyQt9v/P6gTlV/a9R+D61WjucW04kaegOhg8csiluimYodiSv0Pbgymu+Zxm
+          A3Bz9QGmytaYTiJ16083rJkwwIhqoYl7kWsLzreSTaLz87KH+rdeol59+H0Oo1Mw
+          UTAdBgNVHQ4EFgQUCxuHfvqI4YVU5M+A0+aKvd1LrdswHwYDVR0jBBgwFoAUCxuH
+          fvqI4YVU5M+A0+aKvd1LrdswDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNo
+          ADBlAjEArgxgjdNmRlSEuai0dzlktmBEDZKy2Iiul+ttSoce9ohfEVYESwO602HW
+          keVvI56vAjBCro3dc3m2TuktiKO6lQV56PUEyxko4H/sR5pnHlduCGRDlFzQKXf/
+          pMMmtj7cVb8=
+          -----END CERTIFICATE-----
         '';
+        extraCertificateFile = ./test-cert-file.crt;
+        extraCertificatesToFingerprint = {
+          # String above
+          "NixOS cacert extra certificate string" = "A3:20:D0:84:96:97:25:FF:98:B8:A9:6D:A3:7C:89:95:6E:7A:77:21:92:F3:33:E9:31:AF:5E:03:CE:A9:E5:EE";
+
+          # File
+          "NixOS cacert extra certificate file" = "88:B8:BE:A7:57:AC:F1:FE:D6:98:8B:50:E0:BD:0A:AE:88:C7:DF:70:26:E1:67:5E:F5:F6:91:27:FF:02:D4:A5";
+        };
+        mapExtra = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f extraCertificatesToFingerprint);
+      in runCommand "verify-the-cacert-extra-output" {
+        cacert = cacert.unbundled;
+        cacertWithExtras = (cacert.override {
+          extraCertificateStrings = [ extraCertificateStr ];
+          extraCertificateFiles = [ extraCertificateFile ];
+        }).unbundled;
+
+        nativeBuildInputs = [ openssl ];
+      } ''
+        ${isTrusted}
+
+        # Ensure that the extra certificate is not in the main "cacert".
+        ${mapExtra (extraName: extraFingerprint: ''
+          isTrusted "$cacert" "${extraName}" "${extraFingerprint}" && ({
+            echo "'extra' CA fingerprint ${extraFingerprint} (${extraName}) is present in the main CA bundle." >&2
+            exit 1
+          })
+        '')}
+
+        # Ensure that the extra certificates ARE in the "cacertWithExtras".
+        ${mapExtra (extraName: extraFingerprint: ''
+          isTrusted "$cacertWithExtras" "${extraName}" "${extraFingerprint}" || ({
+            echo "CA fingerprint ${extraFingerprint} (${extraName}) is not present in the cacertWithExtras bundle." >&2
+            exit 1
+          })
+        '')}
+
+        touch "$out"
+      '';
     };
   };
 
diff --git a/pkgs/data/misc/cacert/test-cert-file.crt b/pkgs/data/misc/cacert/test-cert-file.crt
new file mode 100644
index 0000000000000..095f38817d20f
--- /dev/null
+++ b/pkgs/data/misc/cacert/test-cert-file.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB7TCCAXSgAwIBAgIUFJB0STXn22fIEDjpncEt++IdFeMwCgYIKoZIzj0EAwIw
+LjEsMCoGA1UEAwwjTml4T1MgY2FjZXJ0IGV4dHJhIGNlcnRpZmljYXRlIGZpbGUw
+HhcNMjEwNjEyMTkxODA4WhcNMjIwNjEyMTkxODA4WjAuMSwwKgYDVQQDDCNOaXhP
+UyBjYWNlcnQgZXh0cmEgY2VydGlmaWNhdGUgZmlsZTB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABMifTLM5K5xd+guGdKE1+NR7wnEJbxw5INzuMrkg/7jgEIQil4+L2YOF
+kU1gxcM80Ot8tQAG5OcSvX1DF6CxunpoCT+hnHqyfqoWFvl89i1BUKjyWCQ5WXEe
+nSkuJUmYC6NTMFEwHQYDVR0OBBYEFBE2kNis1ri4fweyNVRmvje83gFQMB8GA1Ud
+IwQYMBaAFBE2kNis1ri4fweyNVRmvje83gFQMA8GA1UdEwEB/wQFMAMBAf8wCgYI
+KoZIzj0EAwIDZwAwZAIwUZf1qaSb4cezulV+4B4FoJHY2B/nRVIi/rFD8634YEDT
+vcg6dmCi/AqLEzJn7uFMAjBVTu4EVC/mtQCGESFChMeb04fsuhXgttWSwWliVPEG
+jkG7u0UNNGaU8dvrjpqRRmA=
+-----END CERTIFICATE-----