diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index 3321b7f5f46b..545e161f8eee 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -41,14 +41,6 @@ development process and source code documentation. debugging/* -.. toctree:: - :caption: Signing - :maxdepth: 1 - :glob: - - signing/* - - .. toctree:: :caption: Additional Information :maxdepth: 1 diff --git a/docs/contributing/signing/signing_macos_build.rst b/docs/contributing/signing/signing_macos_build.rst deleted file mode 100644 index ee0ddf9080a2..000000000000 --- a/docs/contributing/signing/signing_macos_build.rst +++ /dev/null @@ -1,154 +0,0 @@ -Signing Local macOS Builds -========================== - -Background ----------- -Firefox for macOS is mainly signed in one of two different ways: one for -production releases, and one for builds that run on try. Typically, developers -testing local builds don’t need to be concerned with signing unless they are -working on an area specifically affected by macOS entitlements such as passkeys, -loading third party libraries, or adding a new process type. However, it is -good practice to test builds that are as close as possible to the production -configuration or the try server configuration. Local builds are not signed -automatically and mach doesn’t include support for running tests on signed -builds. However, the mach command ``macos-sign`` can be used to sign local -packaged builds for manual testing. ``macos-sign`` supports signing builds to -match try or production builds. - - Note: On Apple Silicon Macs, where all executables are required to be - signed, Firefox binaries will be “ad-hoc” self-signed automatically by the - linker during compilation, but this is a per-binary sign with no - Firefox-specific runtime settings or entitlements. This document ignores - automatic signing. - -To sign your own local build so that it has the same set of entitlements as a -production release requires a signing certificate and provisioning profile -issued from Mozilla’s Apple Developer account. Entitlements are used to grant -Firefox certain permissions as well as impose security restrictions when it is -run on macOS. Some entitlements are considered restricted and can only be -enabled when signed by a certificate from an account that has been granted -permission to use the entitlement, such as the passkey macOS entitlement. As an -example of the production restrictions, production builds use an entitlement -that disallows debugger attachment. Disallowing debuggers is required for -distribution because it is required by the Notarization system. When signing -locally, developers can modify entitlements as needed. - -Summary -------- - -**Production build:** requires Mozilla's official Apple Developer ID -certificate, private key, and provisioning profile. Only Mozilla Release -Engineering has access to the official Developer ID certificate and private key. -Mozilla developers can request a limited-use Apple *Developer* certificate which -can be used to generated production-like builds for local testing. See -:ref:`like-prod` below. - -**Developer build:** requires generating a self-signed certificate (to sign -like a try push is signed) or using ad-hoc signing (no setup required and only -usable locally), will have no passkey support (or any other restricted -entitlement), has fewer restrictions with respect to module loading, is -debuggable. - -Signing Your Build Like try ---------------------------- -To sign your own local build with entitlements that match what is used for try -push automated testing, generate a self-signed code signing certificate using -the macOS Keychain Access application. During the process, supply a unique name -not used by other Keychain entries making sure to not use any spaces. For -example, ``my-firefox-selfsign-cert-2024``. This string will be used as -the signing identity and passed to ``macos-sign`` with the ``-s`` option. -``./mach`` passes this to the codesign command which looks up the entry in the -keychain. When running the signing command, you'll be prompted to allow -``codesign`` to access the keychain entry. Select ``Always Allow`` when -prompted. - -.. code-block:: shell - - $ ./mach build package - $ open - - $ ./mach macos-sign -s my-firefox-selfsign-cert-2024 -a ~/Desktop/Nightly.app - -The entitlements in the tree used for this configuration are labeled as -developer and we call this the developer build. Developer signed builds differ -from production signed in the following ways: - -* They allow debugger attachment -* They allow loading of third party libraries in all processes -* They respect dyld environment variables -* They don’t include restricted entitlements such as the passkey entitlement - (and therefore passkeys can’t be used) - -Ad-hoc Signing Your Build - Like try Signing, but Requires no Configuration and is For Local Use Only ------------------------------------------------------------------------------------------------------ -Omitting the ``-s`` option will use ad-hoc signing which requires no setup. The -build will be more limited than builds signed with a self-signed cert. Ad-hoc -signed builds are not verifiable or runnable on any other system. There is -little documentation available about the limitations. - -.. code-block:: shell - - $ ./mach build package - $ open - - $ ./mach macos-sign -a ~/Desktop/Nightly.app - -.. _like-prod: -Signing Your Build Like Production ----------------------------------- -To sign your local build like a production Firefox build, you’ll need an Apple -Developer signing certificate and provisioning profile issued from Mozilla's -Apple Developer account. Developers will be given a development-role login -allowing a signing certificate and provisioning profile to be generated. The -provisioning profile used with Development certificates limits the signed -application to Mozilla developer machines via a hardware ID. Employees can file -a bug `here `__ -to request an account. Once the developer's Apple account is setup as a member -of Mozilla's Apple account, Xcode can be used to download a Developer signing -certificate and provisioning profile for development use. Use Keychain Access to -get the codesigning identifier for your development cert which should be passed -as the ``-s`` codesigning identity to ``mach macos-sign``: - -.. code-block:: shell - - $ ./mach build package - $ open - - $ ./mach macos-sign -a ~/Desktop/Nightly.app -s - -Example: Re-Signing Official Nightly ------------------------------------- - -.. code-block:: shell - - $ ditto /Applications/Firefox\ Nightly.app ~/Desktop/FirefoxNightly.app - $ ./mach macos-sign -a ~/Desktop/FirefoxNightly.app - 0:00.20 Using ad-hoc signing identity - 0:00.20 Using nightly channel signing configuration - 0:00.20 Using developer entitlements - 0:00.20 Reading build config file /Users/me/r/mc/taskcluster/ci/config.yml - 0:00.23 Stripping existing xattrs and signatures - 0:01.91 Signing with codesign - 0:02.72 Verification of signed app /Users/me/Desktop/FirefoxNightly.app OK - -Example: Re-Signing Official Developer Edition With `rcodesign `__ Using a pkcs12 Certificate Key Pair ------------------------------------------------------------------------------------------------------------------------------------------------ - -More information about rcodesign can be found on the -`rust crate page `__ or -`github repo `__. Certificates -can be exported from Keychain Access in .p12 format. - -.. code-block:: shell - - $ ditto /Applications/Firefox\ Developer\ Edition.app/ ~/Desktop/DevEdition.app - $ ./mach macos-sign -r -a ~/Desktop/DevEdition.app \ - --rcodesign-p12-file ./myDevId.p12 \ - --rcodesign-p12-password-file ./myDevId.p12.passwd - 0:00.26 Using pkcs12 signing identity - 0:00.26 Using devedition channel signing configuration - 0:00.26 Using developer entitlements - 0:00.26 Reading build config file /Users/me/r/mc/taskcluster/ci/config.yml - 0:00.29 Stripping existing xattrs and signatures - 0:02.09 Signing with rcodesign - 0:11.16 Verification of signed app /Users/me/Desktop/DevEdition.app OK diff --git a/python/mach/mach/command_util.py b/python/mach/mach/command_util.py index 1c2b064f84f9..741539f6f64b 100644 --- a/python/mach/mach/command_util.py +++ b/python/mach/mach/command_util.py @@ -134,7 +134,6 @@ MACH_COMMANDS = { "mach-debug-commands": MachCommandReference( "python/mach/mach/commands/commandinfo.py" ), - "macos-sign": MachCommandReference("tools/signing/macos/mach_commands.py"), "manifest": MachCommandReference("testing/mach_commands.py"), "marionette-test": MachCommandReference("testing/marionette/mach_commands.py"), "mochitest": MachCommandReference("testing/mochitest/mach_commands.py", ["test"]), diff --git a/tools/signing/macos/mach_commands.py b/tools/signing/macos/mach_commands.py deleted file mode 100644 index 543faca2ea00..000000000000 --- a/tools/signing/macos/mach_commands.py +++ /dev/null @@ -1,675 +0,0 @@ -# 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/. - -import glob -import logging -import os -import os.path -import plistlib -import subprocess -import sys -import tempfile - -import yaml -from mach.decorators import ( - Command, - CommandArgument, - CommandArgumentGroup, -) -from mozbuild.base import MachCommandConditions as conditions - - -@Command( - "macos-sign", - category="misc", - description="Sign a built and packaged (./mach build package) Firefox " - "bundle on macOS. Limitations: 1) macos-sign doesn't support building " - "the .app built in the object dir in-place (for now) because it contains " - 'symlinks. First use "./mach build package" to build a .dmg containing a ' - "bundled .app. Then extract the .app from the dmg to a readable/writable " - "directory. The bundled app can be signed with macos-sign (using the -a " - "argument). 2) The signing configuration (which maps files in the .app " - "to entitlement files in the tree to be used when signing) is loaded from " - "the build configuration in the local repo. For example, when signing a " - "Release 120 build using mach from a revision of mozilla-central, " - "macos-sign will use the bundle ID to determine the signing should use the " - "Release channel entitlements, but the configuration used will be the " - "Release configuration as defined in the repo working directory, not the " - "configuration from the revision of the earlier 120 build.", - conditions=[conditions.is_firefox], -) -@CommandArgument( - "-v", - "--verbose", - default=False, - action="store_true", - dest="verbose_arg", - help="Verbose output including the commands executed.", -) -# The app path could be a required positional argument, but let's reserve that -# for the future where the default will be to sign the locally built .app -# in-place in the object dir. -@CommandArgument( - "-a", - "--app-path", - required=True, - type=str, - dest="app_arg", - help="Path to the .app bundle to sign. This can not be the .app built " - "in the object dir because it contains symlinks and is unbundled. Use " - "an app generated with ./mach build package.", -) -@CommandArgument( - "-s", - "--signing-identity", - metavar="SIGNING_IDENTITY", - default=None, - type=str, - dest="signing_identity_arg", - help="The codesigning identity to be used when signing with the macOS " - "native codesign tool. By default ad-hoc self-signing will be used. ", -) -@CommandArgument( - "-e", - "--entitlements", - default="developer", - choices=["developer", "production", "production-without-restricted"], - type=str, - dest="entitlements_arg", - help="Whether to sign the build for development or production use. " - "By default, a developer signing is performed. This does not require " - "a certificate to be configured. Developer entitlements are limited " - "to be compatible with self-signing and to allow debugging. Production " - "entitlements require a valid Apple Developer ID certificate issued from " - "the organization's Apple Developer account (without one, signing will " - "succeed, but the build will not be usable) and a provisioning profile to " - "be added to the bundle or installed in macOS System Preferences. The " - "certificate may be a 'development' certificate issued by the account. The " - "Apple Developer account must have the necessary restricted entitlements " - "granted in order for the signed build to work correctly. Use " - "production-without-restricted if you have a Developer ID certificate " - "not associated with an account approved for the restricted entitlements.", -) -@CommandArgument( - "-c", - "--channel", - default="auto", - choices=["auto", "nightly", "devedition", "beta", "release"], - dest="channel_arg", - type=str, - help="Which channel build is being signed.", -) -@CommandArgumentGroup("rcodesign") -@CommandArgument( - "-r", - "--use_rcodesign", - group="rcodesign", - default=False, - dest="use_rcodesign_arg", - action="store_true", - help="Enables signing with rcodesign instead of codesign. With rcodesign, " - "only ad-hoc and pkcs12 signing is supported. To use a signing identity, " - "specify a pkcs12 file and password file. See rcodesign documentation for " - "more information.", -) -@CommandArgument( - "-f", - "--rcodesign-p12-file", - group="rcodesign", - metavar="RCODESIGN_P12_FILE_PATH", - default=None, - type=str, - dest="p12_file_arg", - help="The rcodesign pkcs12 file, passed to rcodesign without validation.", -) -@CommandArgument( - "-p", - "--rcodesign-p12-password-file", - group="rcodesign", - metavar="RCODESIGN_P12_PASSWORD_FILE_PATH", - default=None, - type=str, - dest="p12_password_file_arg", - help="The rcodesign pkcs12 password file, passed to rcodesign without " - "validation.", -) -def macos_sign( - command_context, - app_arg, - signing_identity_arg, - entitlements_arg, - channel_arg, - use_rcodesign_arg, - p12_file_arg, - p12_password_file_arg, - verbose_arg, -): - """Signs a .app build with entitlements from the repo - - Validates all the command line options, reads the signing config from - the repo to determine which entitlement files to use, signs the build - using either the native macOS codesign or rcodesign, and finally validates - the .app using codesign. - """ - command_context._set_log_level(verbose_arg) - - # Check appdir and remove trailing slasshes - if not os.path.isdir(app_arg): - command_context.log( - logging.ERROR, - "macos-sign", - {"app": app_arg}, - "ERROR: {app} is not a directory", - ) - sys.exit(1) - app = os.path.realpath(app_arg) - - # With rcodesign, either both a p12 file and p12 password file should be - # provided or neither. If neither, the signing identity must be either '-' - # for ad-hoc or None. - rcodesign_p12_provided = False - if use_rcodesign_arg: - if p12_file_arg is None and p12_password_file_arg is not None: - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: p12 password file with no p12 file, " "use both or neither", - ) - sys.exit(1) - if p12_file_arg is not None and p12_password_file_arg is None: - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: p12 file with no p12 password file, " "use both or neither", - ) - sys.exit(1) - if p12_file_arg is not None and p12_password_file_arg is not None: - rcodesign_p12_provided = True - - # Only rcodesign supports pkcs12 args - if not use_rcodesign_arg and ( - p12_password_file_arg is not None or p12_file_arg is not None - ): - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: pkcs12 signing not supported with " - "native codesign, only rcodesign", - ) - sys.exit(1) - - # Check the user didn't ask for a codesigning identity with rcodesign. - # Check the user didn't ask for ad-hoc signing AND rcodesign pkcs12 signing. - # Check the user didn't ask for ad-hoc signing with production entitlements. - # Self-signing and ad-hoc signing are both incompatible with production - # entitlements. (Library loading entitlements depend on the codesigning - # team ID which is not set on self-signed/ad-hoc signatures and requires an - # Apple-issued cert. Signing succeeds, but the bundle will not be - # launchable.) - if use_rcodesign_arg: - # With rcodesign, only accept "-s -" or no -s argument. - if not rcodesign_p12_provided: - if signing_identity_arg is not None and signing_identity_arg != "-s": - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: rcodesign requires pkcs12 or " "ad-hoc signing", - ) - sys.exit(1) - - # Did the user request a signing identity string and pkcs12 signing? - if rcodesign_p12_provided and signing_identity_arg is not None: - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: both ad-hoc and pkcs12 signing " "requested", - ) - sys.exit(1) - - # Is ad-hoc signing with production entitlements requested? - if ( - (not rcodesign_p12_provided) - and (signing_identity_arg is None or signing_identity_arg == "-") - and (entitlements_arg != "developer") - ): - command_context.log( - logging.ERROR, - "macos-sign", - {}, - "ERROR: " "Production entitlements and self-signing are " "not compatible", - ) - sys.exit(1) - - # By default, use ad-hoc - signing_identity = None - signing_identity_label = None - if use_rcodesign_arg and rcodesign_p12_provided: - # signing_identity will not be used - signing_identity_label = "pkcs12" - elif signing_identity_arg is None or signing_identity_arg == "-": - signing_identity = "-" - signing_identity_label = "ad-hoc" - else: - signing_identity = signing_identity_arg - signing_identity_label = signing_identity_arg - - command_context.log( - logging.INFO, - "macos-sign", - {"id": signing_identity_label}, - "Using {id} signing identity", - ) - - # Which channel are we signing? Set 'channel' based on 'channel_arg'. - channel = None - if channel_arg == "auto": - channel = auto_detect_channel(command_context, app) - else: - channel = channel_arg - command_context.log( - logging.INFO, - "macos-sign", - {"channel": channel}, - "Using {channel} channel signing configuration", - ) - - # Do we want production or developer entitlements? Set 'entitlements_key' - # based on 'entitlements_arg'. In the buildconfig, developer entitlements - # are labeled as "default". - entitlements_key = None - if entitlements_arg == "production": - entitlements_key = "production" - elif entitlements_arg == "developer": - entitlements_key = "default" - elif entitlements_arg == "production-without-restricted": - # We'll strip out restricted entitlements below. - entitlements_key = "production" - - command_context.log( - logging.INFO, - "macos-sign", - {"ent": entitlements_arg}, - "Using {ent} entitlements", - ) - - # Get a path to the config file which maps files in the .app - # bundle to their entitlement files in the tree (if any) to use - # when signing. There is a set of mappings for each combination - # of ({developer, production}, {nightly, devedition, release}). - # i.e., depending on the entitlements argument (production or dev) - # and the channel argument (nightly, devedition, or release) we'll - # use different entitlements. - sourcedir = command_context.topsrcdir - buildconfigpath = sourcedir + "/taskcluster/ci/config.yml" - - command_context.log( - logging.INFO, - "macos-sign", - {"yaml": buildconfigpath}, - "Reading build config file {yaml}", - ) - - with open(buildconfigpath, "r") as buildconfigfile: - parsedconfig = yaml.safe_load(buildconfigfile) - - # Store all the mappings - signing_groups = parsedconfig["mac-signing"]["hardened-sign-config"][ - "by-hardened-signing-type" - ][entitlements_key] - - command_context.log( - logging.INFO, "macos-sign", {}, "Stripping existing xattrs and signatures" - ) - - # Remove extended attributes. Per Apple "Technical Note TN2206", - # code signing uses extended attributes to store signatures for - # non-Mach-O executables such as script files. We want to avoid - # any complications that might be caused by existing extended - # attributes. - xattr_cmd = ["xattr", "-cr", app] - run(command_context, xattr_cmd, capture_output=not verbose_arg) - - # Remove existing signatures. The codesign command only replaces - # signatures if the --force option used. Remove all signatures so - # subsequent signing commands with different options will result - # in re-signing without requiring --force. - cs_reset_cmd = ["find", app, "-exec", "codesign", "--remove-signature", "{}", ";"] - run(command_context, cs_reset_cmd, capture_output=not verbose_arg) - - if use_rcodesign_arg is True: - sign_with_rcodesign( - command_context, - verbose_arg, - signing_groups, - entitlements_arg, - channel, - app, - p12_file_arg, - p12_password_file_arg, - ) - else: - sign_with_codesign( - command_context, - verbose_arg, - signing_groups, - signing_identity, - entitlements_arg, - channel, - app, - ) - - verify_result(command_context, app, verbose_arg) - - -def auto_detect_channel(ctx, app): - """Detects the channel of the provided app (nightly, release, etc.) - - Reads the CFBundleIdentifier from the provided apps Info.plist and - returns the appropriate channel string. Release and Beta builds use - org.mozilla.firefox for the CFBundleIdentifier. Nightly channel builds use - org.mozilla.nightly. - """ - # The bundle IDs for different channels. We use these strings to - # auto-detect the channel being signed. Different channels use - # different entitlement files. - NIGHTLY_BUNDLEID = "org.mozilla.nightly" - DEVEDITION_BUNDLEID = "org.mozilla.firefoxdeveloperedition" - # BETA uses the same bundle ID as Release - RELEASE_BUNDLEID = "org.mozilla.firefox" - - info_plist = os.path.join(app, "Contents/Info.plist") - - ctx.log( - logging.DEBUG, "macos-sign", {"plist": info_plist}, "Reading {plist} bundle ID" - ) - - process = subprocess.Popen( - ["defaults", "read", info_plist, "CFBundleIdentifier"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - out, err = process.communicate() - bundleid = out.decode("utf-8").strip() - - ctx.log( - logging.DEBUG, - "macos-sign", - {"bundleid": bundleid}, - "Found bundle ID {bundleid}", - ) - - if bundleid == NIGHTLY_BUNDLEID: - return "nightly" - elif bundleid == DEVEDITION_BUNDLEID: - return "devedition" - elif bundleid == RELEASE_BUNDLEID: - return "release" - else: - # Couldn't determine the channel from . - # Unrecognized bundle ID . - # Use the channel argument. - ctx.log( - logging.ERROR, - "macos-sign", - {"plist": info_plist}, - "Couldn't read bundle ID from {plist}", - ) - sys.exit(1) - - -def sign_with_codesign( - ctx, verbose_arg, signing_groups, signing_identity, entitlements_arg, channel, app -): - # Signing with codesign: - # - # For each signing_group in signing_groups, invoke codesign with the - # options and paths specified in the signging_group. - ctx.log(logging.INFO, "macos-sign", {}, "Signing with codesign") - - for signing_group in signing_groups: - cs_cmd = ["codesign"] - cs_cmd.append("--sign") - cs_cmd.append(signing_identity) - - if "deep" in signing_group and signing_group["deep"]: - cs_cmd.append("--deep") - if "force" in signing_group and signing_group["force"]: - cs_cmd.append("--force") - if "runtime" in signing_group and signing_group["runtime"]: - cs_cmd.append("--options") - cs_cmd.append("runtime") - - entitlement_file = None - temp_files_to_cleanup = [] - - if "entitlements" in signing_group: - # This signing group has an entitlement file - cs_cmd.append("--entitlements") - - # Given the type of build (dev, prod, or prod without restricted - # entitlements) and the channel we're going to sign, get the path - # to the entitlement file from the config. - if isinstance(signing_group["entitlements"], str): - # If the 'entitlements' key in the signing group maps to - # a string, it's a simple lookup. - entitlement_file = signing_group["entitlements"] - elif isinstance(signing_group["entitlements"], dict): - # If the 'entitlements' key in the signing group maps to - # a dict, the mapping from key to entitlement file is - # different for each channel: - if channel == "nightly": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ]["default"]["by-project"]["mozilla-central"] - elif channel == "devedition": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ][".*devedition.*"] - elif channel == "release" or channel == "beta": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ]["default"]["by-project"]["default"] - else: - raise ("Unexpected channel") - - # We now have an entitlement file for this signing group. - # If we are signing using production-without-restricted, strip out - # restricted entitlements and save the result in a temporary file. - if entitlements_arg == "production-without-restricted": - temp_ent_file = strip_restricted_entitlements(entitlement_file) - temp_files_to_cleanup.append(temp_ent_file) - cs_cmd.append(temp_ent_file) - else: - cs_cmd.append(entitlement_file) - - for pathglob in signing_group["globs"]: - binary_paths = glob.glob( - os.path.join(app, pathglob.strip("/")), recursive=True - ) - for binary_path in binary_paths: - cs_cmd.append(binary_path) - - run(ctx, cs_cmd, capture_output=not verbose_arg, check=True) - - for temp_file in temp_files_to_cleanup: - os.remove(temp_file) - - -def run(ctx, cmd, **kwargs): - cmd_as_str = " ".join(cmd) - ctx.log(logging.DEBUG, "macos-sign", {"cmd": cmd_as_str}, "[{cmd}]") - try: - subprocess.run(cmd, **kwargs) - except subprocess.CalledProcessError as e: - ctx.log( - logging.ERROR, - "macos-sign", - {"rc": e.returncode, "cmd": cmd_as_str, "prog": cmd[0]}, - "{prog} subprocess failed with exit code {rc}. " - "See (-v) verbose output for command output. " - "Failing command: [{cmd}]", - ) - sys.exit(e.returncode) - - -def verify_result(ctx, app, verbose_arg): - # Verbosely verify validity of signed app - cs_verify_cmd = ["codesign", "-vv", app] - try: - run(ctx, cs_verify_cmd, capture_output=not verbose_arg, check=True) - ctx.log( - logging.INFO, - "macos-sign", - {"app": app}, - "Verification of signed app {app} OK", - ) - except subprocess.CalledProcessError as e: - ctx.log( - logging.ERROR, - "macos-sign", - {"rc": e.returncode, "app": app}, - "Verification of {app} failed with exit code {rc}", - ) - sys.exit(e.returncode) - - -def sign_with_rcodesign( - ctx, - verbose_arg, - signing_groups, - entitlements_arg, - channel, - app, - p12_file_arg, - p12_password_file_arg, -): - # Signing with rcodesign: - # - # The rcodesign sign is a single rcodesign invocation with all necessary - # arguments included. rcodesign accepts signing options to be applied to - # an input path (the .app in this case). For inner bundle resources that - # have different codesigning settings, signing options are passed as - # scoped arguments in the form --option :. For - # example, a different entitlement file is specified for the nested - # plugin-container.app with the following: - # - # --entitlements-xml-path \ - # Contents/MacOS/plugin-container.app:/path/to/plugin-container.xml - # - # We iterate through the signing group and generate scoped arguments - # for each path to be signed. If the path is '/', it is the main signing - # input path and its options are specified as standard arguments. - ctx.log(logging.INFO, "macos-sign", {}, "Signing with rcodesign") - - cs_cmd = ["rcodesign", "sign"] - if p12_file_arg is not None: - cs_cmd.append("--p12-file") - cs_cmd.append(p12_file_arg) - if p12_password_file_arg is not None: - cs_cmd.append("--p12-password-file") - cs_cmd.append(p12_password_file_arg) - - temp_files_to_cleanup = [] - - for signing_group in signing_groups: - # Ignore the 'deep' and 'force' setting for rcodesign - group_runtime = "runtime" in signing_group and signing_group["runtime"] - - entitlement_file = None - - if "entitlements" in signing_group: - # Given the type of build (dev, prod, or prod without restricted - # entitlements) and the channel we're going to sign, get the path - # to the entitlement file from the config. - if isinstance(signing_group["entitlements"], str): - # If the 'entitlements' key in the signing group maps to - # a string, it's a simple lookup. - entitlement_file = signing_group["entitlements"] - elif isinstance(signing_group["entitlements"], dict): - # If the 'entitlements' key in the signing group maps to - # a dict, the mapping from key to entitlement file is - # different for each channel: - if channel == "nightly": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ]["default"]["by-project"]["mozilla-central"] - elif channel == "devedition": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ][".*devedition.*"] - elif channel == "release" or channel == "beta": - entitlement_file = signing_group["entitlements"][ - "by-build-platform" - ]["default"]["by-project"]["default"] - else: - raise ("Unexpected channel") - - # We now have an entitlement file for this signing group. - # If we are signing using production-without-restricted, strip out - # restricted entitlements and save the result in a temporary file. - if entitlements_arg == "production-without-restricted": - entitlement_file = strip_restricted_entitlements(entitlement_file) - temp_files_to_cleanup.append(entitlement_file) - - for pathglob in signing_group["globs"]: - binary_paths = glob.glob( - os.path.join(app, pathglob.strip("/")), recursive=True - ) - for binary_path in binary_paths: - if pathglob == "/": - # This is the root of the app. Use these signing options - # without argument scoping. - if group_runtime: - cs_cmd.append("--code-signature-flags") - cs_cmd.append("runtime") - if entitlement_file is not None: - cs_cmd.append("--entitlements-xml-path") - cs_cmd.append(entitlement_file) - cs_cmd.append(binary_path) - continue - - # This is not the root of the app. Paths are convered to - # relative paths and signing options are specified as scoped - # arguments. - binary_path_relative = os.path.relpath(binary_path, app) - if group_runtime: - cs_cmd.append("--code-signature-flags") - scoped_arg = binary_path_relative + ":runtime" - cs_cmd.append(scoped_arg) - if entitlement_file is not None: - cs_cmd.append("--entitlements-xml-path") - scoped_arg = binary_path_relative + ":" + entitlement_file - cs_cmd.append(scoped_arg) - - run(ctx, cs_cmd, capture_output=not verbose_arg, check=True) - - for temp_file in temp_files_to_cleanup: - os.remove(temp_file) - - -def strip_restricted_entitlements(plist_file): - # Not a complete set. Update as needed. This is - # the set of restricted entitlements we use to date. - restricted_entitlements = [ - "com.apple.developer.web-browser.public-key-credential", - "com.apple.application-identifier", - ] - - plist_file_obj = open(plist_file, "rb") - plist_data = plistlib.load(plist_file_obj, fmt=plistlib.FMT_XML) - for entitlement in restricted_entitlements: - if entitlement in plist_data: - del plist_data[entitlement] - - _, temp_file_path = tempfile.mkstemp(prefix="mach-macos-sign.") - with open(temp_file_path, "wb") as temp_file_obj: - plistlib.dump(plist_data, temp_file_obj) - temp_file_obj.close() - - return temp_file_path