#include <CoreFoundation/CFRuntime.h>
#include <Carbon/Carbon.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFUnserialize.h>
#include <pthread.h>
#include <bsm/libbsm.h>
#include "SecCode.h"
#include "SecRequirement.h"
#include "SecTask.h"
struct __SecTask {
CFRuntimeBase base;
audit_token_t token;
Boolean entitlementsLoaded;
CFDictionaryRef entitlements;
};
enum {
kSecCodeMagicEntitlement = 0xfade7171,
};
CFTypeID _kSecTaskTypeID = _kCFRuntimeNotATypeID;
static void SecTaskFinalize(CFTypeRef cfTask)
{
SecTaskRef task = (SecTaskRef) cfTask;
if (task->entitlements != NULL) {
CFRelease(task->entitlements);
task->entitlements = NULL;
}
}
static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask)
{
SecTaskRef task = (SecTaskRef) cfTask;
return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("<SecTask %p>"), task);
}
static void SecTaskRegisterClass(void)
{
static const CFRuntimeClass SecTaskClass = {
.version = 0,
.className = "SecTask",
.init = NULL,
.copy = NULL,
.finalize = SecTaskFinalize,
.equal = NULL,
.hash = NULL,
.copyFormattingDesc = NULL,
.copyDebugDesc = SecTaskCopyDebugDescription,
};
_kSecTaskTypeID = _CFRuntimeRegisterClass(&SecTaskClass);
}
CFTypeID SecTaskGetTypeID(void)
{
static pthread_once_t secTaskRegisterClassOnce = PTHREAD_ONCE_INIT;
pthread_once(&secTaskRegisterClassOnce, SecTaskRegisterClass);
return _kSecTaskTypeID;
}
SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t token)
{
CFIndex extra = sizeof(struct __SecTask) - sizeof(CFRuntimeBase);
SecTaskRef task = (SecTaskRef) _CFRuntimeCreateInstance(allocator, SecTaskGetTypeID(), extra, NULL);
if (task != NULL) {
memcpy(&task->token, &token, sizeof(token));
task->entitlementsLoaded = false;
task->entitlements = NULL;
}
return task;
}
static CFDictionaryRef parseEntitlementsFromData(CFDataRef blobData)
{
const struct theBlob {
uint32_t magic;
uint32_t length;
const uint8_t data[];
} *blob = NULL;
CFDictionaryRef entitlements = NULL;
size_t blobDataLen = CFDataGetLength (blobData);
if (blobDataLen <= sizeof(struct theBlob)) goto fin;
blob = (const struct theBlob *) CFDataGetBytePtr (blobData);
if (kSecCodeMagicEntitlement != ntohl(blob->magic)) goto fin;
if (blobDataLen != ntohl(blob->length)) goto fin;
CFDataRef entitlementData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault,
blob->data,
blobDataLen - sizeof(struct theBlob),
kCFAllocatorNull);
if (NULL == entitlementData) goto fin;
entitlements = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
entitlementData,
kCFPropertyListImmutable,
NULL);
if (NULL == entitlements) goto fin;
if (CFGetTypeID(entitlements) != CFDictionaryGetTypeID()) {
CFRelease (entitlements);
entitlements = NULL;
}
fin:
if (NULL != entitlementData) { CFRelease (entitlementData); }
return entitlements;
}
static void SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error)
{
pid_t pid;
CFNumberRef targetPID = NULL;
CFDictionaryRef guestAttributes = NULL;
CFDictionaryRef targetInfo = NULL;
CFDataRef entitlementData = NULL;
SecCodeRef target = NULL;
CFErrorRef cfErr = NULL;
OSStatus ret = noErr;
audit_token_to_au32 (task->token,
NULL,
NULL,
NULL,
NULL,
NULL,
&pid,
NULL,
NULL);
targetPID = CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &pid);
if (NULL == targetPID) {
ret = kPOSIXErrorENOMEM;
goto err;
}
guestAttributes = CFDictionaryCreate (kCFAllocatorDefault,
(const void **)&kSecGuestAttributePid,
(const void **)&targetPID,
1,
NULL,
NULL);
if (NULL == guestAttributes) goto err;
ret = SecCodeCopyGuestWithAttributes (NULL,
guestAttributes,
kSecCSDefaultFlags,
&target);
if (noErr != ret) goto err;
ret = SecCodeCopySigningInformation (target,
kSecCSRequirementInformation,
&targetInfo);
if (noErr != ret || NULL == targetInfo) goto err;
bool gotKey = CFDictionaryGetValueIfPresent (targetInfo,
(const void *)kSecCodeInfoEntitlements,
(const void **)&entitlementData);
if (false == gotKey || NULL == entitlementData) {
ret = kIOReturnInvalid;
goto err;
}
task->entitlements = parseEntitlementsFromData (entitlementData);
if (NULL == task->entitlements) goto err;
task->entitlementsLoaded = true;
err:
if (noErr != ret && NULL != error) {
if (NULL != cfErr) {
*error = cfErr;
} else {
*error = CFErrorCreate(CFGetAllocator(task), kCFErrorDomainMach, ret, NULL);
}
}
if (NULL != targetPID) CFRelease (targetPID);
if (NULL != guestAttributes) CFRelease (guestAttributes);
if (NULL != target) CFRelease (target);
if (NULL != targetInfo) CFRelease (targetInfo);
}
CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error)
{
if (task->entitlementsLoaded == false) {
SecTaskLoadEntitlements(task, error);
}
CFTypeRef value = NULL;
if (task->entitlements != NULL) {
value = CFDictionaryGetValue(task->entitlements, entitlement);
if (value != NULL) {
CFRetain(value);
}
}
return value;
}
CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef entitlements, CFErrorRef *error)
{
if (task->entitlementsLoaded == false) {
SecTaskLoadEntitlements(task, error);
}
CFMutableDictionaryRef values = NULL;
if (task->entitlementsLoaded == true) {
CFIndex i, count = CFArrayGetCount(entitlements);
values = CFDictionaryCreateMutable(CFGetAllocator(task), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (task->entitlements != NULL) {
for (i = 0; i < count; i++) {
CFStringRef entitlement = CFArrayGetValueAtIndex(entitlements, i);
CFTypeRef value = CFDictionaryGetValue(task->entitlements, entitlement);
if (value != NULL) {
CFDictionarySetValue(values, entitlement, value);
}
}
}
}
return values;
}