Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1{ lib 2, stdenv 3, writeText 4, fetchurl 5, buildcatrust 6, blacklist ? [] 7, extraCertificateFiles ? [] 8, extraCertificateStrings ? [] 9 10# Used by update.sh 11, nssOverride ? null 12 13# Used for tests only 14, runCommand 15, cacert 16, openssl 17}: 18 19let 20 blocklist = writeText "cacert-blocklist.txt" (lib.concatStringsSep "\n" (blacklist ++ [ 21 # Mozilla does not trust new certificates issued by these CAs after 2022/11/30¹ 22 # in their products, but unfortunately we don't have such a fine-grained 23 # solution for most system packages², so we decided to eject these. 24 # 25 # [1] https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/oxX69KFvsm4/m/yLohoVqtCgAJ 26 # [2] https://utcc.utoronto.ca/~cks/space/blog/linux/CARootStoreTrustProblem 27 "TrustCor ECA-1" 28 "TrustCor RootCert CA-1" 29 "TrustCor RootCert CA-2" 30 ])); 31 extraCertificatesBundle = writeText "cacert-extra-certificates-bundle.crt" (lib.concatStringsSep "\n\n" extraCertificateStrings); 32 33 srcVersion = "3.89.1"; 34 version = if nssOverride != null then nssOverride.version else srcVersion; 35 meta = with lib; { 36 homepage = "https://curl.haxx.se/docs/caextract.html"; 37 description = "A bundle of X.509 certificates of public Certificate Authorities (CA)"; 38 platforms = platforms.all; 39 maintainers = with maintainers; [ fpletz lukegb ]; 40 license = licenses.mpl20; 41 }; 42 certdata = stdenv.mkDerivation { 43 pname = "nss-cacert-certdata"; 44 inherit version; 45 46 src = if nssOverride != null then nssOverride.src else fetchurl { 47 url = "mirror://mozilla/security/nss/releases/NSS_${lib.replaceStrings ["."] ["_"] version}_RTM/src/nss-${version}.tar.gz"; 48 hash = "sha256-OtrtuecMPF9AYDv2CgHjNhkKbb4Bkp05XxawH+hKAVY="; 49 }; 50 51 dontBuild = true; 52 53 installPhase = '' 54 runHook preInstall 55 56 mkdir $out 57 cp nss/lib/ckfw/builtins/certdata.txt $out 58 59 runHook postInstall 60 ''; 61 62 inherit meta; 63 }; 64in 65stdenv.mkDerivation rec { 66 pname = "nss-cacert"; 67 inherit version; 68 69 src = certdata; 70 71 outputs = [ "out" "unbundled" "p11kit" ]; 72 73 nativeBuildInputs = [ buildcatrust ]; 74 75 buildPhase = '' 76 mkdir unbundled 77 buildcatrust \ 78 --certdata_input certdata.txt \ 79 --ca_bundle_input "${extraCertificatesBundle}" ${lib.escapeShellArgs (map (arg: "${arg}") extraCertificateFiles)} \ 80 --blocklist "${blocklist}" \ 81 --ca_bundle_output ca-bundle.crt \ 82 --ca_unpacked_output unbundled \ 83 --p11kit_output ca-bundle.trust.p11-kit 84 ''; 85 86 installPhase = '' 87 install -D -t "$out/etc/ssl/certs" ca-bundle.crt 88 89 # install p11-kit specific output to p11kit output 90 install -D -t "$p11kit/etc/ssl/trust-source" ca-bundle.trust.p11-kit 91 92 # install individual certs in unbundled output 93 install -D -t "$unbundled/etc/ssl/certs" unbundled/*.crt 94 ''; 95 96 setupHook = ./setup-hook.sh; 97 98 passthru = { 99 updateScript = ./update.sh; 100 tests = let 101 isTrusted = '' 102 isTrusted() { 103 # isTrusted <unbundled-dir> <ca name> <ca sha256 fingerprint> 104 for f in $1/etc/ssl/certs/*.crt; do 105 if ! [[ -s "$f" ]]; then continue; fi 106 fingerprint="$(openssl x509 -in "$f" -noout -fingerprint -sha256 | cut -f2 -d=)" 107 if [[ "x$fingerprint" == "x$3" ]]; then 108 # If the certificate is treated as rejected for TLS Web Server, then we consider it untrusted. 109 if openssl x509 -in "$f" -noout -text | grep -q '^Rejected Uses:'; then 110 if openssl x509 -in "$f" -noout -text | grep -A1 '^Rejected Uses:' | grep -q 'TLS Web Server'; then 111 return 1 112 fi 113 fi 114 return 0 115 fi 116 done 117 return 1 118 } 119 ''; 120 in { 121 # Test that building this derivation with a blacklist works, and that UTF-8 is supported. 122 blacklist-utf8 = let 123 blacklistCAToFingerprint = { 124 # "blacklist" uses the CA name from the NSS bundle, but we check for presence using the SHA256 fingerprint. 125 "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"; 126 "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"; 127 }; 128 mapBlacklist = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f blacklistCAToFingerprint); 129 in runCommand "verify-the-cacert-filter-output" { 130 cacert = cacert.unbundled; 131 cacertWithExcludes = (cacert.override { 132 blacklist = builtins.attrNames blacklistCAToFingerprint; 133 }).unbundled; 134 135 nativeBuildInputs = [ openssl ]; 136 } '' 137 ${isTrusted} 138 139 # Ensure that each certificate is in the main "cacert". 140 ${mapBlacklist (caName: caFingerprint: '' 141 isTrusted "$cacert" "${caName}" "${caFingerprint}" || ({ 142 echo "CA fingerprint ${caFingerprint} (${caName}) is missing from the CA bundle. Consider picking a different CA for the blacklist test." >&2 143 exit 1 144 }) 145 '')} 146 147 # Ensure that each certificate is NOT in the "cacertWithExcludes". 148 ${mapBlacklist (caName: caFingerprint: '' 149 isTrusted "$cacertWithExcludes" "${caName}" "${caFingerprint}" && ({ 150 echo "CA fingerprint ${caFingerprint} (${caName}) is present in the cacertWithExcludes bundle." >&2 151 exit 1 152 }) 153 '')} 154 155 touch "$out" 156 ''; 157 158 # Test that we can add additional certificates to the store, and have them be trusted. 159 extra-certificates = let 160 extraCertificateStr = '' 161 -----BEGIN CERTIFICATE----- 162 MIIB5DCCAWqgAwIBAgIUItvsAYEIdYDkOIo5sdDYMcUaNuIwCgYIKoZIzj0EAwIw 163 KTEnMCUGA1UEAwweTml4T1MgY2FjZXJ0IGV4dHJhIGNlcnRpZmljYXRlMB4XDTIx 164 MDYxMjE5MDQzMFoXDTIyMDYxMjE5MDQzMFowKTEnMCUGA1UEAwweTml4T1MgY2Fj 165 ZXJ0IGV4dHJhIGNlcnRpZmljYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuP8y 166 lAm6ZyQt9v/P6gTlV/a9R+D61WjucW04kaegOhg8csiluimYodiSv0Pbgymu+Zxm 167 A3Bz9QGmytaYTiJ16083rJkwwIhqoYl7kWsLzreSTaLz87KH+rdeol59+H0Oo1Mw 168 UTAdBgNVHQ4EFgQUCxuHfvqI4YVU5M+A0+aKvd1LrdswHwYDVR0jBBgwFoAUCxuH 169 fvqI4YVU5M+A0+aKvd1LrdswDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNo 170 ADBlAjEArgxgjdNmRlSEuai0dzlktmBEDZKy2Iiul+ttSoce9ohfEVYESwO602HW 171 keVvI56vAjBCro3dc3m2TuktiKO6lQV56PUEyxko4H/sR5pnHlduCGRDlFzQKXf/ 172 pMMmtj7cVb8= 173 -----END CERTIFICATE----- 174 ''; 175 extraCertificateFile = ./test-cert-file.crt; 176 extraCertificatesToFingerprint = { 177 # String above 178 "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"; 179 180 # File 181 "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"; 182 }; 183 mapExtra = f: lib.concatStringsSep "\n" (lib.mapAttrsToList f extraCertificatesToFingerprint); 184 in runCommand "verify-the-cacert-extra-output" { 185 cacert = cacert.unbundled; 186 cacertWithExtras = (cacert.override { 187 extraCertificateStrings = [ extraCertificateStr ]; 188 extraCertificateFiles = [ extraCertificateFile ]; 189 }).unbundled; 190 191 nativeBuildInputs = [ openssl ]; 192 } '' 193 ${isTrusted} 194 195 # Ensure that the extra certificate is not in the main "cacert". 196 ${mapExtra (extraName: extraFingerprint: '' 197 isTrusted "$cacert" "${extraName}" "${extraFingerprint}" && ({ 198 echo "'extra' CA fingerprint ${extraFingerprint} (${extraName}) is present in the main CA bundle." >&2 199 exit 1 200 }) 201 '')} 202 203 # Ensure that the extra certificates ARE in the "cacertWithExtras". 204 ${mapExtra (extraName: extraFingerprint: '' 205 isTrusted "$cacertWithExtras" "${extraName}" "${extraFingerprint}" || ({ 206 echo "CA fingerprint ${extraFingerprint} (${extraName}) is not present in the cacertWithExtras bundle." >&2 207 exit 1 208 }) 209 '')} 210 211 touch "$out" 212 ''; 213 }; 214 }; 215 216 inherit meta; 217}