macOS: Bundled python needs additional entitlements to run third-party extensions
Summary:
The Python bundled into the Mac version of Inkscape does not have the necessary entitlements for third party python scripts to run, speaking in the general case. Only "pure" Python code that does not include .so
files or other library objects can be called by extensions.
This should be corrected by setting additional entitlements for the bundled Python, ideally matching that for "system" installed python.
Steps to reproduce:
Demonstration with unsigned third-party library
We have built an example extension to demonstrate the issue. This example extension does nothing other than import a library (pyclipper, an open source vector graphics library available on PyPi) and return an SVG stub.
Install the attached extension, python_example_unsigned.zip as one would for any extension on a Mac. (This example extension -- built to demonstrate the issue as succinctly as possible -- includes only a cpython-38-darwin.so
and thus is is Mac and Python 3.8 specific.)
Call the extension from Extensions>Python Example..., and observe that there is a traceback, ending in:
ImportError: [...]
code signature in (./example_dependencies/pyclipper.cpython-38-darwin.so)
not valid for use in process using Library Validation: mapped file has no cdhash,
completely unsigned? Code has to be at least ad-hoc signed.
The included file that causing the issue (pyclipper.cpython-38-darwin.so) is the same one that is installed by pip install pyclipper
(if using Python 3.8 on Mac).
Key takeaway 1:
Demonstration with signed and notarized third-party library
We have also signed and notarized the example extension to see if that would fix the issue; it does not.
Install the python_example.zip as one would for any extension on a Mac, perhaps replacing the unsigned one.
Call the extension from Extensions>Python Example..., and observe that there is a traceback, ending in:
ImportError: [...]
code signature in (./example_dependencies/pyclipper.cpython-38-darwin.so)
not valid for use in process using Library Validation: mapping process and
mapped file (non-platform) have different Team IDs
Key takeaway 2:
What should have happened?
The extension should run, returning a fixed stub of SVG contents.
For reference, python3 example.py > out.svg
works on the CLI, on a Mac running Python 3.8.
(Note that running on a different architecture or Python version will cause a ModuleNotFoundError
traceback
Discussion: Library Validation
The key item that the example extension demonstrates is Library Validation, part of the macOS security stack, which ensures that libraries called by a piece of signed software are not altered.
By default, applications cannot run arbitrary code (plugins/extensions) that are not signed with the same team id or signed by Apple. (See: https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW9)
This is ideal for a "closed" application that is not extensible. It does not make as much sense for an application like Inkscape, one that is designed to be open for use with third-party extensions and other types of modifications.
This default behavior can be overridden by using the DisableLibraryValidation entitlement.
Discussion: Current entitlements of Python (system, python.org, and Inkscape) and Inkscape
We can inspect the entitlements of Python, as installed by Python.org, as follows:
codesign -d --entitlements - /Applications/Python\ 3.8/Python\ Launcher.app # Downloaded from python.org
Executable=/Applications/Python 3.8/Python Launcher.app/Contents/MacOS/Python Launcher
with result:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
</dict>
</plist>
Python asserts the four entitlements disable-library-validation, allow-dyld-environment-variables, disable-executable-page-protection, and apple-events.
A copy of Python installed as the system Python ( /Library/Frameworks/Python.framework/Versions/3.8/Python
) lists the same four entitlements. A reasonable interpretation is that these are what allow Python to execute general code downloaded from PyPI.
We can inspect the entitlements of Python, as installed by Python.org, as follows:
% codesign -d --entitlements - /Applications/Inkscape.app/Contents/Frameworks/Python.framework/Versions/3.8/Python # python in inkscape
Executable=/Applications/Inkscape.app/Contents/Frameworks/Python.framework/Versions/3.8/Python
% codesign -d --entitlements - /Applications/Inkscape.app
Executable=/Applications/Inkscape.app/Contents/MacOS/inkscape
There do not appear to be any entitlements at present on Inkscape or its bundled Python.
Discussion: Relevance
Some motivations for considering this change:
-
I personally have been bumping up against this for about six months, unable to use a library (that works just fine out of Inkscape) in an extension. I can assert from first-hand knowledge that this issue affects at least two Inkscape extension developers.
-
A number of extensions would make use of GUIs if they could do so. The TexText project has had to jump through quite a lot of hoops to use a GUI. Opening up the restrictions would make that much easier. I expect that #1566 (closed) will be resolved sometime, but it would be possible for a third-party extension to work around it, were it not for library validation.
-
Most importantly, looking towards the future, Inkscape will stop relying on bundled extensions and switch to an Extensions Manager. In doing so, it will become increasingly important that the extensions be able to run third-party code. This may be things as simple as "import math", which have caused library validation errors in the past ( inbox#835 (closed) inbox#837 (closed) inbox#1238 (closed) inbox#887 (closed) ).
As an aside: There is a security risk to installing third party extensions, but that needs to be handled separately, with a warning to the user. Library validation is probably not a reasonable way to "vet" third party code, if we intend to allow plugins.
Suggested Resolution
(1). Add at least the disable-library-validation entitlement to Inkscape and/or its bundled Python.
(2). Evaluate if the other three entitlements that Python.org uses are helpful.
There are some known issues related to "DYLD" variables ( #637 (closed) ), that may be related to the other entitlements.
Inkscape Version and Operating System:
- Inkscape Version: 1.0.2, 1.1.0-dev (0486c1af)
- Operating System: macOS; this is a macOS specific issue.
- Operating System version: 10.14 (verified on computers running macOS 10.15 and 11 as well)
Credit
Thanks to @shoshber for researching the entitlements issue and writing the example extension to demonstrate this issue.