Reading your own entitlements

When you’re writing an iOS or macOS app, you typically don’t need to dynamically know what your own entitlements are. However, there are a couple of rare circumstances when it could be Nice To Have.

I recently came across one of those situations. Like most developers, I have a set of core libraries I maintain that I use in my apps. These libraries tend to contain all of the common pieces of code I’ve found to be helpful. It’s like my own private SDK.

For example, I have a Sandbox type that represents a set of on-disk locations:

public class Sandbox {
    
    public static let currentProcess: Sandbox
    
    public let documents: AbsolutePath
    public let caches: AbsolutePath
    public let support: AbsolutePath
    public let temporary: AbsolutePath

    public init(documents: AbsolutePath, caches: AbsolutePath, support: AbsolutePath, defaults: UserDefaults)
    public convenience init?(groupIdentifier: String)
}

I got thinking that it’d be nice to add a static default property on Sandbox that would contain the “default” Sandbox. In a situation where my app doesn’t use a shared group container, the default sandbox would be the current process’s sandbox. If I do have one (or more) shared group containers, then the default sandbox would be the first one listed. This is similar to how CKContainer.default behaves. You could imagine wanting similar behavior if you have a wrapper around the Keychain APIs, for example.

However, entitlements end up getting embedded inside your app binary. They are not a copied-in resource, but are rather a blob of data stuck in your executable file.

After lots of googling and asking friends, I eventually found two resources that were exceptionally helpful:

1️⃣ The first was this StackOverflow.com answer by Cédric Luthi. In it, he shows how to use the dyld APIs to find your executable image, and then iterate through the sections defined in that image until you find the one you’re looking for. In his case, he wanted the LC_UUID section. In order to read your own entitlements, you want the LC_CODE_SIGNATURE section.

Once you’ve found the LC_CODE_SIGNATURE section, you know roughly where in your executable file the entitlements are located. However, the resulting __LINKEDIT section has a largely undocumented format, and it wasn’t until Daniel Jalkut suggested I find the codesign source that I made any progress.

2️⃣ After some targeted googling, I found this source code on opensource.apple.com. This file appears to be a decent amount of the source for the codesign utility, but the really awesome bit is that lc_load_sig function. There, finally, is how to poke around in that special __LINKEDIT section and interpret what’s going on.

Armed with these two pieces of data, we can now build some code that will read the entitlements blob out of your own executable (and once you have the blob, you can run it through NSPropertyListSerialization to parse and introspect it):


#import <Foundation/Foundation.h>
#import <mach-o/dyld.h>
/*
 * Structure of an embedded-signature MultiBlob (called a SuperBlob in the codesign source)
 */
typedef struct __BlobIndex {
    uint32_t type;                   /* type of entry */
    uint32_t offset;                 /* offset of entry */
} CS_Blob;

typedef struct __MultiBlob {
    uint32_t magic;                  /* magic number */
    uint32_t length;                 /* total length of SuperBlob */
    uint32_t count;                  /* number of index entries following */
    CS_Blob index[];                 /* (count) entries */
    /* followed by Blobs in no particular order as indicated by offsets in index */
} CS_MultiBlob;

extern NSData *EntitlementsData(void) {

    // iterate through the headers to find the executable, since only the executable has the entitlements
    const struct mach_header *executableHeader = NULL;
    for (uint32_t i = 0; i < _dyld_image_count() && executableHeader == NULL; i++) {
        const struct mach_header *header = _dyld_get_image_header(i);
        if (header->filetype == MH_EXECUTE) { executableHeader = header; }
    }

    if (executableHeader == NULL) { return nil; }

    // find if it's a 64-bit executable or not
    BOOL is64bit = executableHeader->magic == MH_MAGIC_64 || executableHeader->magic == MH_CIGAM_64;
    uintptr_t cursor = (uintptr_t)executableHeader + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));

    // iterate through the dyld commands to find the "LC_CODE_SIGNATURE" command
    const struct segment_command *segmentCommand = NULL;
    for (uint32_t i = 0; i < executableHeader->ncmds; i++, cursor += segmentCommand->cmdsize) {
        segmentCommand = (struct segment_command *)cursor;
        if (segmentCommand->cmd != LC_CODE_SIGNATURE) { continue; }
    
        const struct linkedit_data_command *dataCommand = (const struct linkedit_data_command *)segmentCommand;
    
        // jump to the offset specified by the command
        uintptr_t dataStart = (uintptr_t)executableHeader + dataCommand->dataoff;
        CS_MultiBlob *multiBlob = (CS_MultiBlob *)dataStart;
        if (ntohl(multiBlob->magic) != 0xfade0cc0) { return nil; }
    
        // iterate through the blobs in this segment until we find the one with the appropriate magic value
        uint32_t count = ntohl(multiBlob->count);
        for (int i = 0; i < count; i++) {
            uintptr_t blobBytes = dataStart + ntohl(multiBlob->index[i].offset);
            uint32_t blobMagic = ntohl(*(uint32_t *)blobBytes);
            if (blobMagic != 0xfade7171) { continue; }
        
            // the first 4 bytes are the magic
            // the next 4 are the length
            // after that is the encoded plist
            uint32_t blobLength = ntohl(*(uint32_t *)(blobBytes + 4));
            return [NSData dataWithBytes:(const void *)(blobBytes + 8) length:(blobLength - 8)];
        }
    }

    return nil;
}

Update

It turns out that the code above only works when you’re deploying to a device. If you’re just building for simulator, then the entitlements actually go in a different location; they’re in the __TEXT segment in a section called __entitlements, and they’re the sole contents of that section. This means reading them is very easy:

NSData *ReadEntitlementsFromTEXT(const struct mach_header *executable) {
    uint32_t dataOffset;
    uint64_t dataLength;
    
    BOOL is64bit = executable->magic == MH_MAGIC_64 || executable->magic == MH_CIGAM_64;
    if (is64bit) {
        const struct section_64 *section = getsectbynamefromheader_64((const struct mach_header_64 *)executable, "__TEXT", "__entitlements");
        dataOffset = section->offset;
        dataLength = section->size;
    } else {
        const struct section *section = getsectbynamefromheader(executable, "__TEXT", "__entitlements");
        dataOffset = section->offset;
        dataLength = (uint64_t)section->size;
    }
    
    uintptr_t dataStart = (uintptr_t)executable + dataOffset;
    return [NSData dataWithBytes:(const void *)dataStart length:dataLength];
}

Related️️ Posts️

A Better MVC, Part 5: An Evolution
Swift Protocols Wishlist
Simplifying Swift framework development
A Better MVC, Part 2: Fixing Encapsulation
A Better MVC, Part 3: Fixing Massive View Controller
A Better MVC, Part 4: Future Directions
A Better MVC, Part 1: The Problems