[Bf-blender-cvs] [b178bd6650e] codesign: Codesign: Initial implementation of macOS code signer

Sergey Sharybin noreply at git.blender.org
Thu Dec 12 12:00:26 CET 2019


Commit: b178bd6650ef0c9424ed3b15bcbabfcb5bba9fb3
Author: Sergey Sharybin
Date:   Thu Dec 12 11:56:52 2019 +0100
Branches: codesign
https://developer.blender.org/rBb178bd6650ef0c9424ed3b15bcbabfcb5bba9fb3

Codesign: Initial implementation of macOS code signer

Main goal of this change is to make codesign step of release signing
to work on macOS. Development is currently was done on Linux based on
looking into what commands are being invoked.

Part of the change which touches other code signing utilities is
related on making it possible to test "foreign" code signers on a
different platform.

===================================================================

M	build_files/buildbot/codesign/base_code_signer.py
M	build_files/buildbot/codesign/config_builder.py
M	build_files/buildbot/codesign/config_server_template.py
M	build_files/buildbot/codesign/linux_code_signer.py
A	build_files/buildbot/codesign/macos_code_signer.py
M	build_files/buildbot/codesign/simple_code_signer.py
M	build_files/buildbot/codesign/util.py
M	build_files/buildbot/codesign/windows_code_signer.py
A	build_files/buildbot/codesign_server_macos.py
M	build_files/buildbot/codesign_server_windows.py

===================================================================

diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py
index ff4b4539658..845eb6f5a5b 100644
--- a/build_files/buildbot/codesign/base_code_signer.py
+++ b/build_files/buildbot/codesign/base_code_signer.py
@@ -52,6 +52,8 @@ from pathlib import Path
 from tempfile import TemporaryDirectory
 from typing import Iterable, List
 
+import codesign.util as util
+
 from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
 from codesign.archive_with_indicator import ArchiveWithIndicator
 
@@ -133,6 +135,9 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
     # This archive is created by the code signing server.
     signed_archive_info: ArchiveWithIndicator
 
+    # Platform the code is currently executing on.
+    platform: util.Platform
+
     def __init__(self, config):
         self.config = config
 
@@ -148,6 +153,8 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
         self.signed_archive_info = ArchiveWithIndicator(
             self.signed_storage_dir, 'signed_files.zip', 'ready.stamp')
 
+        self.platform = util.get_current_platform()
+
     """
     General note on cleanup environment functions.
 
@@ -383,3 +390,25 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
             logger_server.info(
                 'Got signing request, beging signign procedure.')
             self.run_signing_pipeline()
+
+    ############################################################################
+    # Command executing.
+    #
+    # Abstracted to a degree that allows to run commands from a foreign
+    # platform.
+    # The goal with this is to allow performing dry-run tests of code signer
+    # server from other platforms (for example, to test that macOS code signer
+    # does what it is supposed to after doing a refactor on Linux).
+
+    # TODO(sergey): What is the typo annotation for the command?
+    def run_command_or_mock(self, command, platform: util.Platform) -> None:
+        """
+        Run given command if current platform matches given one
+
+        If the platform is different then it will only be printed allowing
+        to verify logic of the code signing process.
+        """
+        if platform != self.platform:
+            logger_server.info(
+                f'Will run command for {platform}: {command}')
+            return
diff --git a/build_files/buildbot/codesign/config_builder.py b/build_files/buildbot/codesign/config_builder.py
index c023b4234da..0cb83aba41e 100644
--- a/build_files/buildbot/codesign/config_builder.py
+++ b/build_files/buildbot/codesign/config_builder.py
@@ -25,11 +25,14 @@ import sys
 
 from pathlib import Path
 
+import util
+
 from codesign.config_common import *
 
-if sys.platform == 'linux':
+platform = util.get_current_platform()
+if platform == util.Platform.LINUX:
     SHARED_STORAGE_DIR = Path('/data/codesign')
-elif sys.platform == 'win32':
+elif platform == util.Platform.WINDOWS:
     SHARED_STORAGE_DIR = Path('Z:\\codesign')
 
 # https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema
diff --git a/build_files/buildbot/codesign/config_server_template.py b/build_files/buildbot/codesign/config_server_template.py
index dc164634cef..326e76d0283 100644
--- a/build_files/buildbot/codesign/config_server_template.py
+++ b/build_files/buildbot/codesign/config_server_template.py
@@ -27,8 +27,34 @@ from pathlib import Path
 
 from codesign.config_common import *
 
+CODESIGN_DIRECTORY = Path(__file__).parent
+BLENDER_GIT_ROOT_DIRECTORY = CODESIGN_DIRECTORY.parent.parent.parent
+
+################################################################################
+# Common configuration.
+
+# Directory where folders for codesign requests and signed result are stored.
+# For example, /data/codesign
+SHARED_STORAGE_DIR: Path
+
+################################################################################
+# macOS-specific configuration.
+
+MACOS_ENTITLEMENTS_FILE = \
+    BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' / 'entitlements.plist'
+
+# Identity of the Developer ID Application certificate which is to be used for
+# codesign tool.
+# Use `security find-identity -v -p codesigning` to find the identity.
+#
+# NOTE: This identity is just an example from release/darwin/README.txt.
+MACOS_CODESIGN_IDENTITY = 'AE825E26F12D08B692F360133210AF46F4CF7B97'
+
+################################################################################
+# Windows-specific configuration.
+
 # URL to the timestamping authority.
-TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com'
+WIN_TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com'
 
 # Full path to the certificate used for signing.
 #
@@ -36,7 +62,10 @@ TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com'
 #
 # On Windows it is usually is a PKCS #12 key (.pfx), so the path will look
 # like Path('C:\\Secret\\Blender.pfx').
-CERTIFICATE_FILEPATH: Path
+WIN_CERTIFICATE_FILEPATH: Path
+
+################################################################################
+# Logging configuration, common for all platforms.
 
 # https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema
 LOGGING = {
diff --git a/build_files/buildbot/codesign/linux_code_signer.py b/build_files/buildbot/codesign/linux_code_signer.py
index f1523851eb7..04935f67832 100644
--- a/build_files/buildbot/codesign/linux_code_signer.py
+++ b/build_files/buildbot/codesign/linux_code_signer.py
@@ -51,7 +51,7 @@ class LinuxCodeSigner(BaseCodeSigner):
             self, file: AbsoluteAndRelativeFileName) -> bool:
         if file.relative_filepath == Path('blender'):
             return True
-        if (file.relative_filepath.parts()[-3:-1] == ('python', 'bin') and
+        if (file.relative_filepath.parts[-3:-1] == ('python', 'bin') and
                 file.relative_filepath.name.startwith('python')):
             return True
         if file.relative_filepath.suffix == '.so':
diff --git a/build_files/buildbot/codesign/macos_code_signer.py b/build_files/buildbot/codesign/macos_code_signer.py
new file mode 100644
index 00000000000..a60ef5771e5
--- /dev/null
+++ b/build_files/buildbot/codesign/macos_code_signer.py
@@ -0,0 +1,215 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+import logging
+import subprocess
+
+from pathlib import Path
+from typing import List
+
+import codesign.util as util
+
+from buildbot_utils import Builder
+
+from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
+from codesign.base_code_signer import BaseCodeSigner
+
+logger = logging.getLogger(__name__)
+logger_server = logger.getChild('server')
+
+# NOTE: Check is done as filename.endswith(), so keep the dot
+EXTENSIONS_TO_BE_SIGNED = {'.dylib', '.so', '.dmg'}
+
+# Prefixes of a file (not directory) name which are to be signed.
+# Used to sign extra executable files in Contents/Resources.
+NAME_PREFIXES_TO_BE_SIGNED = {'python'}
+
+
+def is_file_from_bundle(file: AbsoluteAndRelativeFileName) -> bool:
+    """
+    Check whether file is coming from an .app bundle
+    """
+    parts = file.relative_filepath.parts
+    if not parts:
+        return False
+    if not parts[0].endswith('.app'):
+        return False
+    return True
+
+
+def get_bundle_from_file(
+        file: AbsoluteAndRelativeFileName) -> AbsoluteAndRelativeFileName:
+    """
+    Get AbsoluteAndRelativeFileName descriptor of bundle
+    """
+    assert(is_file_from_bundle(file))
+
+    parts = file.relative_filepath.parts
+    bundle_name = parts[0]
+
+    base_dir = file.base_dir
+    bundle_filepath = file.base_dir / bundle_name
+    return AbsoluteAndRelativeFileName(base_dir, bundle_filepath)
+
+
+def is_bundle_executable_file(file: AbsoluteAndRelativeFileName) -> bool:
+    """
+    Check whether given file is an executable within an app bundle
+    """
+    if not is_file_from_bundle(file):
+        return False
+
+    parts = file.relative_filepath.parts
+    num_parts = len(parts)
+    if num_parts < 3:
+        return False
+
+    if parts[1:3] != ('Contents', 'MacOS'):
+        return False
+
+    return True
+
+
+class MacOSCodeSigner(BaseCodeSigner):
+    def check_file_is_to_be_signed(
+            self, file: AbsoluteAndRelativeFileName) -> bool:
+        if is_bundle_executable_file(file):
+            return True
+
+        base_name = file.relative_filepath.name
+        if any(base_name.startswith(prefix)
+               for prefix in NAME_PREFIXES_TO_BE_SIGNED):
+            return True
+
+        return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED
+
+    def codesign_remove_signature(
+            self, file: AbsoluteAndRelativeFileName) -> None:
+        """
+        Make sure given file does not have codesign signature
+
+        This is needed because codesigning is not possible for file which has
+        signature already.
+        """
+
+        logger_server.info(
+            'Removing codesign signature from  %s...', file.relative_filepath)
+
+        command = ['codesign', '--remove-signature', file.absolute_filepath]
+        self.run_command_or_mock(command, util.Platform.MACOS)
+
+    def codesign_file(
+            self, file: AbsoluteAndRelativeFileName) -> None:
+        """
+        Sign given file
+
+        NOTE: File must not have any signatures.
+        """
+
+        logger_server.info(
+            'Codesigning  %s...', file.relative_filepath)
+
+     

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list