OTATrustUtilities.c [plain text]
#include "OTATrustUtilities.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/syslimits.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ftw.h>
#include "SecFramework.h"
#include <pthread.h>
#include <sys/param.h>
#include <stdlib.h>
#include <utilities/SecCFRelease.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
#include <Security/SecBasePriv.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecFramework.h>
#include <dispatch/dispatch.h>
#include <CommonCrypto/CommonDigest.h>
#if VERBOSE_LOGGING
static void TestOTALog(const char* sz, ...)
{
va_list va;
va_start(va, sz);
FILE* fp = fopen("/tmp/secd_OTAUtil.log", "a");
if (NULL != fp)
{
vfprintf(fp, sz, va);
fclose(fp);
}
va_end(va);
}
static void TestOTAResourceLog(const char *msg,
CFStringRef resourceName,
CFStringRef resourceType,
CFStringRef subDirName,
CFURLRef url)
{
CFStringRef tmpStr = NULL;
CFIndex maxLength = 0;
char *buf = NULL;
tmpStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("%s (name=%@, type=%@, subdir=%@), url=%@"),
msg, resourceName, resourceType, subDirName, url);
if (tmpStr) {
maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(tmpStr), kCFStringEncodingUTF8) + 1;
buf = (char*) malloc(maxLength);
} else {
TestOTALog("TestOTAResourceLog: failed to create string of length %ld\n", (long)maxLength);
}
if (buf) {
if (CFStringGetCString(tmpStr, buf, (CFIndex)maxLength, kCFStringEncodingUTF8)) {
TestOTALog("%s\n", buf);
}
free(buf);
}
CFReleaseSafe(tmpStr);
}
#else
#define TestOTALog(sz, ...)
#define TestOTAResourceLog(msg, resourceName, resourceType, subDirName, url)
#endif
#if NEW_LOCATION
static const char* kBaseAssetDirectory = "/var/OTAPKI/Assets";
#else
static const char* kBaseAssetDirectory = "/var/Keychains/Assets";
#endif
static const char* kVersionDirectoryNamePrefix = "Version_";
static const char* kNumberString = "%d";
struct index_record
{
unsigned char hash[CC_SHA1_DIGEST_LENGTH];
uint32_t offset;
};
typedef struct index_record index_record;
struct _OpaqueSecOTAPKI
{
CFRuntimeBase _base;
CFSetRef _blackListSet;
CFSetRef _grayListSet;
CFDictionaryRef _allowList;
CFArrayRef _trustedCTLogs;
CFArrayRef _pinningList;
CFArrayRef _escrowCertificates;
CFArrayRef _escrowPCSCertificates;
CFDictionaryRef _evPolicyToAnchorMapping;
CFDictionaryRef _anchorLookupTable;
const char* _anchorTable;
const char* _assetPath;
int _assetVersion;
const char* _validUpdateSnapshot;
const char* _validDatabaseSnapshot;
CFIndex _validSnapshotVersion;
CFIndex _validSnapshotFormat;
};
CFGiblisFor(SecOTAPKI)
static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
{
SecOTAPKIRef otapkiRef = (SecOTAPKIRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTAPKIRef: version %d>"), otapkiRef->_assetVersion);
}
static void SecOTAPKIDestroy(CFTypeRef cf)
{
SecOTAPKIRef otapkiref = (SecOTAPKIRef)cf;
CFReleaseNull(otapkiref->_blackListSet);
CFReleaseNull(otapkiref->_grayListSet);
CFReleaseNull(otapkiref->_escrowCertificates);
CFReleaseNull(otapkiref->_escrowPCSCertificates);
CFReleaseNull(otapkiref->_evPolicyToAnchorMapping);
CFReleaseNull(otapkiref->_anchorLookupTable);
CFReleaseNull(otapkiref->_trustedCTLogs);
CFReleaseNull(otapkiref->_pinningList);
if (otapkiref->_anchorTable) {
free((void *)otapkiref->_anchorTable);
otapkiref->_anchorTable = NULL;
}
if (otapkiref->_assetPath) {
free((void *)otapkiref->_assetPath);
otapkiref->_assetPath = NULL;
}
if (otapkiref->_validUpdateSnapshot) {
free((void *)otapkiref->_validUpdateSnapshot);
otapkiref->_validUpdateSnapshot = NULL;
}
if (otapkiref->_validDatabaseSnapshot) {
free((void *)otapkiref->_validDatabaseSnapshot);
otapkiref->_validDatabaseSnapshot = NULL;
}
}
static CFDataRef SecOTACopyFileContents(const char *path)
{
CFMutableDataRef data = NULL;
int fd = open(path, O_RDONLY, 0666);
if (fd == -1)
{
goto badFile;
}
off_t fsize = lseek(fd, 0, SEEK_END);
if (fsize == (off_t)-1)
{
goto badFile;
}
if (fsize > (off_t)INT32_MAX)
{
goto badFile;
}
data = CFDataCreateMutable(kCFAllocatorDefault, (CFIndex)fsize);
if (NULL == data)
{
goto badFile;
}
CFDataSetLength(data, (CFIndex)fsize);
void *buf = CFDataGetMutableBytePtr(data);
if (NULL == buf)
{
goto badFile;
}
off_t total_read = 0;
while (total_read < fsize)
{
ssize_t bytes_read;
bytes_read = pread(fd, buf, (size_t)(fsize - total_read), total_read);
if (bytes_read == -1)
{
goto badFile;
}
if (bytes_read == 0)
{
goto badFile;
}
total_read += bytes_read;
}
close(fd);
return data;
badFile:
if (fd != -1)
{
close(fd);
}
if (data)
{
CFRelease(data);
}
return NULL;
}
static Boolean PathExists(const char* path, size_t* pFileSize)
{
const char *checked_path = (path) ? path : "";
TestOTALog("In PathExists: checking path \"%s\"\n", checked_path);
Boolean result = false;
struct stat sb;
if (NULL != pFileSize)
{
*pFileSize = 0;
}
int stat_result = stat(checked_path, &sb);
result = (stat_result == 0);
if (result)
{
TestOTALog("In PathExists: stat returned 0 for \"%s\"\n", checked_path);
if (S_ISDIR(sb.st_mode))
{
TestOTALog("In PathExists: \"%s\" is a directory\n", checked_path);
;
}
else
{
TestOTALog("In PathExists: \"%s\" is a file\n", checked_path);
if (NULL != pFileSize)
{
*pFileSize = (size_t)sb.st_size;
}
}
}
#if VERBOSE_LOGGING
else
{
const char *stat_prefix = "In PathExists: stat error";
TestOTALog("%s %d for \"%s\"\n", stat_prefix, stat_result, checked_path);
int local_errno = errno;
switch(local_errno)
{
case EACCES:
TestOTALog("%s EACCES\n", stat_prefix);
break;
case EBADF:
TestOTALog("%s EBADF\n", stat_prefix);
break;
case EFAULT:
TestOTALog("%s EFAULT\n", stat_prefix);
break;
case ELOOP:
TestOTALog("%s ELOOP\n", stat_prefix);
break;
case ENAMETOOLONG:
TestOTALog("%s ENAMETOOLONG\n", stat_prefix);
break;
case ENOENT:
TestOTALog("%s ENOENT (missing?)\n", stat_prefix);
break;
case ENOMEM:
TestOTALog("%s ENOMEM\n", stat_prefix);
break;
case ENOTDIR:
TestOTALog("%s ENOTDIR\n", stat_prefix);
break;
case EOVERFLOW:
TestOTALog("%s EOVERFLOW\n", stat_prefix);
break;
default:
TestOTALog("%s %d\n", stat_prefix, local_errno);
break;
}
}
#endif // #if VERBOSE_LOGGING
return result;
}
static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
int rv = remove(fpath);
return rv;
}
static int rmrf(char *path)
{
const char* p1 = NULL;
char path_buffer[PATH_MAX];
memset(path_buffer, 0, sizeof(path_buffer));
p1 = realpath(path, path_buffer);
if (p1 && !strncmp(path, p1, PATH_MAX))
{
return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
}
return -1;
}
static CFStringRef kSecSystemTrustStoreBundlePath = CFSTR("/System/Library/Security/Certificates.bundle");
CFGiblisGetSingleton(CFBundleRef, SecSystemTrustStoreGetBundle, bundle, ^{
CFStringRef bundlePath = NULL;
#if TARGET_IPHONE_SIMULATOR
char *simulatorRoot = getenv("SIMULATOR_ROOT");
if (simulatorRoot)
bundlePath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s%@"), simulatorRoot, kSecSystemTrustStoreBundlePath);
#endif
if (!bundlePath)
bundlePath = CFRetainSafe(kSecSystemTrustStoreBundlePath);
TestOTAResourceLog("SecSystemTrustStoreGetBundle", bundlePath, NULL, NULL, NULL);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath, kCFURLPOSIXPathStyle, true);
*bundle = (url) ? CFBundleCreate(kCFAllocatorDefault, url) : NULL;
CFReleaseSafe(url);
CFReleaseSafe(bundlePath);
})
static CFURLRef SecSystemTrustStoreCopyResourceURL(CFStringRef resourceName,
CFStringRef resourceType, CFStringRef subDirName)
{
CFURLRef url = NULL;
CFBundleRef bundle = SecSystemTrustStoreGetBundle();
TestOTALog("SecSystemTrustStoreCopyResourceURL: bundle = %p\n", (void*)bundle);
if (bundle) {
url = CFBundleCopyResourceURL(bundle, resourceName,
resourceType, subDirName);
if (!url) {
secwarning("resource: %@.%@ in %@ not found", resourceName,
resourceType, subDirName);
}
}
if (!url) {
TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: unable to get URL!",
resourceName, resourceType, subDirName, url);
} else {
TestOTAResourceLog("SecSystemTrustStoreCopyResourceURL: got URL from bundle",
resourceName, resourceType, subDirName, url);
}
return url;
}
static CFDataRef SecSystemTrustStoreCopyResourceContents(CFStringRef resourceName,
CFStringRef resourceType, CFStringRef subDirName)
{
CFURLRef url = SecSystemTrustStoreCopyResourceURL(resourceName, resourceType, subDirName);
CFDataRef data = NULL;
if (url) {
SInt32 error;
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
url, &data, NULL, NULL, &error)) {
secwarning("read: %ld", (long) error);
}
CFRelease(url);
}
TestOTALog("SecSystemTrustStoreCopyResourceContents: data = %p\n", data);
return data;
}
static CFPropertyListRef CFPropertyListCopyFromAsset(const char *ota_assets_path, CFStringRef asset)
{
CFPropertyListRef plist = NULL;
CFDataRef xmlData = NULL;
if (ota_assets_path) {
CFStringRef filePath = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/%@.%@"), ota_assets_path, asset, CFSTR("plist"));
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
plist = CFPropertyListReadFromFile(url);
CFReleaseSafe(url);
CFReleaseSafe(filePath);
}
if (!plist) {
xmlData = SecSystemTrustStoreCopyResourceContents(asset, CFSTR("plist"), NULL);
if (xmlData) {
plist = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
CFRelease(xmlData);
}
}
return plist;
}
static CFSetRef CFSetCreateFromPropertyList(CFPropertyListRef plist)
{
CFSetRef result = NULL;
if (plist) {
CFMutableSetRef tempSet = NULL;
if (CFGetTypeID(plist) == CFArrayGetTypeID()) {
tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
if (NULL == tempSet) {
return result;
}
CFArrayRef array = (CFArrayRef)plist;
CFIndex num_keys = CFArrayGetCount(array);
for (CFIndex idx = 0; idx < num_keys; idx++) {
CFDataRef data = (CFDataRef)CFArrayGetValueAtIndex(array, idx);
CFSetAddValue(tempSet, data);
}
}
else {
return result;
}
if (NULL != tempSet) {
result = tempSet;
}
}
return result;
}
static const char* InitOTADirectory(int* pAssetVersion)
{
TestOTALog("In InitOTADirectory\n");
const char* result = NULL;
char buffer[PATH_MAX];
DIR *dp;
struct dirent *ep;
int version = 0;
int current_version = 0;
int system_asset_version = 0;
CFIndex asset_number = 0;
CFDataRef assetVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("AssetVersion"), CFSTR("plist"), NULL);
if (NULL != assetVersionData)
{
CFPropertyListFormat propFormat;
CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, assetVersionData, 0, &propFormat, NULL);
if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
{
CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("VersionNumber"));
if (NULL != versionNumber)
{
CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &asset_number);
system_asset_version = (int)asset_number;
}
}
CFReleaseSafe(versionPlist);
CFReleaseSafe(assetVersionData);
}
bool assetDirectoryExists = PathExists(kBaseAssetDirectory, NULL);
if (assetDirectoryExists)
{
TestOTALog("InitOTADirectory: \"%s\" exists\n", kBaseAssetDirectory);
dp = opendir (kBaseAssetDirectory);
if (NULL != dp)
{
TestOTALog("InitOTADirectory: opendir sucessfully open \"%s\"\n", kBaseAssetDirectory);
while ((ep = readdir(dp)))
{
TestOTALog("InitOTADirectory: processing name \"%s\"\n", ep->d_name);
if (strstr(ep->d_name, kVersionDirectoryNamePrefix))
{
TestOTALog("InitOTADirectory: \"%s\" matches\n", ep->d_name);
memset(buffer, 0, sizeof(buffer));
snprintf(buffer, sizeof(buffer), "%s%s", kVersionDirectoryNamePrefix, kNumberString);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
sscanf(ep->d_name, buffer, &version);
#pragma clang diagnostic pop
TestOTALog("InitOTADirectory: version = %d\n", version);
if (current_version > 0)
{
if (version > current_version)
{
memset(buffer, 0, sizeof(buffer));
snprintf(buffer, sizeof(buffer), "%s/%s%d", kBaseAssetDirectory, kVersionDirectoryNamePrefix, current_version);
if (PathExists(buffer, NULL))
{
rmrf(buffer);
}
current_version = version;
}
}
else
{
current_version = version;
}
}
}
closedir(dp);
}
else
{
TestOTALog("InitOTADirectory: opendir failed to open %s\n", kBaseAssetDirectory);
}
}
else
{
TestOTALog("InitOTADirectory: PathExists returned false for %s\n", kBaseAssetDirectory);
}
if (current_version > system_asset_version)
{
memset(buffer, 0, sizeof(buffer));
TestOTALog("InitOTADirectory: current_version = %d\n", current_version);
snprintf(buffer, sizeof(buffer), "%s/%s%d", kBaseAssetDirectory, kVersionDirectoryNamePrefix, current_version);
size_t length = strlen(buffer);
char* temp_str = (char*)malloc(length + 1);
memset(temp_str, 0, (length + 1));
strncpy(temp_str, buffer, length);
result = temp_str;
}
else
{
current_version = system_asset_version;
if (NULL != result)
{
free((void *)result);
}
result = NULL;
}
if (NULL != pAssetVersion)
{
*pAssetVersion = current_version;
}
return result;
}
static CF_RETURNS_RETAINED CFSetRef InitializeBlackList(const char* path_ptr)
{
CFPropertyListRef plist = CFPropertyListCopyFromAsset(path_ptr, CFSTR("Blocked"));
CFSetRef result = CFSetCreateFromPropertyList(plist);
CFReleaseSafe(plist);
return result;
}
static CF_RETURNS_RETAINED CFSetRef InitializeGrayList(const char* path_ptr)
{
CFPropertyListRef plist = CFPropertyListCopyFromAsset(path_ptr, CFSTR("GrayListedKeys"));
CFSetRef result = CFSetCreateFromPropertyList(plist);
CFReleaseSafe(plist);
return result;
}
static CF_RETURNS_RETAINED CFArrayRef InitializePinningList(const char* path_ptr)
{
CFPropertyListRef list = CFPropertyListCopyFromAsset(path_ptr, CFSTR("CertificatePinning"));
if (isArray(list)) {
return list;
} else {
CFReleaseNull(list);
return NULL;
}
}
static CF_RETURNS_RETAINED CFDictionaryRef InitializeAllowList(const char* path_ptr)
{
CFPropertyListRef allowList = CFPropertyListCopyFromAsset(path_ptr, CFSTR("Allowed"));
if (allowList && (CFGetTypeID(allowList) == CFDictionaryGetTypeID())) {
return allowList;
} else {
CFReleaseNull(allowList);
return NULL;
}
}
static CF_RETURNS_RETAINED CFArrayRef InitializeTrustedCTLogs(const char* path_ptr)
{
CFPropertyListRef trustedCTLogs = CFPropertyListCopyFromAsset(path_ptr, CFSTR("TrustedCTLogs"));
if (trustedCTLogs && (CFGetTypeID(trustedCTLogs) == CFArrayGetTypeID())) {
return trustedCTLogs;
} else {
CFReleaseNull(trustedCTLogs);
return NULL;
}
}
static CF_RETURNS_RETAINED CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable(const char* path_ptr)
{
CFDictionaryRef result = NULL;
CFPropertyListRef evroots = CFPropertyListCopyFromAsset(path_ptr, CFSTR("EVRoots"));
if (evroots) {
if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) {
result = (CFDictionaryRef)evroots;
}
else {
secwarning("EVRoot.plist is wrong type.");
CFRelease(evroots);
}
}
return result;
}
static CFIndex InitializeValidSnapshotVersion(CFIndex *outFormat)
{
CFIndex validVersion = 0;
CFIndex validFormat = 0;
CFDataRef validVersionData = SecSystemTrustStoreCopyResourceContents(CFSTR("ValidUpdate"), CFSTR("plist"), NULL);
if (NULL != validVersionData)
{
CFPropertyListFormat propFormat;
CFDictionaryRef versionPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, validVersionData, 0, &propFormat, NULL);
if (NULL != versionPlist && CFDictionaryGetTypeID() == CFGetTypeID(versionPlist))
{
CFNumberRef versionNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Version"));
if (NULL != versionNumber)
{
CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &validVersion);
}
CFNumberRef formatNumber = (CFNumberRef)CFDictionaryGetValue(versionPlist, (const void *)CFSTR("Format"));
if (NULL != formatNumber)
{
CFNumberGetValue(formatNumber, kCFNumberCFIndexType, &validFormat);
}
}
CFReleaseSafe(versionPlist);
CFReleaseSafe(validVersionData);
}
if (outFormat) {
*outFormat = validFormat;
}
return validVersion;
}
static const char* InitializeValidSnapshotData(CFStringRef filename_str)
{
char *result = NULL;
const char *base_error_str = "could not get valid snapshot";
CFURLRef valid_url = SecSystemTrustStoreCopyResourceURL(filename_str, CFSTR("sqlite3"), NULL);
if (NULL == valid_url) {
secerror("%s", base_error_str);
} else {
CFStringRef valid_str = CFURLCopyFileSystemPath(valid_url, kCFURLPOSIXPathStyle);
char file_path_buffer[PATH_MAX];
memset(file_path_buffer, 0, PATH_MAX);
if (NULL == valid_str) {
secerror("%s path", base_error_str);
} else {
const char *valid_cstr = CFStringGetCStringPtr(valid_str, kCFStringEncodingUTF8);
if (NULL == valid_cstr) {
if (CFStringGetCString(valid_str, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8)) {
valid_cstr = file_path_buffer;
}
}
if (NULL == valid_cstr) {
secerror("%s path as UTF8 string", base_error_str);
} else {
asprintf(&result, "%s", valid_cstr);
}
}
CFReleaseSafe(valid_str);
}
CFReleaseSafe(valid_url);
if (result && !PathExists(result, NULL)) {
free(result);
result = NULL;
}
return (const char*)result;
}
static const char* InitializeValidUpdateSnapshot()
{
return InitializeValidSnapshotData(CFSTR("update-full"));
}
static const char* InitializeValidDatabaseSnapshot()
{
return InitializeValidSnapshotData(CFSTR("valid"));
}
static void* MapFile(const char* path, int* out_fd, size_t* out_file_size)
{
void* result = NULL;
void* temp_result = NULL;
if (NULL == path || NULL == out_fd || NULL == out_file_size)
{
return result;
}
*out_fd = -1;
*out_file_size = 0;
*out_fd = open(path, O_RDONLY, 0666);
if (*out_fd == -1)
{
return result;
}
off_t fsize = lseek(*out_fd, 0, SEEK_END);
if (fsize == (off_t)-1)
{
return result;
}
if (fsize > (off_t)INT32_MAX)
{
close(*out_fd);
*out_fd = -1;
return result;
}
size_t malloc_size = (size_t)fsize;
temp_result = malloc(malloc_size);
if (NULL == temp_result)
{
close(*out_fd);
*out_fd = -1;
return result;
}
*out_file_size = malloc_size;
off_t total_read = 0;
while (total_read < fsize)
{
ssize_t bytes_read;
bytes_read = pread(*out_fd, temp_result, (size_t)(fsize - total_read), total_read);
if (bytes_read == -1)
{
free(temp_result);
temp_result = NULL;
close(*out_fd);
*out_fd = -1;
return result;
}
if (bytes_read == 0)
{
free(temp_result);
temp_result = NULL;
close(*out_fd);
*out_fd = -1;
return result;
}
total_read += bytes_read;
}
if (NULL != temp_result)
{
result = temp_result;
}
return result;
}
static void UnMapFile(void* mapped_data, size_t data_size)
{
#pragma unused(mapped_data, data_size)
if (NULL != mapped_data)
{
free((void *)mapped_data);
mapped_data = NULL;
}
}
static bool InitializeAnchorTable(const char* path_ptr, CFDictionaryRef* pLookupTable, const char** ppAnchorTable)
{
bool result = false;
if (NULL == pLookupTable || NULL == ppAnchorTable)
{
return result;
}
*pLookupTable = NULL;
*ppAnchorTable = NULL;;
const char* dir_path = NULL;
CFDataRef cert_index_file_data = NULL;
char file_path_buffer[PATH_MAX];
CFURLRef table_data_url = NULL;
CFStringRef table_data_cstr_path = NULL;
const char* table_data_path = NULL;
const index_record* pIndex = NULL;
size_t index_offset = 0;
size_t index_data_size = 0;
CFMutableDictionaryRef anchorLookupTable = NULL;
uint32_t offset_int_value = 0;
CFNumberRef index_offset_value = NULL;
CFDataRef index_hash = NULL;
CFMutableArrayRef offsets = NULL;
Boolean release_offset = false;
char* local_anchorTable = NULL;
size_t local_anchorTableSize = 0;
int local_anchorTable_fd = -1;
dir_path = path_ptr;
if (NULL != dir_path)
{
memset(file_path_buffer, 0, PATH_MAX);
snprintf(file_path_buffer, PATH_MAX, "%s/certsIndex.data", dir_path);
cert_index_file_data = SecOTACopyFileContents(file_path_buffer);
if (NULL != cert_index_file_data)
{
memset(file_path_buffer, 0, PATH_MAX);
snprintf(file_path_buffer, PATH_MAX, "%s/certsTable.data", dir_path);
local_anchorTable = (char *)MapFile(file_path_buffer, &local_anchorTable_fd, &local_anchorTableSize);
}
free((void *)dir_path);
dir_path = NULL;
}
if (NULL == local_anchorTable)
{
CFReleaseSafe(cert_index_file_data);
cert_index_file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
if (!cert_index_file_data) {
secerror("could not find certsIndex");
}
table_data_url = SecSystemTrustStoreCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
if (!table_data_url) {
secerror("could not find certsTable");
}
if (NULL != table_data_url)
{
table_data_cstr_path = CFURLCopyFileSystemPath(table_data_url, kCFURLPOSIXPathStyle);
if (NULL != table_data_cstr_path)
{
memset(file_path_buffer, 0, PATH_MAX);
table_data_path = CFStringGetCStringPtr(table_data_cstr_path, kCFStringEncodingUTF8);
if (NULL == table_data_path)
{
if (CFStringGetCString(table_data_cstr_path, file_path_buffer, PATH_MAX, kCFStringEncodingUTF8))
{
table_data_path = file_path_buffer;
}
}
local_anchorTable = (char *)MapFile(table_data_path, &local_anchorTable_fd, &local_anchorTableSize);
CFReleaseSafe(table_data_cstr_path);
}
}
CFReleaseSafe(table_data_url);
}
if (NULL == local_anchorTable || NULL == cert_index_file_data)
{
if (NULL != local_anchorTable)
{
UnMapFile(local_anchorTable, local_anchorTableSize);
local_anchorTable = NULL;
local_anchorTableSize = 0;
}
CFReleaseSafe(cert_index_file_data);
return result;
}
pIndex = (const index_record*)CFDataGetBytePtr(cert_index_file_data);
index_data_size = CFDataGetLength(cert_index_file_data);
anchorLookupTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (index_offset = index_data_size; index_offset > 0; index_offset -= sizeof(index_record), pIndex++)
{
offset_int_value = pIndex->offset;
index_offset_value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &offset_int_value);
index_hash = CFDataCreate(kCFAllocatorDefault, pIndex->hash, CC_SHA1_DIGEST_LENGTH);
release_offset = false;
offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
if (NULL == offsets)
{
offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
release_offset = true;
}
CFArrayAppendValue(offsets, index_offset_value);
CFDictionarySetValue(anchorLookupTable, index_hash, offsets);
CFRelease(index_offset_value);
CFRelease(index_hash);
if (release_offset)
{
CFRelease(offsets);
}
}
CFRelease(cert_index_file_data);
if (NULL != anchorLookupTable && NULL != local_anchorTable)
{
*pLookupTable = anchorLookupTable;
*ppAnchorTable = local_anchorTable;
result = true;
}
else
{
CFReleaseSafe(anchorLookupTable);
if (NULL != local_anchorTable)
{
UnMapFile(local_anchorTable, local_anchorTableSize);
local_anchorTable = NULL;
local_anchorTableSize = 0;
}
}
return result;
}
static void InitializeEscrowCertificates(const char* path_ptr, CFArrayRef *escrowRoots, CFArrayRef *escrowPCSRoots)
{
CFDataRef file_data = NULL;
const char* dir_path = path_ptr;
if (NULL == dir_path)
{
file_data = SecSystemTrustStoreCopyResourceContents(CFSTR("AppleESCertificates"), CFSTR("plist"), NULL);
}
else
{
char buffer[1024];
memset(buffer, 0, 1024);
snprintf(buffer, 1024, "%s/AppleESCertificates.plist", dir_path);
file_data = SecOTACopyFileContents(buffer);
}
if (NULL != file_data)
{
CFPropertyListFormat propFormat;
CFDictionaryRef certsDictionary = CFPropertyListCreateWithData(kCFAllocatorDefault, file_data, 0, &propFormat, NULL);
if (NULL != certsDictionary && CFDictionaryGetTypeID() == CFGetTypeID((CFTypeRef)certsDictionary))
{
CFArrayRef certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionEscrowKey"));
if (NULL != certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)certs) && CFArrayGetCount(certs) > 0)
{
*escrowRoots = CFArrayCreateCopy(kCFAllocatorDefault, certs);
}
CFArrayRef pcs_certs = (CFArrayRef)CFDictionaryGetValue(certsDictionary, CFSTR("ProductionPCSEscrowKey"));
if (NULL != pcs_certs && CFArrayGetTypeID() == CFGetTypeID((CFTypeRef)pcs_certs) && CFArrayGetCount(pcs_certs) > 0)
{
*escrowPCSRoots = CFArrayCreateCopy(kCFAllocatorDefault, pcs_certs);
}
}
CFReleaseSafe(certsDictionary);
CFRelease(file_data);
}
}
static SecOTAPKIRef SecOTACreate()
{
TestOTALog("In SecOTACreate\n");
SecOTAPKIRef otapkiref = NULL;
otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);
if (NULL == otapkiref)
{
return otapkiref;
}
otapkiref->_blackListSet = NULL;
otapkiref->_grayListSet = NULL;
otapkiref->_allowList = NULL;
otapkiref->_trustedCTLogs = NULL;
otapkiref->_pinningList = NULL;
otapkiref->_escrowCertificates = NULL;
otapkiref->_escrowPCSCertificates = NULL;
otapkiref->_evPolicyToAnchorMapping = NULL;
otapkiref->_anchorLookupTable = NULL;
otapkiref->_anchorTable = NULL;
otapkiref->_assetPath = NULL;
otapkiref->_assetVersion = 0;
otapkiref->_validUpdateSnapshot = NULL;
otapkiref->_validDatabaseSnapshot = NULL;
otapkiref->_validSnapshotVersion = 0;
otapkiref->_validSnapshotFormat = 0;
int asset_version = 0;
const char* path_ptr = InitOTADirectory(&asset_version);
otapkiref->_assetPath = path_ptr;
otapkiref->_assetVersion = asset_version;
TestOTALog("SecOTACreate: asset_path = \"%s\"\n", (path_ptr) ? path_ptr : "");
TestOTALog("SecOTACreate: asset_version = %d\n", asset_version);
CFSetRef blackKeysSet = InitializeBlackList(path_ptr);
if (NULL == blackKeysSet)
{
CFReleaseNull(otapkiref);
return otapkiref;
}
otapkiref->_blackListSet = blackKeysSet;
CFSetRef grayKeysSet = InitializeGrayList(path_ptr);
if (NULL == grayKeysSet)
{
CFReleaseNull(otapkiref);
return otapkiref;
}
otapkiref->_grayListSet = grayKeysSet;
otapkiref->_trustedCTLogs = InitializeTrustedCTLogs(path_ptr);
otapkiref->_pinningList = InitializePinningList(path_ptr);
CFIndex update_format = 0;
otapkiref->_validSnapshotVersion = InitializeValidSnapshotVersion(&update_format);
otapkiref->_validSnapshotFormat = update_format;
otapkiref->_validUpdateSnapshot = InitializeValidUpdateSnapshot();
otapkiref->_validDatabaseSnapshot = InitializeValidDatabaseSnapshot();
CFArrayRef escrowCerts = NULL;
CFArrayRef escrowPCSCerts = NULL;
InitializeEscrowCertificates(path_ptr, &escrowCerts, &escrowPCSCerts);
if (NULL == escrowCerts || NULL == escrowPCSCerts)
{
CFReleaseNull(escrowCerts);
CFReleaseNull(escrowPCSCerts);
CFReleaseNull(otapkiref);
return otapkiref;
}
otapkiref->_escrowCertificates = escrowCerts;
otapkiref->_escrowPCSCertificates = escrowPCSCerts;
CFDictionaryRef evOidToAnchorDigestMap = InitializeEVPolicyToAnchorDigestsTable(path_ptr);
if (NULL == evOidToAnchorDigestMap)
{
CFReleaseNull(otapkiref);
return otapkiref;
}
otapkiref->_evPolicyToAnchorMapping = evOidToAnchorDigestMap;
CFDictionaryRef anchorLookupTable = NULL;
const char* anchorTablePtr = NULL;
if (!InitializeAnchorTable(path_ptr, &anchorLookupTable, &anchorTablePtr))
{
CFReleaseSafe(anchorLookupTable);
if (anchorTablePtr) {
free((void *)anchorTablePtr);
}
CFReleaseNull(otapkiref);
return otapkiref;
}
otapkiref->_anchorLookupTable = anchorLookupTable;
otapkiref->_anchorTable = anchorTablePtr;
return otapkiref;
}
static dispatch_once_t kInitializeOTAPKI = 0;
static const char* kOTAQueueLabel = "com.apple.security.OTAPKIQueue";
static dispatch_queue_t kOTAQueue;
static SecOTAPKIRef kCurrentOTAPKIRef = NULL;
SecOTAPKIRef SecOTAPKICopyCurrentOTAPKIRef()
{
__block SecOTAPKIRef result = NULL;
dispatch_once(&kInitializeOTAPKI,
^{
kOTAQueue = dispatch_queue_create(kOTAQueueLabel, NULL);
kCurrentOTAPKIRef = SecOTACreate();
});
dispatch_sync(kOTAQueue,
^{
result = kCurrentOTAPKIRef;
CFRetainSafe(result);
});
return result;
}
CFSetRef SecOTAPKICopyBlackListSet(SecOTAPKIRef otapkiRef)
{
CFSetRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_blackListSet;
CFRetainSafe(result);
return result;
}
CFSetRef SecOTAPKICopyGrayList(SecOTAPKIRef otapkiRef)
{
CFSetRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_grayListSet;
CFRetainSafe(result);
return result;
}
CFDictionaryRef SecOTAPKICopyAllowList(SecOTAPKIRef otapkiRef)
{
CFDictionaryRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_allowList;
if (!result) {
result = InitializeAllowList(otapkiRef->_assetPath);
otapkiRef->_allowList = result;
}
CFRetainSafe(result);
return result;
}
CFArrayRef SecOTAPKICopyAllowListForAuthKeyID(SecOTAPKIRef otapkiRef, CFStringRef authKeyID)
{
const CFStringRef keyIDs[3] = {
CFSTR("7C724B39C7C0DB62A54F9BAA183492A2CA838259"),
CFSTR("65F231AD2AF7F7DD52960AC702C10EEFA6D53B11"),
CFSTR("D2A716207CAFD9959EEB430A19F2E0B9740EA8C7")
};
CFArrayRef result = NULL;
bool hasAllowList = false;
CFIndex count = (sizeof(keyIDs) / sizeof(keyIDs[0]));
for (CFIndex ix=0; ix<count && authKeyID; ix++) {
if (kCFCompareEqualTo == CFStringCompare(authKeyID, keyIDs[ix], 0)) {
hasAllowList = true;
break;
}
}
if (!hasAllowList || !otapkiRef) {
return result;
}
CFDictionaryRef allowListDict = SecOTAPKICopyAllowList(otapkiRef);
if (!allowListDict) {
return result;
}
result = CFDictionaryGetValue(allowListDict, authKeyID);
CFRetainSafe(result);
CFReleaseSafe(allowListDict);
return result;
}
CFArrayRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef)
{
CFArrayRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_trustedCTLogs;
CFRetainSafe(result);
return result;
}
CFArrayRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
CFArrayRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_pinningList;
CFRetainSafe(result);
return result;
}
CFArrayRef SecOTAPKICopyEscrowCertificates(uint32_t escrowRootType, SecOTAPKIRef otapkiRef)
{
CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (NULL == otapkiRef) {
return result;
}
switch (escrowRootType) {
case kSecCertificateBaselineEscrowRoot:
case kSecCertificateProductionEscrowRoot:
case kSecCertificateBaselineEscrowBackupRoot:
case kSecCertificateProductionEscrowBackupRoot:
if (otapkiRef->_escrowCertificates) {
CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
CFArrayAppendArray(result, escrowCerts, CFRangeMake(0, CFArrayGetCount(escrowCerts)));
}
break;
case kSecCertificateBaselineEscrowEnrollmentRoot:
case kSecCertificateProductionEscrowEnrollmentRoot:
if (otapkiRef->_escrowCertificates) {
static const unsigned char V100EscrowRoot[] = {
0x65,0x5C,0xB0,0x3C,0x39,0x3A,0x32,0xA6,0x0B,0x96,
0x40,0xC0,0xCA,0x73,0x41,0xFD,0xC3,0x9E,0x96,0xB3
};
CFArrayRef escrowCerts = otapkiRef->_escrowCertificates;
CFIndex idx, count = CFArrayGetCount(escrowCerts);
for (idx=0; idx < count; idx++) {
CFDataRef tmpData = (CFDataRef) CFArrayGetValueAtIndex(escrowCerts, idx);
SecCertificateRef tmpCert = (tmpData) ? SecCertificateCreateWithData(NULL, tmpData) : NULL;
CFDataRef sha1Hash = (tmpCert) ? SecCertificateGetSHA1Digest(tmpCert) : NULL;
const uint8_t *dp = (sha1Hash) ? CFDataGetBytePtr(sha1Hash) : NULL;
if (!(dp && !memcmp(V100EscrowRoot, dp, sizeof(V100EscrowRoot))) && tmpData) {
CFArrayAppendValue(result, tmpData);
}
CFReleaseSafe(tmpCert);
}
}
break;
case kSecCertificateBaselinePCSEscrowRoot:
case kSecCertificateProductionPCSEscrowRoot:
if (otapkiRef->_escrowPCSCertificates) {
CFArrayRef escrowPCSCerts = otapkiRef->_escrowPCSCertificates;
CFArrayAppendArray(result, escrowPCSCerts, CFRangeMake(0, CFArrayGetCount(escrowPCSCerts)));
}
break;
default:
break;
}
return result;
}
CFDictionaryRef SecOTAPKICopyEVPolicyToAnchorMapping(SecOTAPKIRef otapkiRef)
{
CFDictionaryRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_evPolicyToAnchorMapping;
CFRetainSafe(result);
return result;
}
CFDictionaryRef SecOTAPKICopyAnchorLookupTable(SecOTAPKIRef otapkiRef)
{
CFDictionaryRef result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_anchorLookupTable;
CFRetainSafe(result);
return result;
}
const char* SecOTAPKIGetAnchorTable(SecOTAPKIRef otapkiRef)
{
const char* result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_anchorTable;
return result;
}
const char* SecOTAPKIGetValidUpdateSnapshot(SecOTAPKIRef otapkiRef)
{
const char* result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_validUpdateSnapshot;
return result;
}
const char* SecOTAPKIGetValidDatabaseSnapshot(SecOTAPKIRef otapkiRef)
{
const char* result = NULL;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_validDatabaseSnapshot;
return result;
}
CFIndex SecOTAPKIGetValidSnapshotVersion(SecOTAPKIRef otapkiRef)
{
CFIndex result = 0;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_validSnapshotVersion;
return result;
}
CFIndex SecOTAPKIGetValidSnapshotFormat(SecOTAPKIRef otapkiRef)
{
CFIndex result = 0;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_validSnapshotFormat;
return result;
}
int SecOTAPKIGetAssetVersion(SecOTAPKIRef otapkiRef)
{
int result = 0;
if (NULL == otapkiRef)
{
return result;
}
result = otapkiRef->_assetVersion;
return result;
}
void SecOTAPKIRefreshData()
{
TestOTALog("In SecOTAPKIRefreshData\n");
SecOTAPKIRef new_otaPKRef = SecOTACreate();
dispatch_sync(kOTAQueue,
^{
CFReleaseSafe(kCurrentOTAPKIRef);
kCurrentOTAPKIRef = new_otaPKRef;
});
}
CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error)
{
CFArrayRef result = NULL;
SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiref)
{
SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
return result;
}
result = SecOTAPKICopyEscrowCertificates(escrowRootType, otapkiref);
CFRelease(otapkiref);
if (NULL == result)
{
SecError(errSecInternal, error, CFSTR("Could not get escrow certificates from the current OTAPKIRef"));
}
return result;
}
int SecOTAPKIGetCurrentAssetVersion(CFErrorRef* error)
{
int result = 0;
SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
if (NULL == otapkiref)
{
SecError(errSecInternal, error, CFSTR("Unable to get the current OTAPKIRef"));
return result;
}
result = otapkiref->_assetVersion;
return result;
}
int SecOTAPKISignalNewAsset(CFErrorRef* error)
{
TestOTALog("SecOTAPKISignalNewAsset has been called!\n");
SecOTAPKIRefreshData();
return 1;
}