The Impact of iOS 8 on App Wrapping
Prior to iOS 8, dynamic linking on iOS has always been a tricky question. For example, there was no way to create iOS dylib targets without modifying Xcode and App Store guidelines prohibit more than one binary in a single app. Yet every iOS app in existence already uses dynamic libraries! In Apple parlance, a framework is a bundle containing among other things a dynamic library. So, for example, when your app links a system framework such as UIKit, it does so dynamically. This has two distinct benefits over static linking - it makes it easier for a single app binary to run on multiple versions of the OS (each of which may have a slightly different version of UIKit) and it obviates the need to include UIKit in its entirety when you distribute an app. In fact, by the time applicationDidFinishLaunching: is called on your app delegate, well over 100 dynamic libraries have been loaded into memory. From the app developer point of view, a prominent use of dynamic linking on iOS is application wrapping, where a third-party dynamic library is distributed along with the application for code injection at runtime. While this is not a possibility for App Store apps, this practice is very common for securing the data of apps distributed in-house.
So, what’s new with iOS 8 and what does it mean for app developers and administrators? As of iOS 5.0.1, the dynamic linker ensures that a library bears some valid code signature before it will load it into memory. This ensures unsigned code cannot be executed by an application. Then with iOS 8, Extensions happened (which is a wonderful thing,) and the rules changed. An Extension is essentially a dynamic library, in the sense that it is distributed as a separate binary which may be separately loaded at runtime by another app. Additionally, Xcode now has a ‘Cocoa Touch Framework’ project template. And with the presence of third-party dynamic libraries there is a need for additional security checks. Specifically, the Apple Mobile File Integrity kernel extension (and it’s userland cohort, amfid) will now check that a dynamic library is signed, has a valid Team Identifier *and* that the Team Identifier matches that of the containing application. And this has a serious effect on app wrapping in the enterprise. A vast majority of legacy wrapped apps signed with enterprise credentials will simply terminate when launched on iOS8. The error? “[deny-mmap] mapped file has no team identifier and is not a platform binary” A little digging reveals that a crash log is generated by dyld, indicating a crash when a call to mmap() attempts to read from a __TEXT section in the dylib to be loaded.
Fortunately, there are ways to deal with this. First of all, re-signing the dylib with a development or ad-hoc profile fixes the issue, the dylib is loaded and the app executes normally. This particular parsing issue seems to only affect enterprise (UniversalDistribution) signing. While a number of Radar tickets have been filed by us and others experiencing similar issues, we also have a way to address this problem for enterprise signed apps. Specifically, we have upgraded our platform so that apps that have been wrapped with Apperian dynamic policies will work on iOS 8 (as well as previous versions of iOS, of course) by the simple process of re-wrapping and re-signing the app and then distributing the update to the user base. While this may sound complicated for the uninitiated, it is, with our platform, an extremely easy and fast process. Wrapping can be done with a simple click of the policies an administrator wants to add to the app, wrapping and signing is done also with the touch of a button, and the apps can be updated to the user base with proactive notifications and mandatory updates, in both managed and unmanaged devices. Our focus on usability and scalability pays off handsomely in a situation like this, in which environmental changes make a large scale and fast change for apps necessary.