This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch AOO50X in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit f900124de934456d775b70820d6425330cae57fa Author: Jim Jagielski <[email protected]> AuthorDate: Thu Sep 10 12:38:25 2026 -0400 Fix macOS language-pack signing, load-path check and sysconfig data The load-path check matched each dylib's own @_______ ID and so rejected every signed build; it now looks only for the CPython staging prefix. (cherry picked from commit e88c27187f2588c2cbaa28304e98e9fcf62910dc) --- main/configure.ac | 6 +-- .../extensions/source/macosx/spotlight/makefile.mk | 4 -- main/python/makefile.mk | 11 +---- main/python/prj/d.lst | 13 ++---- main/setup_native/scripts/mac_install.script | 2 +- .../scripts/osx_install_languagepack.applescript | 6 +-- .../scripts/osx_install_patch.applescript | 7 +-- main/setup_native/source/mac/macinstall.ulf | 5 ++- main/solenv/bin/macosx-check-load-commands.sh | 52 ++++++++++++++++++++++ main/solenv/bin/macosx-codesign.sh | 11 +---- main/solenv/bin/modules/installer/simplepackage.pm | 18 +++++--- 11 files changed, 86 insertions(+), 49 deletions(-) diff --git a/main/configure.ac b/main/configure.ac index bbba0d6c63..75b84637ca 100644 --- a/main/configure.ac +++ b/main/configure.ac @@ -987,9 +987,9 @@ AC_ARG_WITH(macosx-codesigning-identity, Code-sign the macOS application bundle and disk image while packaging, with this codesign identity ("-" for ad-hoc). Unset: package unsigned, as before. - Traditional macOS language packs cannot be installed - into a signed application because that would invalidate - its resource seal. + Traditional macOS language packs cannot be installed + into a signed application because that would invalidate + its resource seal. Usage: --with-macosx-codesigning-identity="Developer ID Application: ..." ],with_macosx_codesigning_identity=$withval,) diff --git a/main/extensions/source/macosx/spotlight/makefile.mk b/main/extensions/source/macosx/spotlight/makefile.mk index 3df817d3ef..475a1a9f71 100644 --- a/main/extensions/source/macosx/spotlight/makefile.mk +++ b/main/extensions/source/macosx/spotlight/makefile.mk @@ -83,10 +83,6 @@ $(CONTENTS)$/%.plist : mdimporter/%.plist $(MACOS)$/OOoSpotlightImporter: $(SLOFILES) $(MKDIRHIER) $(@:d) $(CC) -o $(MACOS)$/OOoSpotlightImporter $(SLOFILES:s/.obj/.o/) $(EXTRA_LINKFLAGS) $(BUNDLELIBS) -bundle -# we have to change the zlib install name, otherwise the plugin will not work - .IF "$(SYSTEM_ZLIB)"=="NO" - install_name_tool -change @executable_path/libz.1.dylib @loader_path/../../../../../program/libz.1.dylib $(MACOS)$/OOoSpotlightImporter - .ENDIF #the resources are just copied $(RESOURCES)$/% : mdimporter/% diff --git a/main/python/makefile.mk b/main/python/makefile.mk index d4e2a038c7..ad196e1455 100644 --- a/main/python/makefile.mk +++ b/main/python/makefile.mk @@ -117,20 +117,13 @@ CONFIGURE_ACTION += --disable-ipv6 .ENDIF BUILD_ACTION=$(ENV_BUILD) $(GNUMAKE) -j$(EXTMAXPROCESS) && $(GNUMAKE) install && chmod -R ug+w $(MYCWD)/python-inst && chmod g+w Include -.IF "$(OS)"=="LINUX" -# CPython's sysconfig module names its generated data module after the -# platform/ABI (e.g. _sysconfigdata__linux_x86_64-linux-gnu.py) instead of -# the plain "_sysconfigdata.py" that main/python/prj/d.lst delivers. Rename -# it in place so delivery finds the name it's looking for. -BUILD_ACTION+= && for d in $(MYCWD)/python-inst/lib/python3.11 $(MYCWD)/python-inst/lib64/python3.11; do test -d "$$d" && (cd "$$d" && for f in _sysconfigdata_*.py; do test -f "$$f" && mv -f "$$f" _sysconfigdata.py; done); done; true -.ENDIF - .IF "$(OS)"=="MACOSX" # CPython records its temporary installation prefix in the interpreter's load # command and the dylib ID. Both files are packaged together in program/. -BUILD_ACTION+= && (cd $(MYCWD)/python-inst/lib/python3.11 && for f in _sysconfigdata_*.py; do test -f "$$f" && cp -f "$$f" _sysconfigdata.py; done) BUILD_ACTION+= && install_name_tool -id @loader_path/libpython3.11.dylib $(MYCWD)/python-inst/lib/libpython3.11.dylib BUILD_ACTION+= && install_name_tool -change $(MYCWD)/python-inst/lib/libpython3.11.dylib @executable_path/libpython3.11.dylib $(MYCWD)/python-inst/bin/python3.11 +# install_name_tool -change succeeds silently when the old name does not match. +BUILD_ACTION+= && $(SOLARENV)$/bin$/macosx-check-load-commands.sh $(MYCWD)/python-inst/bin/python3.11 .ENDIF .ELSE diff --git a/main/python/prj/d.lst b/main/python/prj/d.lst index 09a6045ae2..3170dede93 100644 --- a/main/python/prj/d.lst +++ b/main/python/prj/d.lst @@ -96,17 +96,10 @@ mkdir: %_DEST%\inc%_EXT%\python\cpython ..\%__SRC%\misc\build\Python-3.11.15\Lib\xmlrpc\* %_DEST%\lib%_EXT%\python\xmlrpc\* ..\%__SRC%\misc\build\Python-3.11.15\Lib\zoneinfo\* %_DEST%\lib%_EXT%\python\zoneinfo\* ..\%__SRC%\misc\build\Python-3.11.15\Makefile %_DEST%\lib%_EXT%\python\python3.11\config\Makefile -# _sysconfigdata filename is platform-mangled (e.g. _sysconfigdata__darwin_darwin.py on macOS, -# _sysconfigdata__linux_x86_64-linux-gnu.py on Linux). On Linux, makefile.mk's BUILD_ACTION -# renames the generated file to the plain name below before delivery runs. The source below -# is deliberately NOT a wildcard: deliver.pl's -# glob_line() only renames-on-copy when the destination also has a wildcard; paired with a -# literal destination, a wildcard source makes it treat the destination as a directory and -# append the matched filename onto it, producing a bogus nested path. -..\%__SRC%\misc\build\python-inst\lib\python3.11\_sysconfigdata.py %_DEST%\lib%_EXT%\python\_sysconfigdata.py -# Preserve the platform-mangled module that Python's sysconfig imports. Linux -# renames it above, so this wildcard is effective only where one remains. +# sysconfig imports the platform-named module (e.g. _sysconfigdata__darwin_darwin.py) by +# that name, so it must keep it: with both sides wildcarded, deliver.pl keeps the filename. ..\%__SRC%\misc\build\python-inst\lib\python3.11\_sysconfigdata_*.py %_DEST%\lib%_EXT%\python\*.py +..\%__SRC%\misc\build\python-inst\lib64\python3.11\_sysconfigdata_*.py %_DEST%\lib%_EXT%\python\*.py ..\pyversion.mk %_DEST%\inc%_EXT%\pyversion.mk ..\pyversion_dmake.mk %_DEST%\inc%_EXT%\pyversion_dmake.mk diff --git a/main/setup_native/scripts/mac_install.script b/main/setup_native/scripts/mac_install.script index 0fc219d9a1..0a8f9068c3 100644 --- a/main/setup_native/scripts/mac_install.script +++ b/main/setup_native/scripts/mac_install.script @@ -27,4 +27,4 @@ MY_DIR=$(dirname "$0") -osascript "$MY_DIR/../osx_install.applescript" +osascript "$MY_DIR/../Resources/osx_install.applescript" diff --git a/main/setup_native/scripts/osx_install_languagepack.applescript b/main/setup_native/scripts/osx_install_languagepack.applescript index 17b73a8d8f..91610d5839 100644 --- a/main/setup_native/scripts/osx_install_languagepack.applescript +++ b/main/setup_native/scripts/osx_install_languagepack.applescript @@ -130,10 +130,10 @@ on error return 3 --wrong target-directory end try --- Adding files below Contents invalidates a sealed application's resource --- envelope. Refuse rather than silently break a Developer ID or ad-hoc seal. +-- Adding files below Contents would break a sealed application. Test for the seal +-- itself: codesign --display also accepts the linker's ad-hoc signature on arm64. try - do shell script "/usr/bin/codesign --display " & quoted form of (choice as string) + do shell script "test -e " & quoted form of ((choice as string) & "/Contents/_CodeSignature/CodeResources") display dialog installSignedFailed buttons {OKLabel} default button 1 with icon 0 return 4 on error diff --git a/main/setup_native/scripts/osx_install_patch.applescript b/main/setup_native/scripts/osx_install_patch.applescript index 634db8ef23..1147956ba6 100644 --- a/main/setup_native/scripts/osx_install_patch.applescript +++ b/main/setup_native/scripts/osx_install_patch.applescript @@ -53,7 +53,7 @@ set IdentifyQ to "[IdentifyQText] set IdentifyYES to "[IdentifyYES]" set IdentifyNO to "[IdentifyNO]" set installFailed to "[InstallFailedText]" -set installSignedFailed to "[InstallSignedFailedText]" +set installSignedFailed to "[InstallSignedPatchFailedText]" set installComplete to "[InstallCompleteTextPatch]" set sourcedir to (do shell script "dirname " & quoted form of POSIX path of (path to of me)) @@ -128,9 +128,10 @@ on error return 3 --wrong target-directory end try --- A patch changes sealed bundle contents and would invalidate the application. +-- A patch would break a sealed application. Test for the seal itself: codesign +-- --display also accepts the linker's ad-hoc signature on arm64. try - do shell script "/usr/bin/codesign --display " & quoted form of (choice as string) + do shell script "test -e " & quoted form of ((choice as string) & "/Contents/_CodeSignature/CodeResources") display dialog installSignedFailed buttons {OKLabel} default button 1 with icon 0 return 4 on error diff --git a/main/setup_native/source/mac/macinstall.ulf b/main/setup_native/source/mac/macinstall.ulf index a1997f7a96..115df7368e 100644 --- a/main/setup_native/source/mac/macinstall.ulf +++ b/main/setup_native/source/mac/macinstall.ulf @@ -59,7 +59,10 @@ en-US = "No, abort installation" en-US = "Installation failed." [InstallSignedFailedText] -en-US = "This add-on cannot modify a signed [PRODUCTNAME] application. Install a complete language-specific application instead." +en-US = "This language pack cannot modify a signed [PRODUCTNAME] application. Install a complete language-specific application instead." + +[InstallSignedPatchFailedText] +en-US = "This patch cannot modify a signed [PRODUCTNAME] application. Install the complete updated application instead." [InstallCompleteText] en-US = "Installation of [PRODUCTNAME] language pack completed." diff --git a/main/solenv/bin/macosx-check-load-commands.sh b/main/solenv/bin/macosx-check-load-commands.sh new file mode 100755 index 0000000000..5f71606388 --- /dev/null +++ b/main/solenv/bin/macosx-check-load-commands.sh @@ -0,0 +1,52 @@ +#!/bin/bash +#************************************************************** +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +#************************************************************** +# +# Fail when a Mach-O under the given paths still loads from or searches the +# CPython staging prefix, which does not exist outside the build tree. +# +# ./solenv/bin/macosx-check-load-commands.sh <bundle-or-file> ... +# +# Unresolved @_______ placeholders are not rejected: gbuild executables such as +# uno.bin and regcomp.bin still ship them and load only via DYLD_LIBRARY_PATH. + +set -euo pipefail + +[ $# -gt 0 ] || { echo "usage: $(basename "$0") <bundle-or-file> ..." >&2; exit 2; } + +machos=() +while IFS= read -r -d '' f && IFS= read -r type; do + case "$type" in ": Mach-O"*) machos+=("$f") ;; esac +done < <(find "$@" -type f -print0 | xargs -0 file --no-pad --print0 -- 2>/dev/null) +[ ${#machos[@]} -gt 0 ] || exit 0 + +# A dylib's own ID (LC_ID_DYLIB) is never used to locate it at runtime. +bad=$(printf '%s\0' "${machos[@]}" | xargs -0 otool -l | awk ' + /^[^[:space:]].*:$/ { file = $0; next } + $1 == "cmd" { cmd = $2; next } + cmd != "LC_ID_DYLIB" && ($1 == "name" || $1 == "path") && /python-inst/ { + print file " " cmd " " $2 + }') +if [ -n "$bad" ]; then + echo "unrelocated Mach-O load commands:" >&2 + echo "$bad" >&2 + exit 1 +fi diff --git a/main/solenv/bin/macosx-codesign.sh b/main/solenv/bin/macosx-codesign.sh index e04a973532..f331c72476 100755 --- a/main/solenv/bin/macosx-codesign.sh +++ b/main/solenv/bin/macosx-codesign.sh @@ -145,16 +145,7 @@ sign_app() { ;; esac done < <(find "$app" -type f -print0 | xargs -0 file --no-pad --print0 -- 2>/dev/null) - if [ ${#machos[@]} -gt 0 ]; then - local bad_load_commands - bad_load_commands=$(printf '%s\0' "${machos[@]}" | xargs -0 otool -L 2>/dev/null | - grep -E 'python-inst|@_______' || true) - if [ -n "$bad_load_commands" ]; then - echo "unrelocated Mach-O load commands in $app:" >&2 - echo "$bad_load_commands" >&2 - return 1 - fi - fi + "$SRCDIR/macosx-check-load-commands.sh" "$app" # codesign rewrites a Mach-O through a temporary file beside it, so the # containing directory has to be writable as well. if [ ${#machos[@]} -gt 0 ]; then diff --git a/main/solenv/bin/modules/installer/simplepackage.pm b/main/solenv/bin/modules/installer/simplepackage.pm index eb0a342425..4d3da52361 100644 --- a/main/solenv/bin/modules/installer/simplepackage.pm +++ b/main/solenv/bin/modules/installer/simplepackage.pm @@ -471,11 +471,14 @@ sub create_package $installer::logger::Lang->print($infoline); } + # codesign treats loose files in Contents as unsigned nested code. + my $resourcesfolder = $contentsfolder . "/Resources"; my $sourcefile = $appfolder . "/" . $tarballname; - my $destfile = $contentsfolder . "/" . $tarballname; + my $destfile = $resourcesfolder . "/" . $tarballname; installer::systemactions::remove_complete_directory($contentsfolder); installer::systemactions::create_directory($contentsfolder); + installer::systemactions::create_directory($resourcesfolder); installer::systemactions::copy_one_file($sourcefile, $destfile); unlink($sourcefile); @@ -502,7 +505,7 @@ sub create_package my $translationfileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath( \$translationfilename, $includepatharrayref, 0); if ($$translationfileref eq "") { installer::exiter::exit_program("ERROR: Could not find Apple script translation file $translationfilename!", "create_package"); } - $scriptfilename = $contentsfolder . "/" . $scriptrealfilename; + $scriptfilename = $resourcesfolder . "/" . $scriptrealfilename; my $macosfolder = $contentsfolder . "/MacOS"; installer::systemactions::create_directory($macosfolder); $scripthelperrealfilename = $macosfolder . "/" . $scripthelperrealfilename; @@ -529,9 +532,7 @@ sub create_package my $iconfile = "ooo3_installer.icns"; my $iconfileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath( \$iconfile, $includepatharrayref, 0); if ($$iconfileref eq "") { installer::exiter::exit_program("ERROR: Could not find Apple script icon file $iconfile!", "create_package"); } - my $subdir = $contentsfolder . "/" . "Resources"; - if ( ! -d $subdir ) { installer::systemactions::create_directory($subdir); } - $destfile = $subdir . "/" . $iconfile; + $destfile = $resourcesfolder . "/" . $iconfile; installer::systemactions::copy_one_file($$iconfileref, $destfile); my $infoplistfile = "Info.plist.langpack"; @@ -550,6 +551,13 @@ sub create_package chdir $localfrom; } + # A build-time load path left in a shipped Mach-O breaks at runtime, signed or not. + foreach my $appdir ( glob("$localtempdir/$folder/*.app") ) + { + my $checkreturn = system($ENV{'SOLARENV'} . "/bin/macosx-check-load-commands.sh", $appdir); + if ( $checkreturn ) { installer::exiter::exit_program("ERROR: Unrelocated Mach-O load commands in $appdir!", "create_package"); } + } + # Code-sign the .app before it is sealed into the .dmg. Opt-in: without # MACOSX_CODESIGNING_IDENTITY the installation set is left as it was. if ( $ENV{'MACOSX_CODESIGNING_IDENTITY'} )
