#!/usr/bin/env bash # Copyright (C) 2012-2017 VLC authors and VideoLAN # Copyright (C) 2012-2014 Felix Paul Kühne # Copyright (C) 2018-2021 Damiano Galassi # Copyright (C) 2018-2021 Bradley Sepos # # Based on VLC's codesign.sh # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser 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. NAME="hbsign" set -e set -u SELF="${0}" SELF_NAME=$(basename "${SELF}") HELP="\ usage: ${SELF_NAME} [-hrs] ${SELF_NAME} identity application [application2 ...] where: -h display this help text -r enable runtime hardening -s enable sandbox " # Logs error message and exits function exit_with_error { set +e ERROR="${2}" echo "${SELF_NAME}: ${ERROR}" >&2 PRINT_HELP="${3:-false}" if [[ "${PRINT_HELP}" == true ]]; then echo -e "${HELP}" fi exit "${1}" } LOG="${NAME}.log" touch "${LOG}" || exit_with_error 1 "${SELF_NAME}: unable to create log file ${LOG}" OPTIND=1 RUNTIME=false SANDBOX=false while getopts ":hrs" OPT; do case "${OPT}" in h) # Print help and exit echo -e "${HELP}" exit 0 ;; r) RUNTIME=true ;; s) SANDBOX=true ;; :) # Option without required argument exit_with_error 1 "${SELF_NAME}: option -${OPTARG} requires a value" true ;; \?) # Invalid option specified exit_with_error 1 "${SELF_NAME}: invalid option: -${OPTARG}" true ;; esac done shift $((${OPTIND} - 1)) IDENTITY="${1:-}" if [[ "${IDENTITY}" == '' ]]; then exit_with_error 1 "${SELF_NAME}: identity not specified" true fi shift 1 if [[ ${#@} -eq 0 ]]; then exit_with_error 1 "${SELF_NAME}: application not specified" true fi SCRIPTDIR=$(dirname "${SELF}") RUNTIME_FLAGS="" if [[ "${RUNTIME}" == true ]]; then RUNTIME_FLAGS="--options=runtime" fi ENTITLEMENTS_MAIN_FLAGS="" ENTITLEMENTS_XPC_FLAGS="" ENTITLEMENTS_CLI_FLAGS="" if [[ "${SANDBOX}" == true ]]; then ENTITLEMENTS_MAIN_FLAGS="--entitlements $SCRIPTDIR/HandBrake.entitlements" ENTITLEMENTS_XPC_FLAGS="--entitlements $SCRIPTDIR/HandBrakeXPCService/HandBrakeXPCService.entitlements" ENTITLEMENTS_CLI_FLAGS="--entitlements $SCRIPTDIR/HandBrakeXPCService/HandBrakeXPCService-RuntimeOnly.entitlements" elif [[ "${RUNTIME}" == true ]]; then ENTITLEMENTS_MAIN_FLAGS="--entitlements $SCRIPTDIR/HandBrake-RuntimeOnly.entitlements" ENTITLEMENTS_XPC_FLAGS="--entitlements $SCRIPTDIR/HandBrakeXPCService/HandBrakeXPCService-RuntimeOnly.entitlements" ENTITLEMENTS_CLI_FLAGS="${ENTITLEMENTS_XPC_FLAGS}" fi ENTITLEMENTS_CLI_FLAGS="${ENTITLEMENTS_CLI_FLAGS:+$ENTITLEMENTS_CLI_FLAGS }--prefix fr.handbrake." function sign { # sign target flags local TARGET FLAGS ERR TARGET="${1:-}" if [[ "${TARGET}" == "" ]]; then ERR="${SELF_NAME}: target not specified to sign function" echo -e "${ERR}" >> "${LOG}" exit_with_error 1 "${ERR}" fi if [[ "${2:-}" == "main" ]]; then FLAGS="${ENTITLEMENTS_MAIN_FLAGS}" elif [[ "${2:-}" == "xpc" ]]; then FLAGS="${ENTITLEMENTS_XPC_FLAGS}" elif [[ "${2:-}" == "cli" ]]; then FLAGS="${ENTITLEMENTS_CLI_FLAGS}" else FLAGS="" fi codesign --force --verbose $RUNTIME_FLAGS $FLAGS -s "${IDENTITY}" "${TARGET}" >>"${LOG}" 2>&1 || exit_with_error 1 "Signing failed. More info may be available in ${LOG}" } function verify { # verify target deep local TARGET FLAGS ERR TARGET="${1:-}" if [[ "${TARGET}" == "" ]]; then ERR="${SELF_NAME}: target not specified to verify function" echo -e "${ERR}" >> "${LOG}" exit_with_error 1 "${ERR}" fi if [[ "${2:-}" == "deep" ]]; then FLAGS="--deep --strict --verbose=4" else FLAGS="-vv" fi codesign --verify $FLAGS "${TARGET}" >>"${LOG}" 2>&1 || exit_with_error 1 "Validation failed. More info may be available in ${LOG}" } echo "Script dir: ${SCRIPTDIR}" echo "Identity: ${IDENTITY}" echo "Hardened runtime: ${RUNTIME}" echo "Sandbox: ${SANDBOX}" for TARGET in "${@}"; do TARGET="${TARGET#./}" if [[ "${TARGET##*/}" == 'HandBrake.app' ]]; then echo "${TARGET}:" find "${TARGET}"/Contents/Frameworks -type f -name ".DS_Store" -exec rm '{}' \; >/dev/null 2>&1 find "${TARGET}"/Contents/Frameworks -type f -name "*.textile" -exec rm '{}' \; >/dev/null 2>&1 find "${TARGET}"/Contents/Frameworks -type f -name "*.txt" -exec rm '{}' \; >/dev/null 2>&1 rm -rf "${TARGET}"/Contents/Frameworks/Sparkle.framework/Resources/Updater.app rm -rf "${TARGET}"/Contents/Frameworks/Sparkle.framework/Resources/Autoupdate echo " Signing Frameworks" sign "${TARGET}"/Contents/Frameworks/Sparkle.framework/Versions/A sign "${TARGET}"/Contents/Frameworks/HandBrakeKit.framework/Versions/A echo " Signing Updater App" sign "${TARGET}"/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app sign "${TARGET}"/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Autoupdate echo " Signing XPC Services" OIFS="${IFS}" IFS=$'\n' XPC=($(find -s "${TARGET}"/Contents/XPCServices -type d -regex ".*/HandBrakeXPCService[0-9]*\.xpc" 2>/dev/null)) XPC_EXT=($(find -s "${TARGET}"/Contents/XPCServices -type d -regex ".*/org\.sparkle-project.*\.xpc" 2>/dev/null)) IFS="${OIFS}" for FILE in "${XPC[@]}"; do sign "${FILE}" xpc done for FILE in "${XPC_EXT[@]}"; do sign "${FILE}" done echo " Signing App Bundle" sign "${TARGET}" main echo " Validating Frameworks" verify "${TARGET}"/Contents/Frameworks/Sparkle.framework verify "${TARGET}"/Contents/Frameworks/HandBrakeKit.framework echo " Validating Updater App" verify "${TARGET}"/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app echo " Validating XPC Services" for FILE in "${XPC[@]}" "${XPC_EXT[@]}"; do verify "${FILE}" done echo " Validating App Bundle" verify "${TARGET}" deep if [ "${IDENTITY}" != "-" ]; then echo " Validating Execution Privileges" spctl -a -t exec -vv "${TARGET}" >>"${LOG}" 2>&1 || exit_with_error 1 "Validation failed. More info may be available in ${LOG}" fi elif [[ "${TARGET##*/}" == 'HandBrakeCLI' ]]; then echo "${TARGET}:" echo " Signing Executable" sign "${TARGET}" cli echo " Validating Executable" verify "${TARGET}" deep else exit_with_error 1 "This script does not know how to sign the specified target." fi done echo "Complete." exit 0