ca-certs: generate install artifacts and improve certdata/trust-store compatibility

- stop committing generated man/completion files in contrib/
- generate man page and shell completions during Meson install via install-generated-docs.sh
- switch reqwest to native-tls and add Mozilla hg bootstrap certs with openssl fallback fetch
- make certdata sync use a temporary auto-cleaned workspace by default
- add BLFS/make-ca compatibility outputs and symlinks under /etc/pki and /etc/ssl
- validate extracted outputs are non-empty and add tests for temp sync output behavior
This commit is contained in:
2026-02-28 00:47:16 -06:00
parent eb80972c0a
commit 11580aebcf
10 changed files with 669 additions and 1643 deletions
Generated
+135 -507
View File
@@ -64,28 +64,6 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "aws-lc-rs"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.37.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "base64"
version = "0.22.1"
@@ -129,29 +107,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clap"
version = "4.5.60"
@@ -211,36 +175,17 @@ dependencies = [
"roff",
]
[[package]]
name = "cmake"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
dependencies = [
"cc",
]
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
@@ -264,10 +209,20 @@ dependencies = [
]
[[package]]
name = "dunce"
version = "1.0.5"
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
@@ -275,6 +230,21 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -284,12 +254,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures-channel"
version = "0.3.32"
@@ -339,19 +303,6 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.4"
@@ -359,11 +310,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"wasip2",
"wasm-bindgen",
]
[[package]]
@@ -433,18 +382,18 @@ dependencies = [
]
[[package]]
name = "hyper-rustls"
version = "0.27.7"
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"http",
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"rustls",
"rustls-pki-types",
"native-tls",
"tokio",
"tokio-rustls",
"tokio-native-tls",
"tower-service",
]
@@ -601,38 +550,6 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.88"
@@ -649,6 +566,12 @@ version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.1"
@@ -661,12 +584,6 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "memchr"
version = "2.8.0"
@@ -684,6 +601,23 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -697,10 +631,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "openssl-probe"
version = "0.2.1"
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "percent-encoding"
@@ -720,6 +692,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "potential_utf"
version = "0.1.4"
@@ -729,15 +707,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -747,62 +716,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"aws-lc-rs",
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]]
name = "quote"
version = "1.0.44"
@@ -818,35 +731,6 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "reqwest"
version = "0.13.2"
@@ -862,19 +746,17 @@ dependencies = [
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"js-sys",
"log",
"native-tls",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tokio-native-tls",
"tower",
"tower-http",
"tower-service",
@@ -884,20 +766,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roff"
version = "0.2.2"
@@ -905,35 +773,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
[[package]]
name = "rustc-hash"
version = "2.1.1"
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustls"
version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"aws-lc-rs",
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
@@ -942,64 +791,15 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"web-time",
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.28"
@@ -1011,9 +811,9 @@ dependencies = [
[[package]]
name = "security-framework"
version = "3.7.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags",
"core-foundation",
@@ -1102,12 +902,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
@@ -1140,43 +934,16 @@ dependencies = [
]
[[package]]
name = "thiserror"
version = "1.0.69"
name = "tempfile"
version = "3.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl 2.0.18",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
@@ -1189,21 +956,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.49.0"
@@ -1219,12 +971,12 @@ dependencies = [
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"rustls",
"native-tls",
"tokio",
]
@@ -1304,12 +1056,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.8"
@@ -1335,14 +1081,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "walkdir"
version = "2.5.0"
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "want"
@@ -1437,49 +1179,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-root-certs"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -1507,21 +1212,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@@ -1555,12 +1245,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -1573,12 +1257,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@@ -1591,12 +1269,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -1621,12 +1293,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@@ -1639,12 +1305,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@@ -1657,12 +1317,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@@ -1675,12 +1329,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@@ -1728,26 +1376,6 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
+1 -1
View File
@@ -9,7 +9,7 @@ base64 = "0.22.1"
clap = { version = "4.5.60", features = ["derive"] }
clap_complete = "4.5.66"
clap_mangen = "0.2.31"
reqwest = { version = "0.13.2", default-features = false, features = ["blocking", "rustls"] }
reqwest = { version = "0.13.2", default-features = false, features = ["blocking", "native-tls"] }
[lints.rust]
warnings = "deny"
+2 -2
View File
@@ -34,7 +34,7 @@ target/release/ca-certs
### Meson (packaging-friendly)
Builds the Rust binary and installs the man page + shell completions from `contrib/`.
Builds the Rust binary and generates man/completion artifacts during install.
```bash
meson setup build
@@ -156,7 +156,7 @@ Run tests:
cargo test
```
The repository also includes a generated man page and shell completions under `contrib/`.
Man page and shell completions are generated by `ca-certs gen-artifacts`.
## License
-31
View File
@@ -1,31 +0,0 @@
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH ca-certs 8 "ca-certs 0.1.0"
.SH NAME
ca\-certs \- Manage Linux CA trust store sources and extracted bundles (p11\-kit/trust)
.SH SYNOPSIS
\fBca\-certs\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] <\fIsubcommands\fR>
.SH DESCRIPTION
Manage Linux CA trust store sources and extracted bundles (p11\-kit/trust)
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help
.TP
\fB\-V\fR, \fB\-\-version\fR
Print version
.SH SUBCOMMANDS
.TP
ca\-certs\-add(8)
Add a PEM CA certificate to trust\-source/anchors and refresh extracted outputs
.TP
ca\-certs\-extract(8)
Regenerate extracted trust bundles (like update\-ca\-trust extract)
.TP
ca\-certs\-certdata(8)
Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)
.TP
ca\-certs\-help(8)
Print this message or the help of the given subcommand(s)
.SH VERSION
v0.1.0
-401
View File
@@ -1,401 +0,0 @@
#compdef ca-certs
autoload -U is-at-least
_ca-certs() {
typeset -A opt_args
typeset -a _arguments_options
local ret=1
if is-at-least 5.2; then
_arguments_options=(-s -S -C)
else
_arguments_options=(-s -C)
fi
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" : \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
":: :_ca-certs_commands" \
"*::: :->ca-certs" \
&& ret=0
case $state in
(ca-certs)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:ca-certs-command-$line[1]:"
case $line[1] in
(add)
_arguments "${_arguments_options[@]}" : \
'-n+[Output certificate name (without extension). Defaults to the input filename stem]:NAME:_default' \
'--name=[Output certificate name (without extension). Defaults to the input filename stem]:NAME:_default' \
'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \
'--output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \
'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \
'--force[Overwrite an existing anchor if contents differ]' \
'--no-extract[Add certificate but do not run extraction]' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
':cert -- Path to a PEM-encoded CA certificate:_files' \
&& ret=0
;;
(extract)
_arguments "${_arguments_options[@]}" : \
'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \
'--output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:OUTPUT:_files' \
'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
&& ret=0
;;
(certdata)
_arguments "${_arguments_options[@]}" : \
'-h[Print help]' \
'--help[Print help]' \
":: :_ca-certs__certdata_commands" \
"*::: :->certdata" \
&& ret=0
case $state in
(certdata)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:ca-certs-certdata-command-$line[1]:"
case $line[1] in
(fetch)
_arguments "${_arguments_options[@]}" : \
'--url=[Source URL for certdata.txt]:URL:_default' \
'--log-url=[Override log URL used to determine the latest revision (optional)]:LOG_URL:_default' \
'-o+[Output file path]:OUTPUT:_files' \
'--output=[Output file path]:OUTPUT:_files' \
'--force[Overwrite an existing file if contents differ]' \
'--no-revision-check[Always download even if the local file revision matches the remote revision]' \
'--no-parse[Skip parsing/summary after download]' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
&& ret=0
;;
(parse)
_arguments "${_arguments_options[@]}" : \
'--limit=[Print up to N object summaries after the aggregate stats]:LIMIT:_default' \
'-h[Print help]' \
'--help[Print help]' \
'::input -- Path to certdata.txt:_files' \
&& ret=0
;;
(convert)
_arguments "${_arguments_options[@]}" : \
'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \
'--output=[Destination p11-kit trust source file inside the target root]:OUTPUT:_files' \
'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \
'--extract-output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \
'--force[Overwrite an existing output file if contents differ]' \
'--no-extract[Skip running trust extraction after writing the Mozilla source bundle]' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
'::input -- Path to certdata.txt:_files' \
&& ret=0
;;
(sync)
_arguments "${_arguments_options[@]}" : \
'--url=[Source URL for certdata.txt]:URL:_default' \
'--log-url=[Override log URL used to determine the latest revision (optional)]:LOG_URL:_default' \
'--certdata-output=[Local certdata.txt path used for fetch and convert]:CERTDATA_OUTPUT:_files' \
'--root=[Target root filesystem (for chroot/image builds)]:ROOT:_files' \
'--mozilla-output=[Destination p11-kit trust source file inside the target root]:MOZILLA_OUTPUT:_files' \
'-o+[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \
'--extract-output=[Extracted output directory inside the target root (default\: /etc/ca-certificates/extracted)]:EXTRACT_OUTPUT:_files' \
'--force[Overwrite existing files if contents differ]' \
'--no-revision-check[Always download even if the local certdata revision matches the remote revision]' \
'--no-parse[Skip parsing/summary after download]' \
'--no-extract[Skip running trust extraction after writing the Mozilla source bundle]' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
":: :_ca-certs__certdata__help_commands" \
"*::: :->help" \
&& ret=0
case $state in
(help)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:ca-certs-certdata-help-command-$line[1]:"
case $line[1] in
(fetch)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(parse)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(convert)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(sync)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
esac
;;
esac
;;
esac
;;
esac
;;
(gen-artifacts)
_arguments "${_arguments_options[@]}" : \
'--out-dir=[Directory to write generated packaging artifacts into]:OUT_DIR:_files' \
'*--shell=[Shell completions to generate]:SHELLS:(bash fish zsh)' \
'--dry-run[Print actions without modifying files]' \
'-h[Print help]' \
'--help[Print help]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
":: :_ca-certs__help_commands" \
"*::: :->help" \
&& ret=0
case $state in
(help)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:ca-certs-help-command-$line[1]:"
case $line[1] in
(add)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(extract)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(certdata)
_arguments "${_arguments_options[@]}" : \
":: :_ca-certs__help__certdata_commands" \
"*::: :->certdata" \
&& ret=0
case $state in
(certdata)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:ca-certs-help-certdata-command-$line[1]:"
case $line[1] in
(fetch)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(parse)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(convert)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(sync)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
esac
;;
esac
;;
(gen-artifacts)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" : \
&& ret=0
;;
esac
;;
esac
;;
esac
;;
esac
}
(( $+functions[_ca-certs_commands] )) ||
_ca-certs_commands() {
local commands; commands=(
'add:Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' \
'extract:Regenerate extracted trust bundles (like update-ca-trust extract)' \
'certdata:Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' \
'gen-artifacts:' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'ca-certs commands' commands "$@"
}
(( $+functions[_ca-certs__add_commands] )) ||
_ca-certs__add_commands() {
local commands; commands=()
_describe -t commands 'ca-certs add commands' commands "$@"
}
(( $+functions[_ca-certs__certdata_commands] )) ||
_ca-certs__certdata_commands() {
local commands; commands=(
'fetch:Fetch certdata.txt from an NSS source URL' \
'parse:Parse a local certdata.txt file and print a summary' \
'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \
'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'ca-certs certdata commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__convert_commands] )) ||
_ca-certs__certdata__convert_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata convert commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__fetch_commands] )) ||
_ca-certs__certdata__fetch_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata fetch commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help_commands] )) ||
_ca-certs__certdata__help_commands() {
local commands; commands=(
'fetch:Fetch certdata.txt from an NSS source URL' \
'parse:Parse a local certdata.txt file and print a summary' \
'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \
'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'ca-certs certdata help commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help__convert_commands] )) ||
_ca-certs__certdata__help__convert_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata help convert commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help__fetch_commands] )) ||
_ca-certs__certdata__help__fetch_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata help fetch commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help__help_commands] )) ||
_ca-certs__certdata__help__help_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata help help commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help__parse_commands] )) ||
_ca-certs__certdata__help__parse_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata help parse commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__help__sync_commands] )) ||
_ca-certs__certdata__help__sync_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata help sync commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__parse_commands] )) ||
_ca-certs__certdata__parse_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata parse commands' commands "$@"
}
(( $+functions[_ca-certs__certdata__sync_commands] )) ||
_ca-certs__certdata__sync_commands() {
local commands; commands=()
_describe -t commands 'ca-certs certdata sync commands' commands "$@"
}
(( $+functions[_ca-certs__extract_commands] )) ||
_ca-certs__extract_commands() {
local commands; commands=()
_describe -t commands 'ca-certs extract commands' commands "$@"
}
(( $+functions[_ca-certs__gen-artifacts_commands] )) ||
_ca-certs__gen-artifacts_commands() {
local commands; commands=()
_describe -t commands 'ca-certs gen-artifacts commands' commands "$@"
}
(( $+functions[_ca-certs__help_commands] )) ||
_ca-certs__help_commands() {
local commands; commands=(
'add:Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs' \
'extract:Regenerate extracted trust bundles (like update-ca-trust extract)' \
'certdata:Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)' \
'gen-artifacts:' \
'help:Print this message or the help of the given subcommand(s)' \
)
_describe -t commands 'ca-certs help commands' commands "$@"
}
(( $+functions[_ca-certs__help__add_commands] )) ||
_ca-certs__help__add_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help add commands' commands "$@"
}
(( $+functions[_ca-certs__help__certdata_commands] )) ||
_ca-certs__help__certdata_commands() {
local commands; commands=(
'fetch:Fetch certdata.txt from an NSS source URL' \
'parse:Parse a local certdata.txt file and print a summary' \
'convert:Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs' \
'sync:Run the full pipeline\: fetch certdata.txt, convert to p11-kit source, then extract outputs' \
)
_describe -t commands 'ca-certs help certdata commands' commands "$@"
}
(( $+functions[_ca-certs__help__certdata__convert_commands] )) ||
_ca-certs__help__certdata__convert_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help certdata convert commands' commands "$@"
}
(( $+functions[_ca-certs__help__certdata__fetch_commands] )) ||
_ca-certs__help__certdata__fetch_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help certdata fetch commands' commands "$@"
}
(( $+functions[_ca-certs__help__certdata__parse_commands] )) ||
_ca-certs__help__certdata__parse_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help certdata parse commands' commands "$@"
}
(( $+functions[_ca-certs__help__certdata__sync_commands] )) ||
_ca-certs__help__certdata__sync_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help certdata sync commands' commands "$@"
}
(( $+functions[_ca-certs__help__extract_commands] )) ||
_ca-certs__help__extract_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help extract commands' commands "$@"
}
(( $+functions[_ca-certs__help__gen-artifacts_commands] )) ||
_ca-certs__help__gen-artifacts_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help gen-artifacts commands' commands "$@"
}
(( $+functions[_ca-certs__help__help_commands] )) ||
_ca-certs__help__help_commands() {
local commands; commands=()
_describe -t commands 'ca-certs help help commands' commands "$@"
}
if [ "$funcstack[1]" = "_ca-certs" ]; then
_ca-certs "$@"
else
compdef _ca-certs ca-certs
fi
-558
View File
@@ -1,558 +0,0 @@
_ca-certs() {
local i cur prev opts cmd
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
cur="$2"
else
cur="${COMP_WORDS[COMP_CWORD]}"
fi
prev="$3"
cmd=""
opts=""
for i in "${COMP_WORDS[@]:0:COMP_CWORD}"
do
case "${cmd},${i}" in
",$1")
cmd="ca__certs"
;;
ca__certs,add)
cmd="ca__certs__add"
;;
ca__certs,certdata)
cmd="ca__certs__certdata"
;;
ca__certs,extract)
cmd="ca__certs__extract"
;;
ca__certs,gen-artifacts)
cmd="ca__certs__gen__artifacts"
;;
ca__certs,help)
cmd="ca__certs__help"
;;
ca__certs__certdata,convert)
cmd="ca__certs__certdata__convert"
;;
ca__certs__certdata,fetch)
cmd="ca__certs__certdata__fetch"
;;
ca__certs__certdata,help)
cmd="ca__certs__certdata__help"
;;
ca__certs__certdata,parse)
cmd="ca__certs__certdata__parse"
;;
ca__certs__certdata,sync)
cmd="ca__certs__certdata__sync"
;;
ca__certs__certdata__help,convert)
cmd="ca__certs__certdata__help__convert"
;;
ca__certs__certdata__help,fetch)
cmd="ca__certs__certdata__help__fetch"
;;
ca__certs__certdata__help,help)
cmd="ca__certs__certdata__help__help"
;;
ca__certs__certdata__help,parse)
cmd="ca__certs__certdata__help__parse"
;;
ca__certs__certdata__help,sync)
cmd="ca__certs__certdata__help__sync"
;;
ca__certs__help,add)
cmd="ca__certs__help__add"
;;
ca__certs__help,certdata)
cmd="ca__certs__help__certdata"
;;
ca__certs__help,extract)
cmd="ca__certs__help__extract"
;;
ca__certs__help,gen-artifacts)
cmd="ca__certs__help__gen__artifacts"
;;
ca__certs__help,help)
cmd="ca__certs__help__help"
;;
ca__certs__help__certdata,convert)
cmd="ca__certs__help__certdata__convert"
;;
ca__certs__help__certdata,fetch)
cmd="ca__certs__help__certdata__fetch"
;;
ca__certs__help__certdata,parse)
cmd="ca__certs__help__certdata__parse"
;;
ca__certs__help__certdata,sync)
cmd="ca__certs__help__certdata__sync"
;;
*)
;;
esac
done
case "${cmd}" in
ca__certs)
opts="-h -V --help --version add extract certdata gen-artifacts help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__add)
opts="-n -o -h --name --force --no-extract --output --root --dry-run --help <CERT>"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--name)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-n)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--root)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata)
opts="-h --help fetch parse convert sync help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__convert)
opts="-o -h --root --output --force --no-extract --extract-output --dry-run --help [INPUT]"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--root)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--extract-output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__fetch)
opts="-o -h --url --log-url --output --force --no-revision-check --no-parse --dry-run --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--url)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--log-url)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help)
opts="fetch parse convert sync help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help__convert)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help__fetch)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help__help)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help__parse)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__help__sync)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__parse)
opts="-h --limit --help [INPUT]"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--limit)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__certdata__sync)
opts="-o -h --url --log-url --certdata-output --root --mozilla-output --force --no-revision-check --no-parse --no-extract --extract-output --dry-run --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--url)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--log-url)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--certdata-output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--root)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--mozilla-output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--extract-output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__extract)
opts="-o -h --output --root --dry-run --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--output)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--root)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__gen__artifacts)
opts="-h --out-dir --shell --dry-run --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--out-dir)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--shell)
COMPREPLY=($(compgen -W "bash fish zsh" -- "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help)
opts="add extract certdata gen-artifacts help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__add)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__certdata)
opts="fetch parse convert sync"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__certdata__convert)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__certdata__fetch)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__certdata__parse)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__certdata__sync)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__extract)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__gen__artifacts)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
ca__certs__help__help)
opts=""
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -F _ca-certs -o nosort -o bashdefault -o default ca-certs
else
complete -F _ca-certs -o bashdefault -o default ca-certs
fi
-99
View File
@@ -1,99 +0,0 @@
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
function __fish_ca_certs_global_optspecs
string join \n h/help V/version
end
function __fish_ca_certs_needs_command
# Figure out if the current invocation already has a command.
set -l cmd (commandline -opc)
set -e cmd[1]
argparse -s (__fish_ca_certs_global_optspecs) -- $cmd 2>/dev/null
or return
if set -q argv[1]
# Also print the command, so this can be used to figure out what it is.
echo $argv[1]
return 1
end
return 0
end
function __fish_ca_certs_using_subcommand
set -l cmd (__fish_ca_certs_needs_command)
test -z "$cmd"
and return 1
contains -- $cmd[1] $argv
end
complete -c ca-certs -n "__fish_ca_certs_needs_command" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_needs_command" -s V -l version -d 'Print version'
complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "add" -d 'Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs'
complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "extract" -d 'Regenerate extracted trust bundles (like update-ca-trust extract)'
complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "certdata" -d 'Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)'
complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "gen-artifacts"
complete -c ca-certs -n "__fish_ca_certs_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s n -l name -d 'Output certificate name (without extension). Defaults to the input filename stem' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s o -l output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l force -d 'Overwrite an existing anchor if contents differ'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l no-extract -d 'Add certificate but do not run extraction'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand add" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -s o -l output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand extract" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and not __fish_seen_subcommand_from fetch parse convert sync help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l url -d 'Source URL for certdata.txt' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l log-url -d 'Override log URL used to determine the latest revision (optional)' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -s o -l output -d 'Output file path' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l force -d 'Overwrite an existing file if contents differ'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l no-revision-check -d 'Always download even if the local file revision matches the remote revision'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l no-parse -d 'Skip parsing/summary after download'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from fetch" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from parse" -l limit -d 'Print up to N object summaries after the aggregate stats' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from parse" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l output -d 'Destination p11-kit trust source file inside the target root' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -s o -l extract-output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l force -d 'Overwrite an existing output file if contents differ'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l no-extract -d 'Skip running trust extraction after writing the Mozilla source bundle'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from convert" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l url -d 'Source URL for certdata.txt' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l log-url -d 'Override log URL used to determine the latest revision (optional)' -r
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l certdata-output -d 'Local certdata.txt path used for fetch and convert' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l root -d 'Target root filesystem (for chroot/image builds)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l mozilla-output -d 'Destination p11-kit trust source file inside the target root' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -s o -l extract-output -d 'Extracted output directory inside the target root (default: /etc/ca-certificates/extracted)' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l force -d 'Overwrite existing files if contents differ'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-revision-check -d 'Always download even if the local certdata revision matches the remote revision'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-parse -d 'Skip parsing/summary after download'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l no-extract -d 'Skip running trust extraction after writing the Mozilla source bundle'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from sync" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand certdata; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l out-dir -d 'Directory to write generated packaging artifacts into' -r -F
complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l shell -d 'Shell completions to generate' -r -f -a "bash\t''
fish\t''
zsh\t''"
complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -l dry-run -d 'Print actions without modifying files'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand gen-artifacts" -s h -l help -d 'Print help'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "add" -d 'Add a PEM CA certificate to trust-source/anchors and refresh extracted outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "extract" -d 'Regenerate extracted trust bundles (like update-ca-trust extract)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "certdata" -d 'Fetch and parse Mozilla/NSS certdata.txt (no certificate generation yet)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "gen-artifacts"
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and not __fish_seen_subcommand_from add extract certdata gen-artifacts help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "fetch" -d 'Fetch certdata.txt from an NSS source URL'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "parse" -d 'Parse a local certdata.txt file and print a summary'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "convert" -d 'Convert certdata.txt into a Mozilla p11-kit trust bundle and optionally extract outputs'
complete -c ca-certs -n "__fish_ca_certs_using_subcommand help; and __fish_seen_subcommand_from certdata" -f -a "sync" -d 'Run the full pipeline: fetch certdata.txt, convert to p11-kit source, then extract outputs'
+7 -29
View File
@@ -14,15 +14,6 @@ tracked_rust_inputs = files(
'src/main.rs',
)
# Track packaging assets in one place so install rules stay auditable.
tracked_packaging_assets = files(
'LICENSE',
'contrib/ca-certs.8',
'contrib/completions/ca-certs.bash',
'contrib/completions/ca-certs.fish',
'contrib/completions/_ca-certs',
)
tracked_install_paths = [
join_paths(get_option('bindir'), 'ca-certs'),
join_paths(get_option('datadir'), 'licenses', meson.project_name(), 'LICENSE'),
@@ -54,31 +45,18 @@ ca_certs_bin = custom_target(
install_dir: get_option('bindir'),
)
install_data(
'contrib/ca-certs.8',
install_dir: join_paths(get_option('mandir'), 'man8'),
)
install_data(
'contrib/completions/ca-certs.bash',
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions'),
rename: 'ca-certs',
)
install_data(
'contrib/completions/ca-certs.fish',
install_dir: join_paths(get_option('datadir'), 'fish', 'vendor_completions.d'),
)
install_data(
'contrib/completions/_ca-certs',
install_dir: join_paths(get_option('datadir'), 'zsh', 'site-functions'),
meson.add_install_script(
sh,
files('scripts/install-generated-docs.sh'),
ca_certs_bin_path,
get_option('mandir'),
get_option('datadir'),
)
summary(
{
'tracked rust inputs': tracked_rust_inputs.length(),
'tracked packaging assets': tracked_packaging_assets.length(),
'generated docs on install': true,
'tracked install paths': '\n ' + '\n '.join(tracked_install_paths),
},
section: 'Packaging',
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
set -eu
bin="$1"
mandir_opt="$2"
datadir_opt="$3"
resolve_dir() {
case "$1" in
/*) printf '%s%s\n' "${DESTDIR:-}" "$1" ;;
*) printf '%s/%s\n' "${MESON_INSTALL_DESTDIR_PREFIX:?}" "$1" ;;
esac
}
man_root="$(resolve_dir "$mandir_opt")"
data_root="$(resolve_dir "$datadir_opt")"
tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/ca-certs-docs.XXXXXX")"
cleanup() {
rm -rf "$tmp_dir"
}
trap cleanup EXIT INT TERM
"$bin" gen-artifacts --out-dir "$tmp_dir"
install -Dm644 "$tmp_dir/ca-certs.8" \
"$man_root/man8/ca-certs.8"
install -Dm644 "$tmp_dir/completions/ca-certs.bash" \
"$data_root/bash-completion/completions/ca-certs"
install -Dm644 "$tmp_dir/completions/ca-certs.fish" \
"$data_root/fish/vendor_completions.d/ca-certs.fish"
install -Dm644 "$tmp_dir/completions/_ca-certs" \
"$data_root/zsh/site-functions/_ca-certs"
+491 -15
View File
@@ -9,19 +9,84 @@ use std::io::Write;
use std::os::unix::fs::{PermissionsExt, symlink};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::{SystemTime, UNIX_EPOCH};
// Default paths mirror the p11-kit / update-ca-trust layout used by this system.
const DEFAULT_ROOT: &str = "/";
const DEFAULT_ANCHORS_DIR: &str = "/etc/ca-certificates/trust-source/anchors";
const DEFAULT_EXTRACTED_DIR: &str = "/etc/ca-certificates/extracted";
const DEFAULT_SSL_CERTS_DIR: &str = "/etc/ssl/certs";
const DEFAULT_SSL_CERT_PEM_LINK: &str = "/etc/ssl/cert.pem";
const DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK: &str = "/etc/ssl/certs/ca-certificates.crt";
const DEFAULT_SSL_CA_BUNDLE_CRT_LINK: &str = "/etc/ssl/certs/ca-bundle.crt";
const DEFAULT_JAVA_CACERTS_LINK: &str = "/etc/ssl/certs/java/cacerts";
// BLFS make-ca compatibility paths (commonly used by OpenSSL/libgit2 builds on LFS).
const DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.crt";
const DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.trust.crt";
const DEFAULT_PKI_TLS_JAVA_CACERTS_LINK: &str = "/etc/pki/tls/java/cacerts";
const DEFAULT_CERTDATA_URL: &str =
"https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt";
const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt";
const DEFAULT_MOZILLA_TRUST_P11KIT: &str =
"/usr/share/ca-certificates/trust-source/mozilla.trust.p11-kit";
const DEFAULT_PKI_MOZILLA_TRUST_P11KIT: &str = "/etc/pki/anchors/mozilla.trust.p11-kit";
// make-ca uses a pinned ISRG Root X1 to bootstrap downloads from hg.mozilla.org
// before the local trust store exists. We do the same for certdata fetch/sync.
const MOZILLA_HG_BOOTSTRAP_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
"#;
const MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----
"#;
// Top-level CLI entrypoint with `add` and `extract` subcommands.
#[derive(Parser, Debug)]
@@ -194,9 +259,10 @@ struct CertdataSyncArgs {
#[arg(long)]
log_url: Option<String>,
/// Local certdata.txt path used for fetch and convert
#[arg(long, default_value = DEFAULT_CERTDATA_OUTPUT)]
certdata_output: PathBuf,
/// Local certdata.txt path used for fetch and convert.
/// Defaults to a temporary `/tmp/.../certdata.txt` path that is cleaned up.
#[arg(long)]
certdata_output: Option<PathBuf>,
/// Target root filesystem (for chroot/image builds)
#[arg(long, default_value = DEFAULT_ROOT)]
@@ -263,6 +329,24 @@ struct ExtractJob {
relative_dest: &'static str,
}
#[derive(Debug)]
struct TempWorkspaceCleanup {
path: PathBuf,
}
impl Drop for TempWorkspaceCleanup {
fn drop(&mut self) {
if let Err(err) = fs::remove_dir_all(&self.path) {
if err.kind() != std::io::ErrorKind::NotFound {
eprintln!(
"warning: failed to remove temporary directory {}: {err}",
self.path.display()
);
}
}
}
}
// Output set mirrors `/bin/update-ca-trust extract`.
const EXTRACT_JOBS: &[ExtractJob] = &[
ExtractJob {
@@ -308,10 +392,10 @@ const EXTRACT_JOBS: &[ExtractJob] = &[
relative_dest: "java-cacerts.jks",
},
ExtractJob {
format: "pem-directory-hash",
format: "openssl-directory",
filter: "ca-anchors",
purpose: Some("server-auth"),
comment: false,
purpose: None,
comment: true,
relative_dest: "cadir",
},
];
@@ -399,11 +483,14 @@ fn run_certdata(command: CertdataCommands) -> Result<()> {
}
fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> {
let (certdata_output, _temp_workspace_guard) =
resolve_sync_certdata_output(args.certdata_output, args.dry_run)?;
// Reuse the existing subcommand implementations so behavior stays aligned.
run_certdata_fetch(CertdataFetchArgs {
url: args.url,
log_url: args.log_url,
output: args.certdata_output.clone(),
output: certdata_output.clone(),
force: args.force,
no_revision_check: args.no_revision_check,
parse: args.parse,
@@ -411,7 +498,7 @@ fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> {
})?;
run_certdata_convert(CertdataConvertArgs {
input: args.certdata_output,
input: certdata_output,
root: args.root,
output: args.mozilla_output,
force: args.force,
@@ -421,6 +508,60 @@ fn run_certdata_sync(args: CertdataSyncArgs) -> Result<()> {
})
}
fn resolve_sync_certdata_output(
requested: Option<PathBuf>,
dry_run: bool,
) -> Result<(PathBuf, Option<TempWorkspaceCleanup>)> {
if let Some(path) = requested {
return Ok((path, None));
}
if dry_run {
return Ok((build_sync_tmp_workspace_path(0).join(DEFAULT_CERTDATA_OUTPUT), None));
}
let workspace = create_sync_tmp_workspace_dir()?;
println!("Using temporary certdata workspace: {}", workspace.display());
Ok((
workspace.join(DEFAULT_CERTDATA_OUTPUT),
Some(TempWorkspaceCleanup { path: workspace }),
))
}
fn create_sync_tmp_workspace_dir() -> Result<PathBuf> {
let tmp_root = Path::new("/tmp");
for attempt in 0..32_u32 {
let candidate = build_sync_tmp_workspace_path(attempt);
match fs::create_dir(&candidate) {
Ok(()) => return Ok(candidate),
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => continue,
Err(err) => {
return Err(err).with_context(|| {
format!(
"failed to create temporary certdata directory {}",
candidate.display()
)
});
}
}
}
bail!(
"failed to create a unique temporary certdata directory under {}",
tmp_root.display()
);
}
fn build_sync_tmp_workspace_path(attempt: u32) -> PathBuf {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
Path::new("/tmp").join(format!(
"ca-certs-certdata-{}-{nonce:032x}-{attempt:02x}",
std::process::id()
))
}
fn run_gen_artifacts(args: GenArtifactsArgs) -> Result<()> {
let man_path = args.out_dir.join("ca-certs.8");
let completions_dir = args.out_dir.join("completions");
@@ -563,9 +704,7 @@ fn run_certdata_fetch(args: CertdataFetchArgs) -> Result<()> {
return Ok(());
}
let client = reqwest::blocking::Client::builder()
.build()
.context("failed to build HTTP client")?;
let client = build_http_client().context("failed to build HTTP client")?;
let existing_revision = read_certdata_revision_from_path(&args.output)
.ok()
.flatten();
@@ -613,15 +752,217 @@ fn run_certdata_fetch(args: CertdataFetchArgs) -> Result<()> {
}
fn http_get_text(client: &reqwest::blocking::Client, url: &str) -> Result<String> {
let response = client
.get(url)
.send()
.with_context(|| format!("failed to request {url}"))?
let response = match client.get(url).send() {
Ok(response) => response,
Err(err) if is_mozilla_hg_url(url) => {
println!("reqwest fetch failed for Mozilla hg URL; retrying via openssl.");
return openssl_https_get_text(url);
}
Err(err) => return Err(err).with_context(|| format!("failed to request {url}")),
}
.error_for_status()
.with_context(|| format!("server returned an error for {url}"))?;
response.text().context("failed to read response body")
}
fn is_mozilla_hg_url(url: &str) -> bool {
reqwest::Url::parse(url)
.ok()
.and_then(|parsed| parsed.host_str().map(str::to_ascii_lowercase))
.map(|host| host == "hg.mozilla.org" || host == "hg-edge.mozilla.org")
.unwrap_or(false)
}
fn openssl_https_get_text(url: &str) -> Result<String> {
let parsed = reqwest::Url::parse(url).with_context(|| format!("invalid URL: {url}"))?;
if parsed.scheme() != "https" {
bail!("openssl fallback only supports https URLs: {url}");
}
let host = parsed.host_str().context("URL missing host")?;
let port = parsed.port_or_known_default().context("URL missing port")?;
let mut path = parsed.path().to_string();
if path.is_empty() {
path.push('/');
}
if let Some(query) = parsed.query() {
path.push('?');
path.push_str(query);
}
let host_header = match parsed.port() {
Some(p) if p != 443 => format!("{host}:{p}"),
_ => host.to_string(),
};
let bootstrap_ca_path = write_bootstrap_ca_tempfile()?;
let result = (|| {
let mut cmd = Command::new("openssl");
cmd.args([
"s_client",
"-quiet",
"-verify_return_error",
"-verifyCAfile",
bootstrap_ca_path.to_str().unwrap_or(""),
"-connect",
&format!("{host}:{port}"),
"-servername",
host,
]);
if Path::new(DEFAULT_SSL_CERTS_DIR).is_dir() {
cmd.args(["-verifyCApath", DEFAULT_SSL_CERTS_DIR]);
}
cmd.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = cmd.spawn().context("failed to spawn `openssl s_client`")?;
{
let stdin = child
.stdin
.as_mut()
.context("failed to open openssl stdin")?;
write!(
stdin,
"GET {path} HTTP/1.1\r\nHost: {host_header}\r\nConnection: close\r\n\r\n"
)
.context("failed to write HTTP request to openssl stdin")?;
}
let output = child
.wait_with_output()
.context("failed to wait for `openssl s_client`")?;
if !output.status.success() {
bail!(
"openssl s_client failed: {}",
String::from_utf8_lossy(&output.stderr).trim()
);
}
parse_http_response_text(&output.stdout)
.with_context(|| format!("failed to parse HTTP response from openssl for {url}"))
})();
let _ = fs::remove_file(&bootstrap_ca_path);
result
}
fn write_bootstrap_ca_tempfile() -> Result<PathBuf> {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let path = std::env::temp_dir().join(format!(
"ca-certs-hg-bootstrap-{}-{nonce}.pem",
std::process::id()
));
let bundle = format!(
"{}{}",
MOZILLA_HG_BOOTSTRAP_ROOT_PEM, MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM
);
fs::write(&path, bundle)
.with_context(|| format!("failed to write {}", path.display()))?;
#[cfg(unix)]
{
fs::set_permissions(&path, fs::Permissions::from_mode(0o600))
.with_context(|| format!("failed to set permissions on {}", path.display()))?;
}
Ok(path)
}
fn parse_http_response_text(raw: &[u8]) -> Result<String> {
let (header_bytes, body_bytes) = split_http_response(raw)?;
let header_text = String::from_utf8_lossy(header_bytes);
let mut header_lines = header_text.lines();
let status_line = header_lines.next().context("missing HTTP status line")?;
let status_code = status_line
.split_whitespace()
.nth(1)
.context("missing HTTP status code")?
.parse::<u16>()
.context("invalid HTTP status code")?;
if !(200..300).contains(&status_code) {
bail!("HTTP request failed with status {status_code}");
}
let chunked = header_lines.any(|line| {
line.split_once(':')
.map(|(name, value)| {
name.eq_ignore_ascii_case("transfer-encoding")
&& value.to_ascii_lowercase().contains("chunked")
})
.unwrap_or(false)
});
let body = if chunked {
decode_http_chunked_body(body_bytes)?
} else {
body_bytes.to_vec()
};
String::from_utf8(body).context("HTTP response body was not UTF-8")
}
fn split_http_response(raw: &[u8]) -> Result<(&[u8], &[u8])> {
if let Some(idx) = raw.windows(4).position(|w| w == b"\r\n\r\n") {
return Ok((&raw[..idx], &raw[idx + 4..]));
}
if let Some(idx) = raw.windows(2).position(|w| w == b"\n\n") {
return Ok((&raw[..idx], &raw[idx + 2..]));
}
bail!("HTTP response missing header/body separator")
}
fn decode_http_chunked_body(mut input: &[u8]) -> Result<Vec<u8>> {
let mut out = Vec::new();
loop {
let (line, rest) = take_http_line(input).context("truncated chunk header")?;
let size_hex = line.split(';').next().unwrap_or("").trim();
let size = usize::from_str_radix(size_hex, 16)
.with_context(|| format!("invalid chunk size `{size_hex}`"))?;
input = rest;
if size == 0 {
break;
}
if input.len() < size {
bail!("truncated chunk body");
}
out.extend_from_slice(&input[..size]);
input = &input[size..];
if input.starts_with(b"\r\n") {
input = &input[2..];
} else if input.starts_with(b"\n") {
input = &input[1..];
} else {
bail!("missing chunk terminator");
}
}
Ok(out)
}
fn take_http_line(input: &[u8]) -> Option<(String, &[u8])> {
if let Some(idx) = input.windows(2).position(|w| w == b"\r\n") {
let line = String::from_utf8(input[..idx].to_vec()).ok()?;
return Some((line, &input[idx + 2..]));
}
if let Some(idx) = input.iter().position(|b| *b == b'\n') {
let line = String::from_utf8(input[..idx].to_vec()).ok()?;
return Some((line, &input[idx + 1..]));
}
None
}
fn build_http_client() -> Result<reqwest::blocking::Client> {
let bootstrap_ca = reqwest::Certificate::from_pem(MOZILLA_HG_BOOTSTRAP_ROOT_PEM.as_bytes())
.context("failed to parse bundled Mozilla hg bootstrap root certificate")?;
let digicert_g2_ca =
reqwest::Certificate::from_pem(MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM.as_bytes())
.context("failed to parse bundled DigiCert Global Root G2 bootstrap certificate")?;
reqwest::blocking::Client::builder()
.add_root_certificate(bootstrap_ca)
.add_root_certificate(digicert_g2_ca)
.build()
.context("failed to construct reqwest client")
}
fn derive_hg_log_url(raw_url: &str) -> Option<String> {
if raw_url.contains("/raw-file/") {
Some(raw_url.replacen("/raw-file/", "/log/", 1))
@@ -759,10 +1100,17 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> {
);
}
println!("[dry-run] Would write {}", output_host.display());
if should_write_make_ca_mirror(&args.root, &output_target) {
println!(
"[dry-run] Would also write BLFS make-ca compatibility source {}",
path_in_root(&args.root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)).display()
);
}
} else {
let parsed = parsed.context("certdata parse result missing")?;
let p11kit = convert_certdata_to_p11kit_bundle(&parsed)?;
install_file(&output_host, p11kit.as_bytes(), args.force, false)?;
maybe_install_make_ca_mozilla_mirror(&args.root, &output_target, &p11kit, args.force)?;
}
if args.no_extract {
@@ -774,10 +1122,57 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> {
.extract_output
.unwrap_or_else(|| PathBuf::from(DEFAULT_EXTRACTED_DIR));
extract_trust(&args.root, &extract_output, args.dry_run)?;
if !args.dry_run {
ensure_nonempty_extraction_outputs(&args.root, &extract_output)?;
}
println!("certdata conversion and extraction complete.");
Ok(())
}
fn should_write_make_ca_mirror(root: &Path, output_target: &Path) -> bool {
output_target == Path::new(DEFAULT_MOZILLA_TRUST_P11KIT)
&& path_in_root(root, Path::new("/etc/pki")).exists()
}
fn maybe_install_make_ca_mozilla_mirror(
root: &Path,
output_target: &Path,
p11kit: &str,
force: bool,
) -> Result<()> {
if !should_write_make_ca_mirror(root, output_target) {
return Ok(());
}
let mirror_host = path_in_root(root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT));
println!(
"Installing BLFS make-ca compatibility trust source: {}",
mirror_host.display()
);
install_file(&mirror_host, p11kit.as_bytes(), force, false)
}
fn ensure_nonempty_extraction_outputs(root: &Path, extract_output: &Path) -> Result<()> {
let tls_bundle = path_in_root(root, &extract_output.join("tls-ca-bundle.pem"));
let trust_bundle = path_in_root(root, &extract_output.join("ca-bundle.trust.crt"));
let cadir = path_in_root(root, &extract_output.join("cadir"));
let tls_size = fs::metadata(&tls_bundle).map(|m| m.len()).unwrap_or(0);
let trust_size = fs::metadata(&trust_bundle).map(|m| m.len()).unwrap_or(0);
let cadir_count = fs::read_dir(&cadir).map(|it| it.count()).unwrap_or(0);
if tls_size == 0 || trust_size == 0 || cadir_count == 0 {
bail!(
"trust extract produced empty outputs (tls-ca-bundle.pem={} bytes, ca-bundle.trust.crt={} bytes, cadir entries={}); p11-kit may be reading a different trust source layout (e.g. BLFS make-ca uses /etc/pki/anchors)",
tls_size,
trust_size,
cadir_count
);
}
Ok(())
}
fn count_cert_and_trust_objects(doc: &CertDataDocument) -> (usize, usize) {
let mut certs = 0usize;
let mut trusts = 0usize;
@@ -1439,10 +1834,18 @@ fn run_trust_extract_job(
fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool) -> Result<()> {
let ssl_certs_host = path_in_root(root, Path::new(DEFAULT_SSL_CERTS_DIR));
let cert_pem_link_host = path_in_root(root, Path::new(DEFAULT_SSL_CERT_PEM_LINK));
let ca_bundle_link_host =
path_in_root(root, Path::new(DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK));
let ca_bundle_crt_link_host = path_in_root(root, Path::new(DEFAULT_SSL_CA_BUNDLE_CRT_LINK));
let java_link_host = path_in_root(root, Path::new(DEFAULT_JAVA_CACERTS_LINK));
let pki_ca_bundle_crt_link_host =
path_in_root(root, Path::new(DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK));
let pki_ca_bundle_trust_crt_link_host =
path_in_root(root, Path::new(DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK));
let pki_java_link_host = path_in_root(root, Path::new(DEFAULT_PKI_TLS_JAVA_CACERTS_LINK));
let tls_bundle_src_host = path_in_root(root, &extracted_target.join("tls-ca-bundle.pem"));
let trust_bundle_src_host = path_in_root(root, &extracted_target.join("ca-bundle.trust.crt"));
let cadir_host = path_in_root(root, &extracted_target.join("cadir"));
let java_src_host = path_in_root(root, &extracted_target.join("java-cacerts.jks"));
@@ -1459,16 +1862,41 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool)
"[dry-run] Would delete broken symlinks in {}",
ssl_certs_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
cert_pem_link_host.display(),
tls_bundle_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
ca_bundle_link_host.display(),
tls_bundle_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
ca_bundle_crt_link_host.display(),
tls_bundle_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
java_link_host.display(),
java_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
pki_ca_bundle_crt_link_host.display(),
tls_bundle_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
pki_ca_bundle_trust_crt_link_host.display(),
trust_bundle_src_host.display()
);
println!(
"[dry-run] Would link {} -> {}",
pki_java_link_host.display(),
java_src_host.display()
);
return Ok(());
}
@@ -1479,6 +1907,21 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool)
.context("java cacerts link path has no parent")?;
fs::create_dir_all(java_parent)
.with_context(|| format!("failed to create {}", java_parent.display()))?;
let pki_ca_bundle_parent = pki_ca_bundle_crt_link_host
.parent()
.context("pki ca-bundle.crt link path has no parent")?;
fs::create_dir_all(pki_ca_bundle_parent)
.with_context(|| format!("failed to create {}", pki_ca_bundle_parent.display()))?;
let pki_ca_bundle_trust_parent = pki_ca_bundle_trust_crt_link_host
.parent()
.context("pki ca-bundle.trust.crt link path has no parent")?;
fs::create_dir_all(pki_ca_bundle_trust_parent)
.with_context(|| format!("failed to create {}", pki_ca_bundle_trust_parent.display()))?;
let pki_java_parent = pki_java_link_host
.parent()
.context("pki java cacerts link path has no parent")?;
fs::create_dir_all(pki_java_parent)
.with_context(|| format!("failed to create {}", pki_java_parent.display()))?;
// Link every extracted hash file into `/etc/ssl/certs`, then prune stale links.
for entry in fs::read_dir(&cadir_host)
@@ -1491,8 +1934,13 @@ fn sync_system_ssl_symlinks(root: &Path, extracted_target: &Path, dry_run: bool)
}
remove_broken_symlinks(&ssl_certs_host)?;
replace_symlink(&tls_bundle_src_host, &cert_pem_link_host)?;
replace_symlink(&tls_bundle_src_host, &ca_bundle_link_host)?;
replace_symlink(&tls_bundle_src_host, &ca_bundle_crt_link_host)?;
replace_symlink(&java_src_host, &java_link_host)?;
replace_symlink(&tls_bundle_src_host, &pki_ca_bundle_crt_link_host)?;
replace_symlink(&trust_bundle_src_host, &pki_ca_bundle_trust_crt_link_host)?;
replace_symlink(&java_src_host, &pki_java_link_host)?;
Ok(())
}
@@ -1866,6 +2314,34 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
assert_eq!(bytes, b"ABC");
}
#[test]
fn sync_certdata_output_respects_explicit_path() {
let requested = PathBuf::from("/var/tmp/custom-certdata.txt");
let (resolved, cleanup) = resolve_sync_certdata_output(Some(requested.clone()), false).unwrap();
assert_eq!(resolved, requested);
assert!(cleanup.is_none());
}
#[test]
fn sync_certdata_output_uses_tmp_workspace_and_cleans_it() {
let (resolved, cleanup) = resolve_sync_certdata_output(None, false).unwrap();
assert!(resolved.starts_with(Path::new("/tmp")));
assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT)));
let workspace = resolved.parent().unwrap().to_path_buf();
assert!(workspace.exists());
drop(cleanup);
assert!(!workspace.exists());
}
#[test]
fn sync_certdata_output_dry_run_uses_tmp_path_without_creating_workspace() {
let (resolved, cleanup) = resolve_sync_certdata_output(None, true).unwrap();
assert!(resolved.starts_with(Path::new("/tmp")));
assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT)));
assert!(cleanup.is_none());
}
#[test]
fn percent_encode_bytes_encodes_lower_hex() {
assert_eq!(percent_encode_bytes(&[0x00, 0x2a, 0xff]), "%00%2a%ff");