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 dd421d0d84da8ed6ffa5edfb4b6b0262e7b9165d Author: Jim Jagielski <[email protected]> AuthorDate: Thu Sep 10 11:18:37 2026 -0400 macOS: code-signable bundle layout, opt-in signing of the .app and .dmg Select patches from the mac-vcl-button-contrast branch. codesign rejects a bundle with anything but Mach-O binaries in Contents/MacOS, where scp2 installed everything; the installation now lives in Contents/program. Signing only runs with --with-macosx-codesigning-identity (or the env var) set. (cherry picked from commit f3385db031069db3e0655c6a4d85518bf39a7b68) --- main/configure.ac | 30 ++++ main/cppuhelper/source/findsofficepath.c | 21 ++- main/desktop/util/makefile.mk | 6 + main/icu/icu-darwin.patch | 2 +- main/icu/icu4c-4_2_1-src.patch | 2 +- main/libxml2/makefile.mk | 5 +- main/odk/setsdkenv_unix.sh.in | 4 +- main/sal/rtl/source/bootstrap.cxx | 22 +++ main/scp2/source/ooo/common_brand.scp | 44 +++--- main/scp2/source/ooo/directory_ooo.scp | 10 ++ main/scp2/source/ooo/file_library_ooo.scp | 16 +- main/scp2/source/ooo/ooo_brand.scp | 7 +- main/scp2/source/python/file_python.scp | 175 +-------------------- main/scp2/source/python/profileitem_python.scp | 11 -- main/set_soenv.in | 10 ++ main/solenv/bin/macosx-change-install-names.pl | 14 +- main/solenv/bin/macosx-codesign-entitlements.plist | 21 +++ main/solenv/bin/macosx-codesign.sh | 168 ++++++++++++++++++++ main/solenv/bin/modules/installer/scriptitems.pm | 18 ++- main/solenv/bin/modules/installer/simplepackage.pm | 47 +++++- main/solenv/inc/unxmacc.mk | 1 + main/solenv/inc/unxmacx.mk | 1 + 22 files changed, 408 insertions(+), 227 deletions(-) diff --git a/main/configure.ac b/main/configure.ac index 759503d909..f80d9267bb 100644 --- a/main/configure.ac +++ b/main/configure.ac @@ -982,6 +982,21 @@ AC_ARG_WITH(macosx-sdk, Usage: --with-macosx-sdk=10.11 (or full path: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk) ],with_macosx_sdk=$withval,) +AC_ARG_WITH(macosx-codesigning-identity, +[ --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. + + Usage: --with-macosx-codesigning-identity="Developer ID Application: ..." +],with_macosx_codesigning_identity=$withval,) +AC_ARG_WITH(macosx-codesigning-keychain, +[ --with-macosx-codesigning-keychain + Keychain holding that identity (default: the + keychain search list, i.e. normally the login keychain). + + Usage: --with-macosx-codesigning-keychain=/path/to/build.keychain-db +],with_macosx_codesigning_keychain=$withval,) AC_ARG_WITH(rat-scan, [ --with-rat-scan Scan source code for license headers. Use as --with-rat-scan to automatically download pre-built Rat binaries. @@ -4575,9 +4590,24 @@ if test "$_os" = "Darwin"; then fi fi +dnl Code signing during packaging: solenv/bin/modules/installer/simplepackage.pm +dnl reads these from the environment (via set_soenv), so a bare "yes" is an error. +MACOSX_CODESIGNING_IDENTITY= +MACOSX_CODESIGNING_KEYCHAIN= +if test "$with_macosx_codesigning_identity" = "yes"; then + AC_MSG_ERROR([--with-macosx-codesigning-identity needs a codesign identity, or "-" for ad-hoc]) +fi +if test -n "$with_macosx_codesigning_identity" -a "$with_macosx_codesigning_identity" != "no"; then + MACOSX_CODESIGNING_IDENTITY=$with_macosx_codesigning_identity +fi +if test -n "$with_macosx_codesigning_keychain" -a "$with_macosx_codesigning_keychain" != "no" -a "$with_macosx_codesigning_keychain" != "yes"; then + MACOSX_CODESIGNING_KEYCHAIN=$with_macosx_codesigning_keychain +fi AC_SUBST(MACOSX_DEPLOYMENT_TARGET) AC_SUBST(MACOSX_SDK_PATH) AC_SUBST(SDKROOT) +AC_SUBST(MACOSX_CODESIGNING_IDENTITY) +AC_SUBST(MACOSX_CODESIGNING_KEYCHAIN) dnl =================================================================== diff --git a/main/cppuhelper/source/findsofficepath.c b/main/cppuhelper/source/findsofficepath.c index eccb7bdeba..93e410adc0 100644 --- a/main/cppuhelper/source/findsofficepath.c +++ b/main/cppuhelper/source/findsofficepath.c @@ -135,13 +135,26 @@ static char* platformSpecific() /* On MacOS we have no soffice link under /usr/bin but the default office location is known and we check this only */ - const char* MACDEFAULTOFFICEPATH = "/Applications/OpenOffice.app/Contents/MacOS"; - const char* MACDEFAULTSOFFICE = "/Applications/OpenOffice.app/Contents/MacOS/soffice"; + /* The installation lives in Contents/program (Contents/MacOS holds only the + launcher, so that the bundle can be code-signed); soffice is reachable + there through a symlink. Before 4.2 everything was in Contents/MacOS, so + fall back to that for an older office -- it has to be tried second, as + the launcher is in Contents/MacOS in both layouts. */ + const char* MACDEFAULTOFFICEPATH = "/Applications/OpenOffice.app/Contents/program"; + const char* MACDEFAULTSOFFICE = "/Applications/OpenOffice.app/Contents/program/soffice"; + const char* MACLEGACYOFFICEPATH = "/Applications/OpenOffice.app/Contents/MacOS"; + const char* MACLEGACYSOFFICE = "/Applications/OpenOffice.app/Contents/MacOS/soffice"; + const char* found = NULL; if ( !access( MACDEFAULTSOFFICE, F_OK ) ) + found = MACDEFAULTOFFICEPATH; + else if ( !access( MACLEGACYSOFFICE, F_OK ) ) + found = MACLEGACYOFFICEPATH; + + if ( found ) { - path = (char*) malloc( strlen(MACDEFAULTOFFICEPATH) + 1 ); - strcpy( path, MACDEFAULTOFFICEPATH); + path = (char*) malloc( strlen(found) + 1 ); + strcpy( path, found ); } return path; #else diff --git a/main/desktop/util/makefile.mk b/main/desktop/util/makefile.mk index 3163142cc9..f19ecad786 100644 --- a/main/desktop/util/makefile.mk +++ b/main/desktop/util/makefile.mk @@ -101,6 +101,12 @@ APP1STACK=10000000 APP5TARGET=soffice APP5NOSAL=TRUE APP5RPATH=BRAND +.IF "$(OS)" == "MACOSX" +# soffice is the bundle launcher and lives alone in Contents/MacOS (only +# Mach-O binaries may, or the bundle cannot be code-signed); the libraries it +# links against are installed in Contents/program. +APP5RPATH=BRANDBIN +.ENDIF # MACOSX APP5OBJS=$(OBJ)$/copyright_ascii_ooo.obj $(OBJ)$/main.obj APP5STDLIBS = $(SALLIB) $(SOFFICELIB) .IF "$(OS)" == "LINUX" diff --git a/main/icu/icu-darwin.patch b/main/icu/icu-darwin.patch index 8ee7976878..4f9df08f3d 100644 --- a/main/icu/icu-darwin.patch +++ b/main/icu/icu-darwin.patch @@ -8,7 +8,7 @@ diff -ru misc/icu/source/data/pkgdataMakefile.in misc/build/icu/source/data/pkgd +# LD_SONAME must end with a path, but on Darwin this does not happen, because +# file source/config/mh-darwin makes it end with FINAL_SO_TARGET, instead of +# MIDDLE_SO_TARGET like other Unixes. -+LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @executable_path/ ++LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @loader_path/ all : clean @echo GENCCODE_ASSEMBLY_TYPE=$(GENCCODE_ASSEMBLY) >> $(OUTPUTFILE) diff --git a/main/icu/icu4c-4_2_1-src.patch b/main/icu/icu4c-4_2_1-src.patch index 572572a3ea..2649f18999 100644 --- a/main/icu/icu4c-4_2_1-src.patch +++ b/main/icu/icu4c-4_2_1-src.patch @@ -56,7 +56,7 @@ diff -ru misc/icu/source/config/mh-darwin misc/build/icu/source/config/mh-darwin ## Compiler switches to embed a library name and version information -LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(notdir $(MIDDLE_SO_TARGET)) -+LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @executable_path/$(notdir $(FINAL_SO_TARGET)) ++LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @loader_path/$(notdir $(FINAL_SO_TARGET)) ## Compiler switch to embed a runtime search path LD_RPATH= diff --git a/main/libxml2/makefile.mk b/main/libxml2/makefile.mk index 8f5a105406..3716ac0fbf 100644 --- a/main/libxml2/makefile.mk +++ b/main/libxml2/makefile.mk @@ -118,7 +118,10 @@ OUTDIR2INC=include$/libxml .IF "$(OS)"=="MACOSX" EXTRPATH=URELIB OUT2LIB+=.libs$/libxml2.a -OUT2BIN+=.libs$/xmllint +# With --enable-shared=no (see CONFIGURE_FLAGS above), libtool links xmllint +# directly against the static lib -- no .libs/ wrapper copy -- so the binary +# lands at the build root. (Cf. libxslt/makefile.mk's xsltproc handling.) +OUT2BIN+=xmllint OUT2BIN+=xml2-config .ELIF "$(OS)"=="WNT" .IF "$(COM)"=="GCC" diff --git a/main/odk/setsdkenv_unix.sh.in b/main/odk/setsdkenv_unix.sh.in index 8390533d7d..0f9baf81ca 100644 --- a/main/odk/setsdkenv_unix.sh.in +++ b/main/odk/setsdkenv_unix.sh.in @@ -101,7 +101,9 @@ programdir=program javadir=bin case ${sdk_platform} in darwin*) - programdir="Contents/MacOS" + # The installation lives in Contents/program; Contents/MacOS holds only the + # soffice launcher, so that the bundle can be code-signed. + programdir="Contents/program" javacdir=Commands ;; esac diff --git a/main/sal/rtl/source/bootstrap.cxx b/main/sal/rtl/source/bootstrap.cxx index 35a3a9f8c1..2ca20daa1d 100644 --- a/main/sal/rtl/source/bootstrap.cxx +++ b/main/sal/rtl/source/bootstrap.cxx @@ -271,6 +271,28 @@ static OUString & getIniFileName_Impl() // append config file suffix fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE(""))); + +#ifdef MACOSX + // In an application bundle only Mach-O binaries may live in + // Contents/MacOS -- code signing rejects the bundle otherwise -- + // so the installation itself (rc files, rdbs, the libraries) sits + // in Contents/program, exactly as on the other UNX platforms. + // Look the ini file up there; $ORIGIN is derived from it below, + // which anchors the whole bootstrap chain in the program dir. + OUString macOSDir (RTL_CONSTASCII_USTRINGPARAM("/Contents/MacOS/")); + sal_Int32 nMacOSDir = fileName.lastIndexOf(macOSDir); + if (nMacOSDir >= 0) + { + OUString programName = + fileName.replaceAt(nMacOSDir, macOSDir.getLength(), + OUString(RTL_CONSTASCII_USTRINGPARAM("/Contents/program/"))); + // Fall back to the old location for anything that is not laid + // out this way (a plain bundle, an mdimporter, ...). + ::osl::DirectoryItem item; + if (::osl::DirectoryItem::get(programName, item) == ::osl::DirectoryItem::E_None) + fileName = programName; + } +#endif } static OUString theFileName; diff --git a/main/scp2/source/ooo/common_brand.scp b/main/scp2/source/ooo/common_brand.scp index 42769143ed..ff45b6060a 100644 --- a/main/scp2/source/ooo/common_brand.scp +++ b/main/scp2/source/ooo/common_brand.scp @@ -84,7 +84,7 @@ Module gid_Module_Root_Brand gid_Brand_File_Share_Xdg_StartCenter, gid_Brand_File_Share_Xdg_Writer, gid_Brand_File_Txt_Package); - Unixlinks = (gid_Brand_Unixlink_Program, + Unixlinks = (gid_Brand_Unixlink_Soffice, gid_Brand_Unixlink_Unopkg); // Unixlinks = (gid_Brand_Unixlink_BasisLink, // gid_Brand_Unixlink_Program, @@ -122,11 +122,10 @@ End Directory gid_Brand_Dir_Program #if defined MACOSX ParentID = gid_Dir_Bundle_Contents; - DosName = "MacOS"; #else ParentID = gid_Dir_Brand_Root; - DosName = "program"; #endif + DosName = "program"; End Directory gid_Brand_Dir_Resource @@ -269,29 +268,21 @@ End // UnixLinks -#if defined MACOSX -Unixlink gid_Brand_Unixlink_Urelibs - Dir = gid_Brand_Dir_Program; - Name = "urelibs"; - Target = "../basis-link/ure-link/lib"; -End -#endif - -#if defined MACOSX -Unixlink gid_Brand_Unixlink_Program - BIN_FILE_BODY; - Dir = gid_Dir_Bundle_Contents; - Name = "program"; - Target = "MacOS"; - Styles = (); -End -#endif +// gid_Brand_Unixlink_Urelibs (urelibs -> ../basis-link/ure-link/lib) and +// gid_Brand_Unixlink_Program (Contents/program -> MacOS) are gone: the first +// has pointed at nothing since the basis/ure split was dropped, and +// Contents/program is now the real installation directory. // Files File gid_Brand_File_Bin_Soffice BIN_FILE_BODY; +#if defined MACOSX + // the bundle's CFBundleExecutable, and the only thing in Contents/MacOS + Dir = gid_Dir_Bundle_Contents_MacOS; +#else Dir = gid_Brand_Dir_Program; +#endif Name = EXENAME(soffice); #ifdef WNT Styles = (PACKED, PATCH_SO_NAME); @@ -411,6 +402,19 @@ Unixlink gid_Brand_Unixlink_Unopkg End #endif +// The launcher itself has to sit in Contents/MacOS, but everything that looks +// for the office binary next to the installation -- cppuhelper's +// findsofficepath(), scripts, the SDK -- expects it in the program directory. +#if defined MACOSX +Unixlink gid_Brand_Unixlink_Soffice + BIN_FILE_BODY; + Dir = gid_Brand_Dir_Program; + Name = "soffice"; + Target = "../MacOS/soffice"; + Styles = (); +End +#endif + #ifndef OS2 File gid_Brand_File_Bin_Unopkg BIN_FILE_BODY; diff --git a/main/scp2/source/ooo/directory_ooo.scp b/main/scp2/source/ooo/directory_ooo.scp index b69c6a2079..52fa8869bd 100644 --- a/main/scp2/source/ooo/directory_ooo.scp +++ b/main/scp2/source/ooo/directory_ooo.scp @@ -37,6 +37,16 @@ Directory gid_Dir_Bundle_Contents_Resources_Lang End #endif +// Only Mach-O binaries may live here: code signing refuses to seal a bundle +// with anything else in Contents/MacOS. The installation itself goes to +// Contents/program, so this holds just the soffice launcher. +#ifdef MACOSX +Directory gid_Dir_Bundle_Contents_MacOS + ParentID = gid_Dir_Bundle_Contents; + HostName = "MacOS"; +End +#endif + #ifdef MACOSX Directory gid_Dir_Bundle_Contents_Library ParentID = gid_Dir_Bundle_Contents; diff --git a/main/scp2/source/ooo/file_library_ooo.scp b/main/scp2/source/ooo/file_library_ooo.scp index bae968b757..345048aa2f 100644 --- a/main/scp2/source/ooo/file_library_ooo.scp +++ b/main/scp2/source/ooo/file_library_ooo.scp @@ -1593,16 +1593,16 @@ STD_JAR_FILE( gid_File_Jar_Xsltvalidate, XSLTValidate ) #endif #ifndef SYSTEM_LIBXSLT +// On macOS the bundled libxslt is built static (--enable-shared=no, like +// main/libxml2) and linked into its consumers, so there is no shared +// libxslt to package -- mirror libxml2, which has no scp2 File entry. +#ifndef MACOSX File gid_File_Lib_Xslt TXT_FILE_BODY; Styles = (PACKED); Dir = SCP2_OOO_BIN_DIR; #ifdef UNX - #ifdef MACOSX - Name = STRING(CONCAT4(libxslt,.,LIBXSLT_MAJOR,UNXSUFFIX)); - #else Name = STRING(CONCAT4(libxslt,UNXSUFFIX,.,LIBXSLT_MAJOR)); - #endif #else #ifdef _gcc3 Name = "libxslt-1.dll"; @@ -1612,6 +1612,7 @@ File gid_File_Lib_Xslt #endif End #endif +#endif STD_LIB_FILE( gid_File_Lib_Unoxml, unoxml ) @@ -1791,6 +1792,10 @@ File gid_File_Lib_Openssl Name = "libssl-3.dll"; #elif defined(WNT) && defined(X86_64) Name = "libssl-3-x64.dll"; + #elif defined(MACOSX) + /* macOS versions dylibs as libNAME.V.dylib, not SCP2_URE_DL_VER's + libNAME.dylib.V; match what main/openssl delivers. */ + Name = "libssl.3.dylib"; #else Name = SCP2_URE_DL_VER("ssl", "3"); #endif @@ -1806,6 +1811,9 @@ File gid_File_Lib_Crypto Name = "libcrypto-3.dll"; #elif defined(WNT) && defined(X86_64) Name = "libcrypto-3-x64.dll"; + #elif defined(MACOSX) + /* see gid_File_Lib_Openssl above */ + Name = "libcrypto.3.dylib"; #else Name = SCP2_URE_DL_VER("crypto", "3"); #endif diff --git a/main/scp2/source/ooo/ooo_brand.scp b/main/scp2/source/ooo/ooo_brand.scp index fc617ad70a..d521fbd6d8 100644 --- a/main/scp2/source/ooo/ooo_brand.scp +++ b/main/scp2/source/ooo/ooo_brand.scp @@ -37,11 +37,10 @@ End File gid_File_Txt_Notice TXT_FILE_BODY; -#ifndef MACOSX + // next to LICENSE in the program directory on every platform; on Mac OS X + // it used to go into the bundle's Contents, which cannot be signed with a + // stray file in it Dir = gid_Brand_Dir_Program; -#else - Dir = gid_Dir_Bundle_Contents; -#endif Name = "NOTICE"; Styles = (PACKED); End diff --git a/main/scp2/source/python/file_python.scp b/main/scp2/source/python/file_python.scp index 95f64c09db..7116bb0b2e 100644 --- a/main/scp2/source/python/file_python.scp +++ b/main/scp2/source/python/file_python.scp @@ -112,27 +112,24 @@ File gid_File_Py_Python_Core Dir = gid_Dir_Common_Ure; #endif // Dir = gid_Dir_Program; - #ifdef MACOSX - Name = "OOoPython.framework.zip"; - Styles = (ARCHIVE,USE_INTERNAL_RIGHTS); - #else Name = STRING(CONCAT3(python-core-,PYVERSION,.zip)); Styles = (ARCHIVE); - #endif End #ifdef UNX -#ifndef MACOSX File gid_File_Py_Python_Bin BIN_FILE_BODY; +#if defined MACOSX + Dir = gid_Brand_Dir_Program; +#else Dir = gid_Dir_Common_Ure; +#endif // Dir = gid_Dir_Program; Name = "python.bin"; Styles = (PACKED); End #endif #endif -#endif // Scripting Framework Python script proxy @@ -167,177 +164,17 @@ File gid_File_Share_Registry_Pyuno_Xcd End #ifndef SYSTEM_PYTHON -#ifndef MACOSX File gid_File_Lib_Python_So TXT_FILE_BODY; - Dir = gid_Dir_Common_Ure; -// Dir = gid_Dir_Program; - Name = STRING(PY_FULL_DLL_NAME); - Styles = (PACKED); -End -#else //MACOSX -//directory entries solely to be able to create the symlinks -Directory gid_Dir_PythonFramework #if defined MACOSX Dir = gid_Brand_Dir_Program; #else Dir = gid_Dir_Common_Ure; #endif -// ParentID = gid_Dir_Program; - HostName = "OOoPython.framework"; -End - -Unixlink gid_Unixlink_Python_Headers - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework; - Name = "Headers"; - Target = "Versions/Current/Headers"; - Styles = (); -End - -Unixlink gid_Unixlink_Python_Resources - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework; - Name = "Resources"; - Target = "Versions/Current/Resources"; - Styles = (); -End - -Directory gid_Dir_PythonFramework_Versions - ParentID = gid_Dir_PythonFramework; - HostName = "Versions"; -End - -Unixlink gid_Unixlink_Python_Versions_Current - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions; - Name = "Current"; - Target = STRING(PYMAJMIN); - Styles = (); -End - -Directory gid_Dir_PythonFramework_Versions_ver - ParentID = gid_Dir_PythonFramework_Versions; - HostName = STRING(PYMAJMIN); -End - -Unixlink gid_Unixlink_Python_Versions_ver_Headers - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver; - Name = "Headers"; - Target = STRING(CONCAT2(include/python,PYMAJMIN)); - Styles = (); -End - -Directory gid_Dir_PythonFramework_Versions_ver_bin - ParentID = gid_Dir_PythonFramework_Versions_ver; - HostName = "bin"; -End - -Directory gid_Dir_PythonFramework_Versions_ver_lib - ParentID = gid_Dir_PythonFramework_Versions_ver; - HostName = "lib"; -End - -Directory gid_Dir_PythonFramework_Versions_ver_lib_pythonver - ParentID = gid_Dir_PythonFramework_Versions_ver_lib; - HostName = STRING(CONCAT2(python,PYMAJMIN)); -End - -Directory gid_Dir_PythonFramework_Versions_ver_lib_pythonver_config - ParentID = gid_Dir_PythonFramework_Versions_ver_lib_pythonver; - HostName = "config"; -End - -Unixlink gid_Unixlink_Python_OOoPython - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework; - Name = "OOoPython"; - Target = "Versions/Current/OOoPython"; - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_idle - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "idle"; - Target = STRING(CONCAT2(idle,PYMAJMIN)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_pydoc - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "pydoc"; - Target = STRING(CONCAT2(pydoc,PYMAJMIN)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_python_real - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "python"; - Target = "../Resources/Python.app/Contents/MacOS/OOoPython"; - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_python - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "python"; - Target = STRING(CONCAT2(python,PYMAJMIN)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_pythonconfig - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "python-config"; - Target = STRING(CONCAT3(python,PYMAJMIN,-config)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_pythonw_real - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "pythonw"; - Target = "../Resources/Python.app/Contents/MacOS/OOoPython"; - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_pythonw - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "pythonw"; - Target = STRING(CONCAT2(pythonw,PYMAJMIN)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_bin_smtpdpy - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_bin; - Name = "smtpd.py"; - Target = STRING(CONCAT3(smtpd,PYMAJMIN,.py)); - Styles = (); -End - -Unixlink gid_Unixlink_Python_libpython - BIN_FILE_BODY; - Dir = gid_Dir_PythonFramework_Versions_ver_lib_pythonver_config; +// Dir = gid_Dir_Program; Name = STRING(PY_FULL_DLL_NAME); - Target = "../../../OOoPython"; - Styles = (); + Styles = (PACKED); End -#endif //MACOSX - -//#ifdef WNT -//File gid_File_Lib_Python_So_Brand // Fix for system-python-problem on windows -// TXT_FILE_BODY; -// Dir = gid_Brand_Dir_Program; -// Name = STRING(PY_FULL_DLL_NAME); -// Styles = (PACKED); -//End -//#endif #endif #ifdef UNX diff --git a/main/scp2/source/python/profileitem_python.scp b/main/scp2/source/python/profileitem_python.scp index 4ae2f5b177..00c02d91bd 100644 --- a/main/scp2/source/python/profileitem_python.scp +++ b/main/scp2/source/python/profileitem_python.scp @@ -47,11 +47,7 @@ ProfileItem gid_Profileitem_Pythonloader_Pythonhome Section = "Bootstrap"; Order = 1; Key = "PYUNO_LOADER_PYTHONHOME"; - #ifdef MACOSX - Value = CONCAT2($ORIGIN,"OOoPython.framework"); - #else Value = CONCAT2($ORIGIN/python-core-,PYVERSION); - #endif End #endif @@ -65,17 +61,10 @@ ProfileItem gid_Profileitem_Pythonloader_Pythonpath Value = "$ORIGIN"; #else #ifdef UNX - #ifdef MACOSX - #define FRAMEWORKLIB CONCAT4($ORIGIN/OOoPython.framework/Versions/,PYMAJMIN,/lib/python,PYMAJMIN) - Value = CONCAT3(FRAMEWORKLIB FRAMEWORKLIB, - /lib-dynload FRAMEWORKLIB, - /site-packages $ORIGIN); - #else Value = CONCAT7($ORIGIN/python-core-,PYVERSION, /lib $ORIGIN/python-core-,PYVERSION, /lib/lib-dynload $ORIGIN/python-core-,PYVERSION, /lib/site-packages $ORIGIN); - #endif #else #ifdef _gcc3 Value = STRING(CONCAT7($ORIGIN/python-core-,PYVERSION, diff --git a/main/set_soenv.in b/main/set_soenv.in index 1075bebb81..956c90e445 100644 --- a/main/set_soenv.in +++ b/main/set_soenv.in @@ -1859,6 +1859,16 @@ if ( $platform =~ m/darwin/ ) ToFile( "MACOSX_DEPLOYMENT_TARGET", "@MACOSX_DEPLOYMENT_TARGET@", "e" ); ToFile( "MACOSX_SDK_PATH", "@MACOSX_SDK_PATH@", "e" ); ToFile( "SDKROOT", "@SDKROOT@", "e" ); +# Optional code signing while packaging (simplepackage.pm). Only written when +# configure was given a value, so an identity exported by hand is not clobbered. + if ( "@MACOSX_CODESIGNING_IDENTITY@" ne "" ) + { + ToFile( "MACOSX_CODESIGNING_IDENTITY", "@MACOSX_CODESIGNING_IDENTITY@", "e" ); + } + if ( "@MACOSX_CODESIGNING_KEYCHAIN@" ne "" ) + { + ToFile( "MACOSX_CODESIGNING_KEYCHAIN", "@MACOSX_CODESIGNING_KEYCHAIN@", "e" ); + } ToFile( "LIBINTL_PREFIX", "@LIBINTL_PREFIX@", "e" ); } if ( $platform =~ m/freebsd/ ) diff --git a/main/solenv/bin/macosx-change-install-names.pl b/main/solenv/bin/macosx-change-install-names.pl index ba4a60a91c..eaaea58373 100644 --- a/main/solenv/bin/macosx-change-install-names.pl +++ b/main/solenv/bin/macosx-change-install-names.pl @@ -33,6 +33,10 @@ sub action($$$) 'app/SDK/URELIB' => '@executable_path', 'app/BRAND/URELIB' => '@executable_path', 'app/BRAND/OOO' => '@executable_path', + # BRANDBIN: the executable is the bundle launcher in Contents/MacOS, + # while the libraries are installed in Contents/program. + 'app/BRANDBIN/URELIB' => '@executable_path/../program', + 'app/BRANDBIN/OOO' => '@executable_path/../program', 'app/NONE/URELIB' => '@__VIA_LIBRARY_PATH__', 'app/NONE/OOO' => '@__VIA_LIBRARY_PATH__', 'app/NONE/NONE' => '@__VIA_LIBRARY_PATH__', @@ -40,8 +44,12 @@ sub action($$$) 'shl/OOO/URELIB' => '@loader_path', 'shl/OOO/OOO' => '@loader_path', 'shl/LOADER/LOADER' => '@loader_path', - 'shl/OXT/URELIB' => '@executable_path', - 'shl/BOXT/URELIB' => '@executable_path', + # Extension libraries can live inside the installation or in an + # extension directory, so they cannot use @loader_path; the office + # libraries are always in Contents/program, one level up from the + # launcher in Contents/MacOS and from the helper binaries themselves. + 'shl/OXT/URELIB' => '@executable_path/../program', + 'shl/BOXT/URELIB' => '@executable_path/../program', 'shl/BOXT/OOO' => '@loader_path', 'shl/NONE/URELIB' => '@__VIA_LIBRARY_PATH__', 'shl/NONE/OOO' => '@__VIA_LIBRARY_PATH__', @@ -74,7 +82,7 @@ sub action($$$) } @ARGV == 3 || @ARGV >= 2 && $ARGV[0] eq "extshl" or die - 'Usage: app|shl|extshl UREBIN|URELIB|OOO|SDK|BRAND|OXT|BOXT|NONE|LOADER <filepath>*'; + 'Usage: app|shl|extshl UREBIN|URELIB|OOO|SDK|BRAND|BRANDBIN|OXT|BOXT|NONE|LOADER <filepath>*'; $type = shift @ARGV; $loc = shift @ARGV; if ($type eq "SharedLibrary") diff --git a/main/solenv/bin/macosx-codesign-entitlements.plist b/main/solenv/bin/macosx-codesign-entitlements.plist new file mode 100644 index 0000000000..ca5913f7e6 --- /dev/null +++ b/main/solenv/bin/macosx-codesign-entitlements.plist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <!-- The bundled JVM/Java bridge and Basic runtime generate and run code at runtime. --> + <key>com.apple.security.cs.allow-jit</key> + <true/> + <key>com.apple.security.cs.allow-unsigned-executable-memory</key> + <true/> + <!-- soffice dlopens an external JDK's libjvm.dylib, user-installed UNO extensions + and Python modules; none of those carry our Team ID. --> + <key>com.apple.security.cs.disable-library-validation</key> + <true/> + <!-- sofficerc / the launcher set DYLD_* to find the bundled URE libraries. --> + <key>com.apple.security.cs.allow-dyld-environment-variables</key> + <true/> + <!-- Mail merge, "Send Document as E-Mail" and the installer script drive other apps. --> + <key>com.apple.security.automation.apple-events</key> + <true/> +</dict> +</plist> diff --git a/main/solenv/bin/macosx-codesign.sh b/main/solenv/bin/macosx-codesign.sh new file mode 100755 index 0000000000..57d7762944 --- /dev/null +++ b/main/solenv/bin/macosx-codesign.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# +# Code-sign a built Apache OpenOffice .app (or .dmg) on macOS. +# +# ./solenv/bin/macosx-codesign.sh [options] <OpenOffice.app|installer.dmg> ... +# +# Options: +# -i, --identity ID codesign identity; "-" = ad-hoc (default, or +# $MACOSX_CODESIGNING_IDENTITY when set) +# -e, --entitlements entitlements plist (default: macosx-codesign-entitlements.plist) +# -k, --keychain PATH keychain holding the identity (default: the search list) +# --hardened force hardened runtime even for an ad-hoc signature +# (implied by a real identity) +# --verify only report the current signing state, change nothing +# +# The linker already ad-hoc-signs each Mach-O it produces, which is why the +# binaries load at all on arm64. What it does not do is seal the *bundles*: +# without a _CodeSignature/CodeResources the .app has no identity, Gatekeeper +# rejects it once it carries a quarantine flag, and it can never be notarized. +# +# This signs inside-out -- every Mach-O, then the nested bundles, then the app. +# It relies on the installation being laid out so that Contents/MacOS holds +# only the soffice launcher (see scp2/source/ooo/common_brand.scp); codesign +# refuses to seal a bundle with anything else in there. + +set -euo pipefail + +SRCDIR=$(cd "$(dirname "$0")" && pwd) +IDENTITY="${MACOSX_CODESIGNING_IDENTITY:--}" +ENTITLEMENTS="$SRCDIR/macosx-codesign-entitlements.plist" +KEYCHAIN="${MACOSX_CODESIGNING_KEYCHAIN:-}" +HARDENED=no +VERIFY_ONLY=no +TARGETS=() + +while [ $# -gt 0 ]; do + case "$1" in + -i|--identity) IDENTITY="$2"; shift 2 ;; + -e|--entitlements) ENTITLEMENTS="$2"; shift 2 ;; + -k|--keychain) KEYCHAIN="$2"; shift 2 ;; + --hardened) HARDENED=yes; shift ;; + --verify) VERIFY_ONLY=yes; shift ;; + -h|--help) sed -n '2,25p' "$0"; exit 0 ;; + -*) echo "unknown option: $1" >&2; exit 2 ;; + *) TARGETS+=("$1"); shift ;; + esac +done + +[ ${#TARGETS[@]} -gt 0 ] || { echo "usage: $(basename "$0") [options] <app-or-dmg> ..." >&2; exit 2; } +[ "$IDENTITY" = "-" ] || HARDENED=yes + +sign_one() { + local path="$1"; shift + local args=(--force --sign "$IDENTITY" --timestamp=none) + if [ "$IDENTITY" != "-" ]; then + args=(--force --sign "$IDENTITY" --timestamp) + fi + if [ "$HARDENED" = yes ]; then + args+=(--options runtime --entitlements "$ENTITLEMENTS") + fi + if [ -n "$KEYCHAIN" ]; then + args+=(--keychain "$KEYCHAIN") + fi + codesign "${args[@]}" "$@" "$path" +} + +report() { + local app="$1" + echo "--- $app" + codesign -dv --verbose=2 "$app" 2>&1 | grep -E 'Identifier|Format|CodeDirectory|Authority|TeamIdentifier|Sealed' || true + if codesign --verify --deep --strict "$app" 2>/dev/null; then + echo "verify: OK" + else + echo "verify: FAILED" + codesign --verify --deep --strict --verbose=2 "$app" 2>&1 | tail -5 + fi + spctl --assess --type exec --verbose=4 "$app" 2>&1 | tail -2 || true +} + +# codesign rewrites every Mach-O it signs and writes _CodeSignature/ into +# every bundle it seals; the installer stages files read-only. Open just those +# for writing rather than the whole tree, so the staged UnixRights survive. +open_for_signing() { + local b="$1" d + for d in "$b" "$b/Contents" "$b"/Versions/*/; do + if [ -d "$d" ]; then chmod u+w "$d"; fi + done + find "$b" -maxdepth 3 -name _CodeSignature -exec chmod -R u+w {} + 2>/dev/null || true +} + +sign_app() { + local app="$1" + echo "==> signing $app (identity: $IDENTITY, hardened: $HARDENED)" + + # Quarantine and other xattrs make codesign fail or produce an unstable seal. + xattr -cr "$app" 2>/dev/null || true + + # A bundle's main executable is signed as part of its bundle, not on its own: + # codesign silently redirects such a path to the enclosing bundle. + local bundles=() main_execs=() b exe + while IFS= read -r b; do bundles+=("$b"); done < <(find "$app" \ + \( -name '*.app' -o -name '*.framework' -o -name '*.bundle' \ + -o -name '*.mdimporter' -o -name '*.plugin' -o -name '*.qlgenerator' \) \ + | awk '{ print gsub(/\//,"/") "\t" $0 }' | sort -rn | cut -f2-) + for b in ${bundles[@]+"${bundles[@]}"}; do + exe=$(/usr/libexec/PlistBuddy -c 'Print CFBundleExecutable' "$b/Contents/Info.plist" 2>/dev/null) || continue + main_execs+=("$b/Contents/MacOS/$exe") + done + + # Find every Mach-O with one batched file(1) run instead of a process per + # file (an installation holds ~10k). --print0 emits "path\0: type\n". + local machos=() f type + while IFS= read -r -d '' f && IFS= read -r type; do + case "$type" in ": Mach-O"*) machos+=("$f") ;; esac + done < <(find "$app" -type f -print0 | xargs -0 file --no-pad --print0 -- 2>/dev/null) + # 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 + local dirs=() + for f in "${machos[@]}"; do dirs+=("${f%/*}"); done + chmod u+w "${machos[@]}" + printf '%s\n' "${dirs[@]}" | sort -u | tr '\n' '\0' | xargs -0 chmod u+w + fi + for b in ${bundles[@]+"${bundles[@]}"}; do open_for_signing "$b"; done + + # 1. every Mach-O object, deepest path first + local count=0 + if [ ${#machos[@]} -gt 0 ]; then + while IFS= read -r f; do + case " ${main_execs[*]-} " in *" $f "*) continue ;; esac + sign_one "$f" + count=$((count + 1)) + done < <(printf '%s\n' "${machos[@]}" | awk '{ print gsub(/\//,"/") "\t" $0 }' | sort -rn | cut -f2-) + fi + echo " signed $count Mach-O objects" + + # 2. nested bundles, deepest first, so each seal covers already-signed contents + for b in ${bundles[@]+"${bundles[@]}"}; do + [ "$b" = "$app" ] && continue + sign_one "$b" + echo " sealed nested bundle: ${b#"$app"/}" + done + + # 3. the app bundle itself + sign_one "$app" + echo " sealed $app" + report "$app" +} + +for target in "${TARGETS[@]}"; do + [ -e "$target" ] || { echo "no such path: $target" >&2; exit 1; } + case "$target" in + *.dmg) + if [ "$VERIFY_ONLY" = yes ]; then report "$target"; continue; fi + # A .dmg is signed as a whole; the .app inside must already be signed. + if [ "$IDENTITY" = "-" ]; then + echo "refusing to ad-hoc sign a .dmg (pointless); pass -i <Developer ID>" >&2 + exit 1 + fi + codesign --force --sign "$IDENTITY" --timestamp "$target" + report "$target" + ;; + *) + if [ "$VERIFY_ONLY" = yes ]; then report "$target"; continue; fi + sign_app "$target" + ;; + esac +done diff --git a/main/solenv/bin/modules/installer/scriptitems.pm b/main/solenv/bin/modules/installer/scriptitems.pm index fe002f4a30..9d931573b9 100644 --- a/main/solenv/bin/modules/installer/scriptitems.pm +++ b/main/solenv/bin/modules/installer/scriptitems.pm @@ -1713,12 +1713,20 @@ sub add_License_Files_into_Installdir $newfile->{'modules'} = $installer::globals::rootbrandpackage; } - push(@newfilesarray, $newfile); + # On Mac OS X the installation root is the bundle's Contents + # directory, where only the entries defined by Apple may live -- + # a stray LICENSE or README there makes the bundle unsignable. + # The copy in the installation set (the .dmg root, below) is the + # one users actually see anyway. + if ( ! $installer::globals::ismacosx ) + { + push(@newfilesarray, $newfile); - $installer::logger::Lang->printf( - "New files: Adding file %s for the installation root to the file list. Language: %s\n", - $newfilename, - $defaultlanguage); + $installer::logger::Lang->printf( + "New files: Adding file %s for the installation root to the file list. Language: %s\n", + $newfilename, + $defaultlanguage); + } if ( defined $newfile->{'InstallName'} ) { diff --git a/main/solenv/bin/modules/installer/simplepackage.pm b/main/solenv/bin/modules/installer/simplepackage.pm index 63b78932c3..4e94abf12b 100644 --- a/main/solenv/bin/modules/installer/simplepackage.pm +++ b/main/solenv/bin/modules/installer/simplepackage.pm @@ -541,11 +541,33 @@ sub create_package chdir $localfrom; } - $systemcall = "cd $localtempdir && hdiutil makehybrid -hfs -hfs-openfolder $folder $folder -hfs-volume-name \"$volume_name\" -ov -o $installdir/tmp && hdiutil convert -ov -format UDZO $installdir/tmp.dmg -o $archive && "; + # 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'} ) + { + my $signscript = $ENV{'SOLARENV'} . "/bin/macosx-codesign.sh"; + foreach my $appdir ( glob("$localtempdir/$folder/*.app") ) + { + my $signcall = "$signscript -i \"$ENV{'MACOSX_CODESIGNING_IDENTITY'}\" \"$appdir\""; + my $signreturn = system($signcall); + if ( $signreturn ) { installer::exiter::exit_program("ERROR: Could not code-sign $appdir!", "create_package"); } + $installer::logger::Lang->print("Success: Code-signed $appdir\n"); + } + } + + # "hdiutil makehybrid -hfs" stamps an (empty) com.apple.FinderInfo onto + # every file in the image, which makes "codesign --verify --strict" + # reject the signed application inside the .dmg and would fail + # notarization. "hdiutil create -srcfolder" copies the files as they + # are. (The -hfs-openfolder auto-open it also did is not supported on + # Apple Silicon any more: bless refuses it.) + # "-fs HFS+" is not optional: left to itself "hdiutil create" makes an + # APFS image on recent macOS, which will not mount before 10.12. + # "makehybrid -hfs" always produced HFS+. + $systemcall = "cd $localtempdir && hdiutil create -srcfolder $folder -volname \"$volume_name\" -fs HFS+ -format UDZO -ov $archive"; if (( $ref ne "" ) && ( $$ref ne "" )) { - $systemcall .= "hdiutil unflatten $archive && Rez -a $$ref -o $archive && hdiutil flatten $archive &&"; + $systemcall .= " && hdiutil unflatten $archive && Rez -a $$ref -o $archive && hdiutil flatten $archive"; } - $systemcall .= "rm -f $installdir/tmp.dmg"; } else { @@ -581,6 +603,25 @@ sub create_package { $infoline = "Success: Executed \"$systemcall\" successfully!\n"; $installer::logger::Lang->print($infoline); + # Sign the finished disk image. This completes the chain: the .app + # inside was signed before the image was built, and the image itself + # is signed here. It has to happen at this point rather than earlier - + # the Rez step above rewrites the image to attach the license + # resource, and that would invalidate a signature applied before it. + # + # Skipped for an ad-hoc identity: an ad-hoc signed .dmg buys nothing + # (Gatekeeper rejects it either way) and macosx-codesign.sh refuses + # it outright, which would turn a working ad-hoc build into an error. + if (( $archive =~ /dmg$/ ) && + ( $ENV{'MACOSX_CODESIGNING_IDENTITY'} ) && + ( $ENV{'MACOSX_CODESIGNING_IDENTITY'} ne "-" )) + { + my $signscript = $ENV{'SOLARENV'} . "/bin/macosx-codesign.sh"; + my $signcall = "$signscript -i \"$ENV{'MACOSX_CODESIGNING_IDENTITY'}\" \"$archive\""; + my $signreturn = system($signcall); + if ( $signreturn ) { installer::exiter::exit_program("ERROR: Could not code-sign $archive!", "create_package"); } + $installer::logger::Lang->print("Success: Code-signed $archive\n"); + } } } diff --git a/main/solenv/inc/unxmacc.mk b/main/solenv/inc/unxmacc.mk index 5f2d00fbda..9cb1c134e4 100644 --- a/main/solenv/inc/unxmacc.mk +++ b/main/solenv/inc/unxmacc.mk @@ -185,6 +185,7 @@ LINKFLAGSRUNPATH_UREBIN= LINKFLAGSRUNPATH_OOO=-install_name '@_______OOO/$(@:f)' LINKFLAGSRUNPATH_SDK= LINKFLAGSRUNPATH_BRAND= +LINKFLAGSRUNPATH_BRANDBIN= LINKFLAGSRUNPATH_OXT= LINKFLAGSRUNPATH_BOXT= LINKFLAGSRUNPATH_NONE=-install_name '@_______NONE/$(@:f)' diff --git a/main/solenv/inc/unxmacx.mk b/main/solenv/inc/unxmacx.mk index 0980e03e27..13dfaa9b13 100644 --- a/main/solenv/inc/unxmacx.mk +++ b/main/solenv/inc/unxmacx.mk @@ -194,6 +194,7 @@ LINKFLAGSRUNPATH_UREBIN= LINKFLAGSRUNPATH_OOO=-install_name '@_______OOO/$(@:f)' LINKFLAGSRUNPATH_SDK= LINKFLAGSRUNPATH_BRAND= +LINKFLAGSRUNPATH_BRANDBIN= LINKFLAGSRUNPATH_OXT= LINKFLAGSRUNPATH_BOXT= LINKFLAGSRUNPATH_NONE=-install_name '@_______NONE/$(@:f)'
