Sandbox, signing, and external dylibs

Is this a supported configuration for Mac sandboxed applications as well as Mac App Store applications?


Dynamically link to a dylib in /usr/local/lib/ that may or may not be codesigned, and may or may not have been installed by a third party.


The documentation tells me how to load dylibs from this location, but it's not altogether clear whether or not this is supported in the mixed scenario I describe above. This is the use case I'm trying to resolve:


I currently have a MAS app that is statically bound to a Unix library in the application bundle. It's worked well this way since 2003!


However my strategy is (tentatively) this: I would redeploy this as a dynamic library and use it pretty much as in, in the application bundle, unless a newer version of the dylib is found in the standard system location (/usr/local/lib/).


The thing is, this is a standard Unix tool that can be built by users with cmake or even installed via homebrew, and so there's no guarantee that I can ensure that it's codesigned, let alone codesigned with my keys. I simply want to make it possible for the non-developer user to allow use of his own dylib if present.


Is the scenario above likely to work with sandboxing? MAS won't reject dynamic instead of static libraries (as iPhone App Store seems to do)?


As for technically making it happen, I plan to check userDefaults (user wants this behavior), check that the library exists at the path, and use dlopen with the appropriate path (app path or /usr/local/lib/).


Thanks.

Is the scenario above likely to work with sandboxing? MAS won't reject dynamic instead of static libraries (as iPhone App Store seems to do)?

Just FYI, iOS has supported third-party dynamically-linked libraries for a few releases now. However, your case doesn’t come up on iOS because on iOS the user can’t install a library independently of your app.

As for technically making it happen, I plan to check userDefaults (user wants this behavior), check that the library exists at the path, and use dlopen with the appropriate path (app path or /usr/local/lib/).

I think this will work on current systems but it seems like a bad idea to me. I have two main concerns, one related to code signing and one not:

  • Code signing has the ability to stop your app from loading unsigned code. Security-wise, this is a really good idea because it prevents all sorts of plug-in based exploits. This feature is not turned on right now but it’s not hard to imagine that happening in the future.

  • Presumably you test your app with the built-in implementation of this library. All of that testing goes out the window when you load a user-built implementation. In general I think it’s best to live with, and fix, the bugs that you know about rather than hope that the user has enough skills to install a version of the library that is both stable and binary compatible with your app.

The second point is the kicker for me: unless there’s a really good reason for allowing the user to replace this library, I think it’s much better to ship a stable product.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Having played around with dlopen and managing dylibs manually, it's too much work for what I'm trying to accomplish. Yuck! I'm playing around with another approach right now that's working well with the sandbox, i.e., just letting the dynamic linker do everything it magically does but using @rpaths. This is completely automatic and outside of application control, but the effect is interesting, to say the least.


I share your concerns with the user-built implementation which is why I want to ensure that the built in library is always available in case the users do something wrong. In this particular case users have to be at least sophisticated enough to use fink, Macports, homebrew, or install and use cmake. It's certainly not a feature I'd promote, but since it's been asked for more than once I thought I'd try to make it happen.


Thanks for the feedback. I definitely will stay away from the dlopen, etc., calls. Apple might cripple the dynamic linker, but there's probably little likelihood that they'll break it within the application's own bundle.

I'm playing around with another approach right now that's working well with the sandbox, i.e., just letting the dynamic linker do everything it magically does but using @rpaths.

I think you’ll run into problems there. Specifically, modern versions of the system prevent an app from referencing libraries outside of the app (other than system libraries). See Gatekeeper Changes in OS X v10.10.4 and Later in Technote 2206 OS X Code Signing In Depth

I didn’t bring this up in my first response because you mentioned

dlopen
, and this restriction does not apply to folks using
dlopen
.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hmmm.... seems to be working on my system. I'd seen the Technote, but it indicates, "This check is performed the first time the app is run. It does not apply to libraries the app loads itself using the

dlopen
function. It also does not apply to libraries loaded from paths where libraries are expected to reside, such as
/System
,
/Library
, and
/usr/
."


The bigger issue I see now is that clean El Capitán installs won't have /usr/local/, and new security model won't allow users to create it. I guess I'll have to figure out how Homebrew and MacPorts are dealing with this issue.

You cannot distribute an app through the MAS that depends on custom library being installed first by the user into /usr/local/lib


Your library needs to be moved out of /usr/local/lib into your app's container and change its RPATH to reflect the new location.


1) Copy the library into your source tree as you will have to modify it with a different RPATH.


2) You will have to change the "rpath" using "install_name_tool:

install_name_tool -id "@executable_path/../Frameworks/libFoo.dylib" libFoo.dylib

You only need to do this step once to modify the RPATH.


3) Modify your app's build to reference the library out of your source tree instead of /usr/local/lib

4) Make sure you modify your build to copy the library into the app's bundle (under the Frameworks subdirectory). Make sure the sign library checkbox is checked. (Add a new Build Phase Copy Files; set Destination to Frameworks; add the library from your source tree; turn on Code Sign on Copy)


4) Also: check what that library depends on using "otool"

otool -L libFoo.dylib


If it shows other libraries that are not in Apple's typical locations, those other libraries will have to be moved into your source tree as well.

You might end up having to copy a handful of libraries into your source tree and the app's bundle.

Sandbox, signing, and external dylibs
 
 
Q