508 lines
17 KiB
C
508 lines
17 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#include "CSBlob.h"
|
|
#include "MachO.h"
|
|
#include "MemoryStream.h"
|
|
#include "FileStream.h"
|
|
#include "MachOByteOrder.h"
|
|
#include "Entitlements.h"
|
|
|
|
CFDataRef get_property_list_data(CFPropertyListRef plist) {
|
|
CFDataRef data = NULL;
|
|
CFErrorRef error = NULL;
|
|
|
|
data = CFPropertyListCreateData(
|
|
kCFAllocatorDefault,
|
|
plist,
|
|
kCFPropertyListXMLFormat_v1_0,
|
|
0,
|
|
&error
|
|
);
|
|
|
|
if (data == NULL) {
|
|
if (error != NULL) {
|
|
CFShow(error);
|
|
CFRelease(error);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
CFPropertyListRef create_property_list_from_file(const char *xmlFile) {
|
|
FILE *file = fopen(xmlFile, "rb");
|
|
if (file == NULL) {
|
|
fprintf(stderr, "Error: failed to open entitlements file\n");
|
|
return NULL;
|
|
}
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
size_t entitlementsSize = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
void *entitlements = malloc(entitlementsSize);
|
|
if (entitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlements file\n");
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
if (fread(entitlements, 1, entitlementsSize, file) != entitlementsSize) {
|
|
fprintf(stderr, "Error: failed to read entitlements file\n");
|
|
free(entitlements);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
fclose(file);
|
|
CFDataRef entitlementsData = CFDataCreate(NULL, entitlements, entitlementsSize);
|
|
if (entitlementsData == NULL) {
|
|
fprintf(stderr, "Error: failed to create CFData from entitlements file\n");
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
|
|
CFPropertyListFormat format;
|
|
CFErrorRef error;
|
|
CFPropertyListRef entitlementsPlist = CFPropertyListCreateWithData(NULL, entitlementsData, kCFPropertyListMutableContainersAndLeaves, &format, &error);
|
|
if (entitlementsPlist == NULL) {
|
|
fprintf(stderr, "Error: failed to parse entitlements file\n");
|
|
CFRelease(entitlementsData);
|
|
CFRelease(error);
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
|
|
CFRelease(entitlementsData);
|
|
free(entitlements);
|
|
|
|
return entitlementsPlist;
|
|
}
|
|
|
|
CS_DecodedBlob *create_xml_entitlements_blob(const char *entitlementsFile) {
|
|
|
|
CFPropertyListRef entitlementsPlist = create_property_list_from_file(entitlementsFile);
|
|
if (entitlementsPlist == NULL) {
|
|
fprintf(stderr, "Error: failed to create property list from entitlements file\n");
|
|
return NULL;
|
|
}
|
|
|
|
CFDataRef entitlementsPlistData = get_property_list_data(entitlementsPlist);
|
|
if (entitlementsPlistData == NULL) {
|
|
fprintf(stderr, "Error: failed to convert entitlements property list to data\n");
|
|
CFRelease(entitlementsPlist);
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t *genericBlob = malloc(sizeof(CSMAGIC_EMBEDDED_ENTITLEMENTS) + sizeof(uint32_t) + CFDataGetLength(entitlementsPlistData));
|
|
if (genericBlob == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlements blob\n");
|
|
CFRelease(entitlementsPlist);
|
|
CFRelease(entitlementsPlistData);
|
|
return NULL;
|
|
}
|
|
|
|
// Messy, but it works
|
|
*genericBlob = HOST_TO_BIG(CSMAGIC_EMBEDDED_ENTITLEMENTS);
|
|
uint32_t totalSize = sizeof(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS) + sizeof(uint32_t) + CFDataGetLength(entitlementsPlistData);
|
|
*(genericBlob + 1) = HOST_TO_BIG(totalSize);
|
|
memcpy((char *)genericBlob + sizeof(CSMAGIC_EMBEDDED_ENTITLEMENTS) + sizeof(uint32_t), CFDataGetBytePtr(entitlementsPlistData), CFDataGetLength(entitlementsPlistData));
|
|
|
|
CS_DecodedBlob *entitlementsBlob = csd_blob_init(CSSLOT_ENTITLEMENTS, (CS_GenericBlob *)genericBlob);
|
|
if (entitlementsBlob == NULL) {
|
|
fprintf(stderr, "Error: failed to create entitlements blob\n");
|
|
free(genericBlob);
|
|
}
|
|
|
|
CFRelease(entitlementsPlist);
|
|
CFRelease(entitlementsPlistData);
|
|
|
|
return entitlementsBlob;
|
|
}
|
|
|
|
enum EntitlementType {
|
|
ENTITLEMENT_TYPE_BOOL,
|
|
ENTITLEMENT_TYPE_INT,
|
|
ENTITLEMENT_TYPE_STRING,
|
|
ENTITLEMENT_TYPE_ARRAY,
|
|
};
|
|
|
|
enum ArrayType {
|
|
ARRAY_TYPE_STRING,
|
|
ARRAY_TYPE_INT,
|
|
};
|
|
|
|
typedef struct {
|
|
uint32_t nItems;
|
|
enum ArrayType type;
|
|
void *items;
|
|
} EntitlementArray;
|
|
|
|
typedef struct {
|
|
enum EntitlementType type;
|
|
union {
|
|
bool boolValue;
|
|
int intValue;
|
|
char *stringValue;
|
|
EntitlementArray arrayValue;
|
|
};
|
|
} EntitlementValue;
|
|
|
|
typedef struct {
|
|
char *key;
|
|
EntitlementValue value;
|
|
} Entitlement;
|
|
|
|
typedef struct {
|
|
uint32_t nEntitlements;
|
|
Entitlement *entitlements;
|
|
} Entitlements;
|
|
|
|
void print_entitlement_information(Entitlement *entitlement) {
|
|
printf("Key: %s\n", entitlement->key);
|
|
switch (entitlement->value.type) {
|
|
case ENTITLEMENT_TYPE_BOOL:
|
|
printf("Value: %s\n", entitlement->value.boolValue ? "true" : "false");
|
|
break;
|
|
case ENTITLEMENT_TYPE_INT:
|
|
printf("Value: %d\n", entitlement->value.intValue);
|
|
break;
|
|
case ENTITLEMENT_TYPE_STRING:
|
|
printf("Value: %s\n", entitlement->value.stringValue);
|
|
break;
|
|
case ENTITLEMENT_TYPE_ARRAY:
|
|
printf("Values:\n");
|
|
for (int i = 0; i < entitlement->value.arrayValue.nItems; i++) {
|
|
printf("%d: %s\n", i + 1, ((char **)entitlement->value.arrayValue.items)[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEREncodedItem *der_encode_array(EntitlementArray array) {
|
|
// Encode each item in the array, then create a sequence of the items
|
|
DEREncodedItem **items = malloc(sizeof(DEREncodedItem *) * array.nItems);
|
|
for (int i = 0; i < array.nItems; i++) {
|
|
// Encode the item
|
|
if (array.type == ARRAY_TYPE_STRING) {
|
|
items[i] = der_encode_utf8_string(((char **)array.items)[i]);
|
|
} else {
|
|
items[i] = der_encode_integer(((int *)array.items)[i]);
|
|
}
|
|
}
|
|
|
|
// Create a sequence of the items
|
|
DEREncodedItem *sequence = der_encode_sequence(items, array.nItems);
|
|
if (sequence == NULL) {
|
|
fprintf(stderr, "Error: failed to encode sequence\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Free the encoded items
|
|
for (int i = 0; i < array.nItems; i++) {
|
|
der_free_encoded_item(items[i]);
|
|
}
|
|
free(items);
|
|
|
|
return sequence;
|
|
}
|
|
|
|
DEREncodedItem *der_encode_entitlement(Entitlement entitlement) {
|
|
if (!entitlement.key) {
|
|
fprintf(stderr, "Error: missing entitlement key\n");
|
|
return NULL;
|
|
}
|
|
|
|
DEREncodedItem *key = der_encode_utf8_string(entitlement.key);
|
|
if (key == NULL) {
|
|
fprintf(stderr, "Error: failed to encode entitlement key\n");
|
|
return NULL;
|
|
}
|
|
DEREncodedItem *value = NULL;
|
|
switch (entitlement.value.type) {
|
|
case ENTITLEMENT_TYPE_BOOL:
|
|
value = der_encode_boolean(entitlement.value.boolValue);
|
|
break;
|
|
case ENTITLEMENT_TYPE_INT:
|
|
value = der_encode_integer(entitlement.value.intValue);
|
|
break;
|
|
case ENTITLEMENT_TYPE_STRING:
|
|
value = der_encode_utf8_string(entitlement.value.stringValue);
|
|
break;
|
|
case ENTITLEMENT_TYPE_ARRAY:
|
|
value = der_encode_array(entitlement.value.arrayValue);
|
|
break;
|
|
}
|
|
|
|
if (value == NULL) {
|
|
fprintf(stderr, "Error: failed to encode entitlement value\n");
|
|
der_free_encoded_item(key);
|
|
return NULL;
|
|
}
|
|
|
|
DEREncodedItem *items[] = {key, value};
|
|
|
|
DEREncodedItem *sequence = der_encode_sequence(items, 2);
|
|
if (sequence == NULL) {
|
|
fprintf(stderr, "Error: failed to encode sequence\n");
|
|
der_free_encoded_item(key);
|
|
der_free_encoded_item(value);
|
|
return NULL;
|
|
}
|
|
|
|
der_free_encoded_item(key);
|
|
der_free_encoded_item(value);
|
|
|
|
return sequence;
|
|
}
|
|
|
|
DEREncodedItem *der_encode_entitlements(DEREncodedItem *entitlements[], uint32_t nEntitlements) {
|
|
DEREncodedItem *entitlementsSet = der_encode_set(entitlements, nEntitlements);
|
|
if (entitlementsSet == NULL) {
|
|
fprintf(stderr, "Error: failed to encode set of entitlements\n");
|
|
return NULL;
|
|
}
|
|
|
|
for(int i = 0; i < nEntitlements; i++) {
|
|
der_free_encoded_item(entitlements[i]);
|
|
}
|
|
|
|
return entitlementsSet;
|
|
}
|
|
|
|
static void append_to_entitlements(const void* key, const void* value, void* context) {
|
|
Entitlements *entitlements = (Entitlements *)context;
|
|
|
|
CFStringRef keyString = (CFStringRef)key;
|
|
CFIndex keyLength = CFStringGetLength(keyString);
|
|
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(keyLength, kCFStringEncodingUTF8);
|
|
char *keyCString = malloc(maxSize);
|
|
if (keyCString == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlement key\n");
|
|
return;
|
|
}
|
|
|
|
if (!CFStringGetCString(keyString, keyCString, maxSize, kCFStringEncodingUTF8)) {
|
|
fprintf(stderr, "Error: failed to convert entitlement key to C string\n");
|
|
free(keyCString);
|
|
return;
|
|
}
|
|
|
|
EntitlementValue entitlementValue;
|
|
|
|
CFTypeID valueType = CFGetTypeID(value);
|
|
if (valueType == CFBooleanGetTypeID()) {
|
|
entitlementValue.type = ENTITLEMENT_TYPE_BOOL;
|
|
entitlementValue.boolValue = CFBooleanGetValue((CFBooleanRef)value);
|
|
} else if (valueType == CFNumberGetTypeID()) {
|
|
entitlementValue.type = ENTITLEMENT_TYPE_INT;
|
|
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &entitlementValue.intValue);
|
|
} else if (valueType == CFStringGetTypeID()) {
|
|
entitlementValue.type = ENTITLEMENT_TYPE_STRING;
|
|
CFIndex valueLength = CFStringGetLength((CFStringRef)value);
|
|
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(valueLength, kCFStringEncodingUTF8);
|
|
char *valueCString = malloc(maxSize);
|
|
if (valueCString == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlement value\n");
|
|
free(keyCString);
|
|
return;
|
|
}
|
|
|
|
if (!CFStringGetCString((CFStringRef)value, valueCString, maxSize, kCFStringEncodingUTF8)) {
|
|
fprintf(stderr, "Error: failed to convert entitlement value to C string\n");
|
|
free(keyCString);
|
|
free(valueCString);
|
|
return;
|
|
}
|
|
|
|
entitlementValue.stringValue = valueCString;
|
|
} else if (valueType == CFArrayGetTypeID()) {
|
|
entitlementValue.type = ENTITLEMENT_TYPE_ARRAY;
|
|
CFArrayRef array = (CFArrayRef)value;
|
|
CFIndex nItems = CFArrayGetCount(array);
|
|
char **items = malloc(sizeof(char *) * nItems);
|
|
if (items == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlement array\n");
|
|
free(keyCString);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < nItems; i++) {
|
|
CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, i);
|
|
CFIndex itemLength = CFStringGetLength(item);
|
|
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(itemLength, kCFStringEncodingUTF8);
|
|
char *itemCString = malloc(maxSize);
|
|
if (itemCString == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlement\n");
|
|
for (int j = 0; j < i; j++) {
|
|
free(items[j]);
|
|
}
|
|
free(items);
|
|
free(keyCString);
|
|
return;
|
|
}
|
|
|
|
if (!CFStringGetCString(item, itemCString, maxSize, kCFStringEncodingUTF8)) {
|
|
fprintf(stderr, "Error: failed to convert entitlement to C string\n");
|
|
for (int j = 0; j < i; j++) {
|
|
free(items[j]);
|
|
}
|
|
free(items);
|
|
free(keyCString);
|
|
free(itemCString);
|
|
return;
|
|
}
|
|
|
|
items[i] = itemCString;
|
|
}
|
|
|
|
EntitlementArray arrayValue = {
|
|
.nItems = nItems,
|
|
.type = ARRAY_TYPE_STRING,
|
|
.items = items,
|
|
};
|
|
|
|
entitlementValue.arrayValue = arrayValue;
|
|
|
|
} else {
|
|
fprintf(stderr, "Error: unknown entitlement value type\n");
|
|
free(keyCString);
|
|
return;
|
|
}
|
|
|
|
Entitlement newEntitlement = {
|
|
.key = keyCString,
|
|
.value = entitlementValue,
|
|
};
|
|
|
|
// print_entitlement_information(&newEntitlement);
|
|
|
|
entitlements->entitlements[entitlements->nEntitlements] = newEntitlement;
|
|
entitlements->nEntitlements++;
|
|
return;
|
|
}
|
|
|
|
Entitlements *parse_entitlements_from_property_list(const char *entitlementsFile) {
|
|
CFPropertyListRef entitlementsPlist = create_property_list_from_file(entitlementsFile);
|
|
if (entitlementsPlist == NULL) {
|
|
fprintf(stderr, "Error: failed to create property list from entitlements file\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (CFGetTypeID(entitlementsPlist) != CFDictionaryGetTypeID()) {
|
|
fprintf(stderr, "Error: entitlements file is not a dictionary\n");
|
|
CFRelease(entitlementsPlist);
|
|
return NULL;
|
|
}
|
|
|
|
CFDictionaryRef entitlementsDict = (CFDictionaryRef)entitlementsPlist;
|
|
|
|
CFIndex nEntitlements = CFDictionaryGetCount(entitlementsDict);
|
|
|
|
Entitlements *entitlements = malloc(sizeof(Entitlements));
|
|
if (entitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlements\n");
|
|
CFRelease(entitlementsPlist);
|
|
return NULL;
|
|
}
|
|
|
|
entitlements->nEntitlements = 0;
|
|
entitlements->entitlements = malloc(sizeof(Entitlement) * nEntitlements);
|
|
|
|
if (entitlements->entitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlements\n");
|
|
CFRelease(entitlementsPlist);
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
|
|
CFDictionaryApplyFunction(entitlementsDict, append_to_entitlements, entitlements);
|
|
|
|
CFRelease(entitlementsPlist);
|
|
|
|
return entitlements;
|
|
}
|
|
|
|
DEREncodedItem *encode_entitlements_property_list(const char *entitlementsFile) {
|
|
Entitlements *entitlements = parse_entitlements_from_property_list(entitlementsFile);
|
|
if (entitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to parse entitlements\n");
|
|
return NULL;
|
|
}
|
|
|
|
printf("Parsed %d entitlements from entitlements file.\n", entitlements->nEntitlements);
|
|
|
|
DEREncodedItem **encodedEntitlements = malloc(sizeof(DEREncodedItem *) * entitlements->nEntitlements);
|
|
if (encodedEntitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for encoded entitlements\n");
|
|
free(entitlements->entitlements);
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
|
|
for (int i = 0; i < entitlements->nEntitlements; i++) {
|
|
encodedEntitlements[i] = der_encode_entitlement(entitlements->entitlements[i]);
|
|
if (encodedEntitlements[i] == NULL) {
|
|
fprintf(stderr, "Error: failed to encode entitlement\n");
|
|
for (int j = 0; j < i; j++) {
|
|
der_free_encoded_item(encodedEntitlements[j]);
|
|
}
|
|
free(encodedEntitlements);
|
|
free(entitlements->entitlements);
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
DEREncodedItem *entitlementsSet = der_encode_entitlements(encodedEntitlements, entitlements->nEntitlements);
|
|
if (entitlementsSet == NULL) {
|
|
fprintf(stderr, "Error: failed to encode entitlements\n");
|
|
for (int i = 0; i < entitlements->nEntitlements; i++) {
|
|
der_free_encoded_item(encodedEntitlements[i]);
|
|
}
|
|
free(encodedEntitlements);
|
|
free(entitlements->entitlements);
|
|
free(entitlements);
|
|
return NULL;
|
|
}
|
|
free(encodedEntitlements);
|
|
free(entitlements->entitlements);
|
|
free(entitlements);
|
|
|
|
return entitlementsSet;
|
|
}
|
|
|
|
CS_DecodedBlob *create_der_entitlements_blob(const char *entitlementsFile) {
|
|
DEREncodedItem *entitlements = encode_entitlements_property_list(entitlementsFile);
|
|
if (entitlements == NULL) {
|
|
fprintf(stderr, "Error: failed to encode entitlements\n");
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t *genericBlob = malloc(sizeof(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS) + sizeof(uint32_t) + entitlements->length);
|
|
if (genericBlob == NULL) {
|
|
fprintf(stderr, "Error: failed to allocate memory for entitlements blob\n");
|
|
der_free_encoded_item(entitlements);
|
|
return NULL;
|
|
}
|
|
|
|
*genericBlob = HOST_TO_BIG(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS);
|
|
uint32_t totalSize = sizeof(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS) + sizeof(uint32_t) + entitlements->length;
|
|
*(genericBlob + 1) = HOST_TO_BIG(totalSize);
|
|
|
|
memcpy((char *)genericBlob + sizeof(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS) + sizeof(uint32_t), entitlements->data, entitlements->length);
|
|
|
|
CS_DecodedBlob *entitlementsBlob = csd_blob_init(CSSLOT_ENTITLEMENTS, (CS_GenericBlob *)genericBlob);
|
|
if (entitlementsBlob == NULL) {
|
|
fprintf(stderr, "Error: failed to create entitlements blob\n");
|
|
free(genericBlob);
|
|
}
|
|
|
|
der_free_encoded_item(entitlements);
|
|
|
|
return entitlementsBlob;
|
|
} |