From 270db6d6713d2c6c14d9df0b4bc7662843d3d54e Mon Sep 17 00:00:00 2001 From: JDX50S <71799746+smoke-wolf@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:28:31 -0300 Subject: [PATCH] Merge commit from fork * security: enable MAR signature verification for updates Remove `--enable-unverified-updates` from the common mozconfig. This flag was disabling all MAR (Mozilla ARchive) signature verification in the updater binary, meaning update packages were applied without any cryptographic authenticity check. With this flag removed, the Mozilla build system will: - Link NSS and signmar into the updater binary - Enable SecVerifyTransformCreate-based signature verification on macOS - Require MAR files to contain valid signatures before applying REQUIRED FOLLOW-UP (maintainer action): 1. Generate a Zen-specific MAR signing keypair (RSA-PKCS1-SHA384) See: https://firefox-source-docs.mozilla.org/build/buildsystem/mar.html 2. Place the public key DER file(s) in the source tree at toolkit/mozapps/update/updater/release_primary.der 3. Sign MAR files during the release build with the private key 4. Set ACCEPTED_MAR_CHANNEL_IDS in update-settings.ini to restrict which update channels the updater will accept Ref: GHSA-qpj9-m8jc-mw6q * no-bug: Added signature steps * no-bug: Export browser/installer/package-manifest.in --------- Co-authored-by: Maliq Barnard Co-authored-by: Mr. M --- .github/workflows/build.yml | 14 ++ .github/workflows/linux-release-build.yml | 8 + .github/workflows/src/release-build.sh | 2 + .gitignore | 7 + build/signing/public_key.der | Bin 0 -> 550 bytes configs/common/mozconfig | 4 +- scripts/mar_sign.sh | 165 ++++++++++++++++++ .../installer/package-manifest-in.patch | 14 +- .../update/updater/updater-common-build.patch | 2 +- 9 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 build/signing/public_key.der create mode 100644 scripts/mar_sign.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 607470d6f..07203a4a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -505,6 +505,20 @@ jobs: run: | git clone https://github.com/zen-browser/windows-binaries.git .github/workflows/object --depth 1 + - name: Download signmar-linux-x86_64 from artifacts + uses: actions/download-artifact@v4 + with: + name: signmar-linux-x86_64 + + - name: Sign MAR files + env: + SIGNMAR: ${{ github.workspace }}/signmar-linux-x86_64 + ZEN_MAR_SIGNING_PASSWORD: ${{ secrets.ZEN_MAR_SIGNING_PASSWORD }} + ZEN_SIGNING_CERT_PEM_BASE64: ${{ secrets.ZEN_SIGNING_CERT_PEM_BASE64 }} + ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64: ${{ secrets.ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 }} + run: | + bash scripts/mar_sign.sh -s + - name: Copy update manifests env: RELEASE_BRANCH: ${{ inputs.update_branch }} diff --git a/.github/workflows/linux-release-build.yml b/.github/workflows/linux-release-build.yml index 93c6a70da..2ce7906cc 100644 --- a/.github/workflows/linux-release-build.yml +++ b/.github/workflows/linux-release-build.yml @@ -173,3 +173,11 @@ jobs: retention-days: 5 name: linux_update_manifest_${{ matrix.arch }} path: ./dist/update + + - name: Upload signmar + if: ${{ matrix.arch == 'x86_64' }} + uses: actions/upload-artifact@v4 + with: + retention-days: 2 + name: signmar-linux-x86_64 + path: engine/obj-x86_64-pc-linux-gnu/dist/bin/signmar diff --git a/.github/workflows/src/release-build.sh b/.github/workflows/src/release-build.sh index e553f3942..4a522473d 100644 --- a/.github/workflows/src/release-build.sh +++ b/.github/workflows/src/release-build.sh @@ -10,6 +10,8 @@ fi . $HOME/.cargo/env +sh ./scripts/mar_sign.sh -i + ulimit -n 4096 if command -v Xvfb &> /dev/null; then diff --git a/.gitignore b/.gitignore index 716fd810f..67b3b7ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,10 @@ locales/firefox-l10n/ .DS_Store mozconfig + +build/signing/env/ +build/signing/nss_config/ +build/signing/cert.pem +build/signing/private_key.pem +build/signing/private_key.p12 + diff --git a/build/signing/public_key.der b/build/signing/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..68994169095e588fc3ab481756409d8c543063fd GIT binary patch literal 550 zcmV+>0@?jAf&wBi4F(A+hDe6@4FLfG1potr0uKN%f&vNxf&u{mu!H(v{h`^QUGD>Z zOB__5qzb75F6PP4u0GgpjsyzUMwQ0yOUlneEURRIFE2~Uevmq?g}GgXYOkGVrO7*- zr?E!)cLOL$rc3#(?YkK%NI>QoU5=y^S2+{*G#Ygv^(@+>*AfrB_f0kl$KvaJ?) za|gFO;VH*s4GF8-_~rAUOzE!E{=z3|slp=ish%(ygOjAW=mXATMaBwm+wh+!o$N?n zH02rNp4BCznTqNAh_XSyxV1TZ{LGA!-|^gjBvL>AC@H=;&a9Gntv76~{C|}USMXpV z_XAAV4g-++Fio=PKNf-+-=aC&d{^l}MMt}{VlRHNeAe!>e$9$;+XMYldMB&0XbH_O zGAldQt1-@V2?$3M=z`0KEG8f9ST0*V8`5@UW{_2ZsR9y*040Sc#&!eaI%S?}y8q2p ziWh5~hwn1fx+UA!?mku3Y^_J0p zUxVpEyeP-%*I{%eOjr!~C@_g-QKcR2*>(2iwpjY#dpAyb$)K+0BEYmE)D9*qkl8I< o=z2%9P3JsgXO6Hx@O7qRa3WF_FO{4xPV_{|6YIc10s{d60o|MlYXATM literal 0 HcmV?d00001 diff --git a/configs/common/mozconfig b/configs/common/mozconfig index 3d41bc83a..16a51385a 100644 --- a/configs/common/mozconfig +++ b/configs/common/mozconfig @@ -48,6 +48,8 @@ if test "$ZEN_RELEASE"; then ac_add_options --enable-optimize + ac_add_options --enable-verify-mar + ac_add_options --enable-release ac_add_options --disable-debug ac_add_options --disable-debug-symbols @@ -89,8 +91,6 @@ if test "$ZEN_RELEASE"; then ac_add_options --enable-replace-malloc fi -ac_add_options --enable-unverified-updates - ac_add_options --enable-jxl ac_add_options --with-unsigned-addon-scopes=app,system diff --git a/scripts/mar_sign.sh b/scripts/mar_sign.sh new file mode 100644 index 000000000..2dfbfbc6c --- /dev/null +++ b/scripts/mar_sign.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +set -e + +CERT_PATH_DIR=build/signing +UPDATER_CERT_DIR="engine/toolkit/mozapps/update/updater" +NSS_CONFIG_DIR="$CERT_PATH_DIR/nss_config" + +generate_certs() { + mkdir temp + cd temp + + # 1. Generate private key + openssl genrsa -out private_key.pem 4096 + + # 2. Generate self-signed certificate (required for PKCS#12 bundling) + openssl req -new -x509 \ + -key private_key.pem \ + -out cert.pem \ + -subj "/CN=MAR Signing" + + # 3. Export public key as SPKI DER (for embedding in updater) + openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der + + cd .. + mkdir -p "$CERT_PATH_DIR" + mv temp/private_key.pem "$CERT_PATH_DIR"/private_key.pem + mv temp/cert.pem "$CERT_PATH_DIR"/cert.pem + mv temp/public_key.der "$CERT_PATH_DIR"/public_key.der + + mkdir -p "$CERT_PATH_DIR/env" + base64 -w 0 "$CERT_PATH_DIR"/cert.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_CERT_PEM_BASE64 + base64 -w 0 "$CERT_PATH_DIR"/private_key.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 + + # Verify public key + openssl rsa -in "$CERT_PATH_DIR"/public_key.der \ + -pubin -inform DER -text -noout + + rm -rf temp +} + +import_cert() { + if [ ! -f "$CERT_PATH_DIR/public_key.der" ]; then + echo "Error: public_key.der not found. Run with -g first." >&2 + exit 1 + fi + echo "Importing certificate into $UPDATER_CERT_DIR/release_primary.der" + cp "$CERT_PATH_DIR/public_key.der" "$UPDATER_CERT_DIR/release_primary.der" + echo "Importing certificate into $UPDATER_CERT_DIR/release_secondary.der" + cp "$CERT_PATH_DIR/public_key.der" "$UPDATER_CERT_DIR/release_secondary.der" + echo "Done. Rebuild the updater to embed the new certificate." +} + +create_nss_config_dir() { + rm -rf "$NSS_CONFIG_DIR" + mkdir "$NSS_CONFIG_DIR" + + if [ -z "$ZEN_MAR_SIGNING_PASSWORD" ]; then + echo "Warning: ZEN_MAR_SIGNING_PASSWORD environment variable not set. Using empty password." >&2 + ZEN_MAR_SIGNING_PASSWORD="" + fi + + password_file="$NSS_CONFIG_DIR/password.txt" + echo "$ZEN_MAR_SIGNING_PASSWORD" > "$password_file" + + if [ "$ZEN_SIGNING_CERT_PEM_BASE64" ]; then + echo "Decoding signing certificate from ZEN_SIGNING_CERT_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_CERT_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/cert.pem" + fi + + if [ "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" ]; then + echo "Decoding signing private key from ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/private_key.pem" + fi + + echo "Generating NSS config directory at $NSS_CONFIG_DIR" + certutil -N -d "$NSS_CONFIG_DIR" -f "$password_file" + + echo "Wrapping private key into PKCS#12..." + echo "Wrapping key + cert into PKCS#12..." + openssl pkcs12 -export \ + -inkey "$CERT_PATH_DIR/private_key.pem" \ + -in "$CERT_PATH_DIR/cert.pem" \ + -name "private_key" \ + -passout pass:"$ZEN_MAR_SIGNING_PASSWORD" \ + -out "$CERT_PATH_DIR/private_key.p12" + + echo "Importing PKCS#12 into NSS database..." + pk12util \ + -i "$CERT_PATH_DIR/private_key.p12" \ + -d "$NSS_CONFIG_DIR" \ + -W "$ZEN_MAR_SIGNING_PASSWORD" \ + -K "$ZEN_MAR_SIGNING_PASSWORD" +} + +cleanup_certs() { + rm -rf "$NSS_CONFIG_DIR" + rm -rf "$CERT_PATH_DIR/env" + + rm -f "$CERT_PATH_DIR/private_key.p12" + rm -f "$CERT_PATH_DIR/private_key.pem" + rm -f "$CERT_PATH_DIR/cert.pem" +} + +sign_mars() { + if [ ! -f "$SIGNMAR" ]; then + echo "Error: signmar not found at $SIGNMAR. Build the engine first." >&2 + exit 1 + fi + + create_nss_config_dir + + folders=( + linux.mar + linux-aarch64.mar + windows.mar + windows-arm64 + macos.mar + ) + # each folder will contain the .mar files for that platform, and the signature will be written in-place + for folder in "${folders[@]}"; do + if [ -d "$folder" ]; then + for mar_file in "$folder"/*.mar; do + if [ -f "$mar_file" ]; then + echo "" + echo "Signing $mar_file..." + # mar [-C workingDir] -d NSSConfigDir -n certname -s archive.mar out_signed_archive.mar + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "private_key" -s "$mar_file" "$mar_file".signed + echo "Signed $mar_file. Verifying signature..." + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "private_key" -v "$mar_file".signed + mv "$mar_file".signed "$mar_file" + echo "Successfully signed $mar_file" + else + echo "No .mar files found in $folder, skipping." + fi + done + else + echo "Directory $folder not found, skipping." + fi + done + + cleanup_certs +} + +case "$1" in + -g) + generate_certs + ;; + -i) + import_cert + ;; + -s) + sign_mars + ;; + *) + echo "Usage: $0 [-g] [-i] [-s]" >&2 + echo " -g Generate MAR signing certificates" >&2 + echo " -i Import the certificate into the updater (release_primary.der)" >&2 + echo " -s Sign *.mar files in the current directory in-place" >&2 + exit 1 + ;; +esac diff --git a/src/browser/installer/package-manifest-in.patch b/src/browser/installer/package-manifest-in.patch index 3aec7d9df..f07fcc3ac 100644 --- a/src/browser/installer/package-manifest-in.patch +++ b/src/browser/installer/package-manifest-in.patch @@ -1,18 +1,8 @@ diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..951889b156498f66118d67d4245aca649e27a3a2 100644 +index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..b6bf327bf286de5246e5c50d89519d16d5771caf 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in -@@ -378,17 +378,17 @@ bin/libfreebl_64int_3.so - ; [MaintenanceService] - ; - #ifdef MOZ_MAINTENANCE_SERVICE --@BINPATH@/maintenanceservice.exe --@BINPATH@/maintenanceservice_installer.exe -+;@BINPATH@/maintenanceservice.exe -+;@BINPATH@/maintenanceservice_installer.exe - #endif - - ; [Crash Reporter] +@@ -386,9 +386,9 @@ bin/libfreebl_64int_3.so ; #ifdef MOZ_CRASHREPORTER #ifdef XP_MACOSX diff --git a/src/toolkit/mozapps/update/updater/updater-common-build.patch b/src/toolkit/mozapps/update/updater/updater-common-build.patch index 03390b4c7..ee8ed45d6 100644 --- a/src/toolkit/mozapps/update/updater/updater-common-build.patch +++ b/src/toolkit/mozapps/update/updater/updater-common-build.patch @@ -7,7 +7,7 @@ index 57ea0415653678bb300e277e66c97e332e756cc7..5f757e68a685948c83fee8f963c49bee "signmar", ] + if CONFIG["OS_ARCH"] == "Linux": -+ # Zen: --enable-unverified-updates is enabled, the RPATH is not added ++ # Zen: ensure RPATH is set so NSS/signmar libs are found at runtime + OS_LIBS += [ + "-Wl,-rpath=\\$$ORIGIN", + ]