How can I check the code signature for a dylib at runtime?

I have an application that uses dylibs as plug-ins. I load these dylib plug-ins using dlopen()/dlsym().


Some of these dylibs are contained in my app bundle; ideally I would be able to dlopen() plugins not in my app bundle as well, but it seems from the codesigning docs that I can't do that.


Some of these dylibs are signed with my developer ID, but some might not be - they will be signed by a business partner's developer ID. Basically we're distributing an app and a partner has a package installer that modifies our app by adding a value-add plug-in.


In my application, is it possible to check the code signature of these dylibs (i.e. like with /usr/bin/codesign)? Without using /usr/bin/codesign at runtime? Is there an API? Am I doing this wrong?

Accepted Reply

fcntl() is just to lock the file so I can do the call to SecStaticCodeCheckValidity() and then dlopen() atomically without someone switching the dylib at just the right time, leaving us loading an unvalidated dylib.

I think that’s unlikely to provide the level of TOCTTOU protection that you need. For example, even if you lock the file someone could rename the parent directory so that path points to a different file.

IMO the only way to get reasonable TOCTTOU protection is to ensure that the file is in a location that’s not writable by potential attackers.

could we somehow create an open-ended constraint on who could create one of these plugins?

Yes, but it’s not easy to do in a Developer ID signed world. Ideally you’d like the signing certificate to include some sort of indication that it was ‘blessed’ by you for this purpose. You can’t do that in a Developer ID world because the signing certificates are issued by Apple. You could use your own CA to issue signing certificates to your partners, and that’d allow you to check that the code was actually signed by one of those partner.

Does Apple have any guidance or policy regarding where application developers should be putting dylib plugins like this in /Library/?

Check out the following sections of the File System Programming Guide:

  • The Library Directory Stores App-Specific Files

  • OS X Library Directory Details

In your case I recommend

/Library/Application Support/YourCompanyName
as your base and, within that, you can break it up however you like.

WARNING I assume you’re targeting 10.7 and later. Older systems don’t necessarily protect

/Library/Application Support/
properly, so you have to jump through extra hoops.

Share and Enjoy

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

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

Replies

In my application, is it possible to check the code signature of these dylibs (i.e. like with /usr/bin/codesign)? Without using /usr/bin/codesign at runtime? Is there an API?

Yes there is. Check out

<Security/SecCode.h>
and
<Security/SecStaticCode.h>
. Chance are you’ll also need a good understanding of
<Security/SecRequirement.h>
.

Share and Enjoy

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

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

Ok - sounds good. So if I'm understanding the API, what I should be doing is

  1. fcntl() the plug-in dylib to prevent races
  2. call SecStaticCodeCreateWithPath() to create a SecStaticCodeRef for the dylib
  3. call SecRequirementCreateWith*() to create a SecRequirementRef, presumeably with a requirement something like what GateKeeper would use? Something like "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] / exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] / exists */"?
  4. call SecStaticCodeCheckValidity() with the SecStaticCodeRef and the SecRequirementRef
  5. If OK, call dlopen()/dlsym()


Is there example code for doing this?


Also, am I correct in assuming that I need to change the designated requirements for my app bundle to make this happen? Currently, when I try to copy a dylib signed by someone else into my app bundle, GateKeeper gives the worst-case "myapp is damaged and cannot be opened. You should move it to the trash" warning message.


Or am I going about this the wrong way? Is there a better place for a third-party partner to put plug-in dylibs? Some place like /Library/ or ~/Library/?

1. fcntl() the plug-in dylib to prevent races

I’m not sure what that means.

3. call SecRequirementCreateWith*() to create a SecRequirementRef, presumeably with a requirement something like what GateKeeper would use?

I would’ve thought you’d want a very specific requirement (the code is signed by your business partner) rather than something generic. Remember that this code will be loaded into your process, so it’ll be able to do anything that your app can do. It makes sense to be quite restrictive here.

Currently, when I try to copy a dylib signed by someone else into my app bundle, GateKeeper gives the worst-case "myapp is damaged and cannot be opened. You should move it to the trash" warning message.

Don’t modify your app bundle. This technique has been a source of problems for decades [1] and code signing is just the final nail in its coffin.

Some place like /Library/ or ~/Library/?

I recommend that you have your business partner put the plug-in somewhere within

/Library
. This has a number of benefits:
  • It avoids modifying your app bundle.

  • It makes it easy for you to find.

  • Their installer can set up permissions such that the plug-in requires admin authentication to change, which means your code signing check doesn’t need to worry about TOCTTOU issues.

Share and Enjoy

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

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

[1] For example, Technote NW12 AppleShare-able Applications and the Resource Manager (from 1987!) indicates that applications shouldn’t write to themselves.

fcntl() is just to lock the file so I can do the call to SecStaticCodeCheckValidity() and then dlopen() atomically without someone switching the dylib at just the right time, leaving us loading an unvalidated dylib.


We actually have a small but open-ended shortlist of partners, one of whom is already prep-ing their installer and others who have expressed interest, that want to add their own plugins. We want to make sure that anyone who makes one of these plugins has a legitimate developer ID, but your answer makes me wonder - could we somehow create an open-ended constraint on who could create one of these plugins? There are uncommitted partners so we can't deploy our application with 'identifier "com.acmecorp.plugin" or identifier "com.soylentcorp.plugin" or identifier "com.umbrellacorp.plugin" or ...'. Is there a better solution to this using the code signing framework?

I've spoken to our product owner, and we've committed to not putting this stuff in our app bundle - the app bundle will be sacred. 😀 Does Apple have any guidance or policy regarding where application developers should be putting dylib plugins like this in /Library/? Should it be under /Library/Application Support/AcmeCorpApp? /Library/AcmeCorpStuff/AcmeCorpApp/ (which seems very messy and anarchic to me)? /Library/Plugins/AcmeCorpApp/?


BTW - thanks for all of your help with this!

fcntl() is just to lock the file so I can do the call to SecStaticCodeCheckValidity() and then dlopen() atomically without someone switching the dylib at just the right time, leaving us loading an unvalidated dylib.

I think that’s unlikely to provide the level of TOCTTOU protection that you need. For example, even if you lock the file someone could rename the parent directory so that path points to a different file.

IMO the only way to get reasonable TOCTTOU protection is to ensure that the file is in a location that’s not writable by potential attackers.

could we somehow create an open-ended constraint on who could create one of these plugins?

Yes, but it’s not easy to do in a Developer ID signed world. Ideally you’d like the signing certificate to include some sort of indication that it was ‘blessed’ by you for this purpose. You can’t do that in a Developer ID world because the signing certificates are issued by Apple. You could use your own CA to issue signing certificates to your partners, and that’d allow you to check that the code was actually signed by one of those partner.

Does Apple have any guidance or policy regarding where application developers should be putting dylib plugins like this in /Library/?

Check out the following sections of the File System Programming Guide:

  • The Library Directory Stores App-Specific Files

  • OS X Library Directory Details

In your case I recommend

/Library/Application Support/YourCompanyName
as your base and, within that, you can break it up however you like.

WARNING I assume you’re targeting 10.7 and later. Older systems don’t necessarily protect

/Library/Application Support/
properly, so you have to jump through extra hoops.

Share and Enjoy

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

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

Sounds good. Thanks.