From afb5f81ca052fcc49906dac36675d966d1a581d4 Mon Sep 17 00:00:00 2001 From: Matt Hardcastle Date: Thu, 20 Jun 2019 15:35:47 -0700 Subject: [PATCH] Add signing to macOS Interface and HQ Launcher This change adds signing to the macOS version of Interface and HQ Launcher. By default, signing is not enabled; to enable signing you must add the `XCODE_DEVELOPMENT_TEAM` environment variable and rerun CMake. Jira: https://highfidelity.atlassian.net/browse/DEVOPS-52 --- interface/CMakeLists.txt | 16 +++++ interface/interface.entitlements | 16 +++++ launchers/darwin/CMakeLists.txt | 23 +++++- launchers/darwin/HQ Launcher.entitlements | 14 ++++ tools/ci-scripts/postbuild.py | 88 +++++++++++++++++++++-- 5 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 interface/interface.entitlements create mode 100644 launchers/darwin/HQ Launcher.entitlements diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 9553b571c5..ac23264aeb 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -142,6 +142,22 @@ endif() if (APPLE) add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM}) + set_from_env(XCODE_DEVELOPMENT_TEAM XCODE_DEVELOPMENT_TEAM "") + if ("${XCODE_DEVELOPMENT_TEAM}" STREQUAL "") + message(WARNING "XCODE_DEVELOPMENT_TEAM environment variable is not set. Not signing build.") + else() + set_target_properties(${TARGET_NAME} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/interface.entitlements" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application" + XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS NO + XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Manual" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${XCODE_DEVELOPMENT_TEAM}" + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp --deep" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "" + ) + endif() + # make sure the output name for the .app bundle is correct # Fix up the rpath so macdeployqt works set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") diff --git a/interface/interface.entitlements b/interface/interface.entitlements new file mode 100644 index 0000000000..22747cbc32 --- /dev/null +++ b/interface/interface.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.application-groups + + high-fidelity.hifi + + + diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt index 0a7ef70461..88ac666a93 100644 --- a/launchers/darwin/CMakeLists.txt +++ b/launchers/darwin/CMakeLists.txt @@ -69,8 +69,27 @@ function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE) endfunction() add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files}) -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME} - MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME}) + +set_from_env(XCODE_DEVELOPMENT_TEAM XCODE_DEVELOPMENT_TEAM "") +if ("${XCODE_DEVELOPMENT_TEAM}" STREQUAL "") + message(WARNING "XCODE_DEVELOPMENT_TEAM environmental variable is not set. Not signing build.") + set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME} + MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME} + ) +else() + set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME} + MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME} + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "HQ Launcher.entitlements" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application" + XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS NO + XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Manual" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${XCODE_DEVELOPMENT_TEAM}" + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "" + ) +endif() + set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "") if ("${LAUNCHER_HMAC_SECRET}" STREQUAL "") message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set") diff --git a/launchers/darwin/HQ Launcher.entitlements b/launchers/darwin/HQ Launcher.entitlements new file mode 100644 index 0000000000..0f41736b78 --- /dev/null +++ b/launchers/darwin/HQ Launcher.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.application-groups + + high-fideilty.hifi + + + diff --git a/tools/ci-scripts/postbuild.py b/tools/ci-scripts/postbuild.py index 00b3007104..949c40b0f2 100644 --- a/tools/ci-scripts/postbuild.py +++ b/tools/ci-scripts/postbuild.py @@ -2,6 +2,9 @@ import os import sys import shutil +import subprocess +import tempfile +import uuid import zipfile import base64 @@ -11,6 +14,33 @@ sys.path.append(SOURCE_PATH) import hifi_utils + +class ZipAttrs: + """ A readable wrapper for ZipInfo's external attributes bit field """ + + S_IFREG = 0x8 + S_IFLNK = 0xa + S_IFDIR = 0x4 + MODE_MASK = 0xfff0000 + + def __init__(self, zip_info): + # File stats are the 4 high bits of external_attr, a 32 bit field. + self._stat = zip_info.external_attr >> 28 + self.mode = (zip_info.external_attr & self.MODE_MASK) >> 16 + + @property + def is_symlink(self): + return self._stat == self.S_IFLNK + + @property + def is_dir(self): + return self._stat == self.S_IFDIR + + @property + def is_regular(self): + return self._stat == self.S_IFREG + + BUILD_PATH = os.path.join(SOURCE_PATH, 'build') INTERFACE_BUILD_PATH = os.path.join(BUILD_PATH, 'interface', 'Release') WIPE_PATHS = [] @@ -65,8 +95,12 @@ def fixupMacZip(filename): fullPath = os.path.join(BUILD_PATH, "{}.zip".format(filename)) outFullPath = "{}.zip".format(fullPath) print("Fixup mac ZIP file {}".format(fullPath)) - with zipfile.ZipFile(fullPath) as inzip: - with zipfile.ZipFile(outFullPath, 'w') as outzip: + tmpDir = os.path.join(tempfile.gettempdir(), + 'com.highfidelity.launcher.postbuild', + str(uuid.uuid4())) + + try: + with zipfile.ZipFile(fullPath) as inzip: rootPath = inzip.infolist()[0].filename for entry in inzip.infolist(): if entry.filename == rootPath: @@ -86,11 +120,51 @@ def fixupMacZip(filename): continue # if we made it here, include the file in the output buffer = inzip.read(entry.filename) - entry.filename = newFilename - outzip.writestr(entry, buffer) - outzip.close() - print("Replacing {} with fixed {}".format(fullPath, outFullPath)) - shutil.move(outFullPath, fullPath) + newFilename = os.path.join(tmpDir, newFilename) + + attrs = ZipAttrs(entry) + if attrs.is_dir: + os.makedirs(newFilename) + elif attrs.is_regular: + with open(newFilename, mode='wb') as _file: + _file.write(buffer) + os.chmod(newFilename, mode=attrs.mode) + elif attrs.is_symlink: + os.symlink(buffer, newFilename) + else: + raise IOError('Invalid file stat') + + if 'XCODE_DEVELOPMENT_TEAM' in os.environ: + print('XCODE_DEVELOPMENT_TEAM environment variable is not set. ' + 'Not signing build.', file=sys.stderr) + else: + # The interface.app bundle must be signed again after being + # stripped. + print('Signing interface.app bundle') + entitlementsPath = os.path.join( + os.path.dirname(__file__), + '../../interface/interface.entitlements') + subprocess.run([ + 'codesign', '-s', 'Developer ID Application', '--deep', + '--timestamp', '--force', '--entitlements', entitlementsPath, + os.path.join(tmpDir, 'interface.app') + ], check=True) + + # Repackage the zip including the signed version of interface.app + print('Replacing {} with fixed {}'.format(fullPath, outFullPath)) + if os.path.exists(outFullPath): + print('fixed zip already exists, deleting it', file=sys.stderr) + os.unlink(outFullPath) + previous_cwd = os.getcwd() + os.chdir(tmpDir) + try: + subprocess.run(['zip', '--symlink', '-r', outFullPath, './.'], + stdout=subprocess.DEVNULL, check=True) + finally: + os.chdir(previous_cwd) + + finally: + shutil.rmtree(tmpDir) def fixupWinZip(filename): fullPath = os.path.join(BUILD_PATH, "{}.zip".format(filename))