OTATrustUtilities.c   [plain text]


/*
 * Copyright (c) 2003-2004,2006-2010 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 * OTATrustUtilities.c
 */

#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/SecFramework.h>
#include <dispatch/dispatch.h>
#include <CommonCrypto/CommonDigest.h>

//#define VERBOSE_LOGGING 1

#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);
}

#else

#define TestOTALog(sz, ...)

#endif


//#define NEW_LOCATION 1

#if NEW_LOCATION
static const char*  kBaseAssertDirectory = "/var/OTAPKI/Assets";
#else
static const char*	kBaseAssertDirectory = "/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;
	CFArrayRef			_escrowCertificates;
	CFDictionaryRef		_evPolicyToAnchorMapping;
	CFDictionaryRef		_anchorLookupTable;
	const char*			_anchorTable;
	int					_assetVersion;
};

CFGiblisFor(SecOTAPKI)

static CF_RETURNS_RETAINED CFStringRef SecOTAPKICopyDescription(CFTypeRef cf) 
{
    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->_evPolicyToAnchorMapping);
    CFReleaseNull(otapkiref->_anchorLookupTable);

	free((void *)otapkiref->_anchorTable);
}

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)
{
	TestOTALog("In PathExists: checking path %s\n", path);
	Boolean result = false;
	struct stat         sb;
	
	if (NULL != pFileSize)
	{
		*pFileSize = 0;
	}
	
	int stat_result = stat(path, &sb);
	result = (stat_result == 0);
	
    
    if (result)
    {
		TestOTALog("In PathExists: stat returned 0 for %s\n", path);
        if (S_ISDIR(sb.st_mode))
        {
			TestOTALog("In PathExists: %s is a directory\n", path);
            // It is a directory
            ;
        }
        else
        {
			TestOTALog("In PathExists: %s is a file\n", path);
            // It is a file
            if (NULL != pFileSize)
            {
                *pFileSize = (size_t)sb.st_size;
            }
        }
    }
#if VERBOSE_LOGGING
	else
	{
		TestOTALog("In PathExists: stat returned %d for %s\n", stat_result, path);
		int local_errno = errno;
		switch(local_errno)
		{
			case EACCES:
				TestOTALog("In PathExists: stat failed because of EACCES\n");
				break;
				
			case EBADF:
				TestOTALog("In PathExists: stat failed because of EBADF (Not likely)\n");
				break;
				
			case EFAULT:
				TestOTALog("In PathExists: stat failed because of EFAULT (huh?)\n");
				break;
				
			case ELOOP:
				TestOTALog("In PathExists: stat failed because of ELOOP (huh?)\n");
				break;
				
			case ENAMETOOLONG:
				TestOTALog("In PathExists: stat failed because of ENAMETOOLONG (huh?)\n");
				break;
		
			case ENOENT:
				TestOTALog("In PathExists: stat failed because of ENOENT (missing?)\n");
				break;
				
			case ENOMEM:
				TestOTALog("In PathExists: stat failed because of ENOMEM (really?)\n");
				break;
				
			case ENOTDIR:
				TestOTALog("In PathExists: stat failed because of ENOTDIR (really?)\n");
				break;
				
			case EOVERFLOW:
				TestOTALog("In PathExists: stat failed because of EOVERFLOW (really?)\n");
				break;
				
			default:
				TestOTALog("In PathExists: unknown errno of %d\n", 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 (!strncmp(path, p1, PATH_MAX))
	{
		return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
	}
	return -1;
}

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;
    CFIndex asset_number = 0;
    
	bool assetDirectoryExists = PathExists(kBaseAssertDirectory, NULL);
    if (assetDirectoryExists)
    {
		TestOTALog("InitOTADirectory: %s exists\n", kBaseAssertDirectory);
		dp = opendir (kBaseAssertDirectory);
		if (NULL != dp)
		{
			TestOTALog("InitOTADirectory: opendir sucessfully open %s\n", kBaseAssertDirectory);
			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);

					sscanf(ep->d_name, buffer, &version);
					
					TestOTALog("InitOTADirectory: version = %d\n", version);

					if (current_version > 0)
					{
						if (version > current_version)
						{
							// There is more than one Version_ directory.
							// Delete the one with the smaller version number
							memset(buffer, 0, sizeof(buffer));
							snprintf(buffer,  sizeof(buffer), "%s/%s%d", kBaseAssertDirectory, 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", kBaseAssertDirectory);
		}
	}
	else
	{
		TestOTALog("InitOTADirectory: PathExists returned false for %s\n", kBaseAssertDirectory);
	}
    
	memset(buffer, 0, sizeof(buffer));
   
    if (0 == current_version)
    {
		TestOTALog("InitOTADirectory: current_version = 0\n");
        // No Assets are installed so get the Asset Verson from the AssertVersion.plist in the Security.framework
        
        // Look in the resources for a AssetVerstion.plst file
        
        CFDataRef assetVersionData = SecFrameworkCopyResourceContents(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);
                }
            }
		    CFReleaseSafe(versionPlist);
            CFReleaseSafe(assetVersionData);
        }
        
        current_version = (int)asset_number;
    }
	else
	{
		TestOTALog("InitOTADirectory: current_version = %d\n", current_version);	    	
	    snprintf(buffer, sizeof(buffer), "%s/%s%d", kBaseAssertDirectory, 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;
	}
	
	if (NULL != pAssetVersion)
	{
		*pAssetVersion = current_version;
	}
	return result;
}

static CFSetRef InitializeBlackList(const char* path_ptr)
{
	CFSetRef result = NULL;

	// Check to see if the EVRoots.plist file is in the asset location
	CFDataRef xmlData = NULL;
	const char* asset_path = path_ptr;
	if (NULL == asset_path)
	{
		// There is no OTA asset file so use the file in the Sercurity.framework bundle
		xmlData = SecFrameworkCopyResourceContents(CFSTR("Blocked"), CFSTR("plist"), NULL);
	}
	else
	{
		char file_path_buffer[PATH_MAX];
		memset(file_path_buffer, 0, PATH_MAX);
		snprintf(file_path_buffer, PATH_MAX, "%s/Blocked.plist", asset_path);
        
        xmlData = SecOTACopyFileContents(file_path_buffer);
        
		if (NULL == xmlData)
		{
            xmlData = SecFrameworkCopyResourceContents(CFSTR("Blocked"), CFSTR("plist"), NULL);
		}
	}

	CFPropertyListRef blackKeys = NULL;
    if (xmlData)
	{
        blackKeys = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
        CFRelease(xmlData);
    }

	if (blackKeys)
	{
		CFMutableSetRef tempSet = NULL;
		if (CFGetTypeID(blackKeys) == CFArrayGetTypeID())
		{
			tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
			if (NULL == tempSet)
			{
				CFRelease(blackKeys);
				return result;
			}
			CFArrayRef blackKeyArray = (CFArrayRef)blackKeys;
			CFIndex num_keys = CFArrayGetCount(blackKeyArray);
			for (CFIndex idx = 0; idx < num_keys; idx++)
			{
				CFDataRef key_data = (CFDataRef)CFArrayGetValueAtIndex(blackKeyArray, idx);
				CFSetAddValue(tempSet, key_data);
			}
		}
		else
		{
			CFRelease(blackKeys);
			return result;
		}

		if (NULL != tempSet)
		{
			result = tempSet;
		}
		CFRelease(blackKeys);
    }

	return result;
}

static CFSetRef InitializeGrayList(const char* path_ptr)
{
	CFSetRef result = NULL;
	
	// Check to see if the EVRoots.plist file is in the asset location
	CFDataRef xmlData = NULL;
	const char* asset_path = path_ptr;
	if (NULL == asset_path)
	{
		// There is no updated asset file so use the file in the Sercurity.framework bundle
		xmlData = SecFrameworkCopyResourceContents(CFSTR("GrayListedKeys"), CFSTR("plist"), NULL);
	}
	else
	{
		char file_path_buffer[PATH_MAX];
		memset(file_path_buffer, 0, PATH_MAX);
		snprintf(file_path_buffer, PATH_MAX, "%s/GrayListedKeys.plist", asset_path);

        xmlData = SecOTACopyFileContents(file_path_buffer);
        
		if (NULL == xmlData)
		{
            xmlData = SecFrameworkCopyResourceContents(CFSTR("GrayListedKeys"), CFSTR("plist"), NULL); 
        }
	}

	CFPropertyListRef grayKeys = NULL;
    if (xmlData)
	{
        grayKeys = CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
        CFRelease(xmlData);
    }

	if (grayKeys)
	{
		CFMutableSetRef tempSet = NULL;
		if (CFGetTypeID(grayKeys) == CFArrayGetTypeID())
		{
			tempSet = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
			if (NULL == tempSet)
			{
				CFRelease(grayKeys);
				return result;
			}
			CFArrayRef grayKeyArray = (CFArrayRef)grayKeys;
			CFIndex num_keys = CFArrayGetCount(grayKeyArray);
			for (CFIndex idx = 0; idx < num_keys; idx++)
			{
				CFDataRef key_data = (CFDataRef)CFArrayGetValueAtIndex(grayKeyArray, idx);
				CFSetAddValue(tempSet, key_data);
			}
		}
		else
		{
			CFRelease(grayKeys);
			return result;
		}

		if (NULL != tempSet)
		{
			result = tempSet;
		}
	
		CFRelease(grayKeys);
    }
	return result;
}

static CFDictionaryRef InitializeEVPolicyToAnchorDigestsTable(const char* path_ptr)
{
	CFDictionaryRef result = NULL;

	// Check to see if the EVRoots.plist file is in the asset location
	CFDataRef xmlData = NULL;
	const char* asset_path = path_ptr;
	if (NULL == asset_path)
	{
		// There is no updated asset file so use the file in the Sercurity.framework bundle
		xmlData = SecFrameworkCopyResourceContents(CFSTR("EVRoots"), CFSTR("plist"), NULL);
	}
	else
	{
        char file_path_buffer[PATH_MAX];
        memset(file_path_buffer, 0, PATH_MAX);
        snprintf(file_path_buffer, PATH_MAX, "%s/EVRoots.plist", asset_path);

        xmlData = SecOTACopyFileContents(file_path_buffer);
        
		if (NULL == xmlData)
		{
			xmlData = SecFrameworkCopyResourceContents(CFSTR("EVRoots"), CFSTR("plist"), NULL);
		}
	}
	
	CFPropertyListRef evroots = NULL;
    if (xmlData) 
	{
        evroots = CFPropertyListCreateWithData(
            kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, NULL, NULL);
        CFRelease(xmlData);
    }

	if (evroots) 
	{
		if (CFGetTypeID(evroots) == CFDictionaryGetTypeID()) 
		{
            /* @@@ Ensure that each dictionary key is a dotted list of digits,
               each value is an NSArrayRef and each element in the array is a
               20 byte digest. */

			result = (CFDictionaryRef)evroots;
		} 
		else 
		{
			secwarning("EVRoot.plist is wrong type.");
			CFRelease(evroots);
		}
    }

	return result;
}

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;;
	
   // first see if there is a file at /var/db/OTA_Anchors
    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;
    
	// ------------------------------------------------------------------------
	// First determine if there are asset files at /var/Keychains.  If there 
	// are files use them for the trust table.  Otherwise, use the files in the
	// Security.framework bundle.
	//
	// The anchor table file is mapped into memory. This SHOULD be OK as the
	// size of the data is around 250K.
	// ------------------------------------------------------------------------
	dir_path = path_ptr;
	
	if (NULL != dir_path)
	{
		// There is a set of OTA asset files
		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;
	}
	
	// Check to see if kAnchorTable was indeed set
	if (NULL == local_anchorTable)
    {  
		// local_anchorTable is still NULL so the asset in the Security framework needs to be used.
        CFReleaseSafe(cert_index_file_data);
        cert_index_file_data = SecFrameworkCopyResourceContents(CFSTR("certsIndex"), CFSTR("data"), NULL);
        table_data_url =  SecFrameworkCopyResourceURL(CFSTR("certsTable"), CFSTR("data"), NULL);
        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)
	{
		// we are in trouble
		CFReleaseSafe(cert_index_file_data);
		return result;
	}

	// ------------------------------------------------------------------------
	// Now that the locations of the files are known and the table file has
	// been mapped into memory, create a dictionary that maps the SHA1 hash of
	// normalized issuer to the offset in the mapped anchor table file which
	// contains a index_record to the correct certificate
	// ------------------------------------------------------------------------
	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);

        // see if the dictionary already has this key
		release_offset = false;
        offsets = (CFMutableArrayRef)CFDictionaryGetValue(anchorLookupTable, index_hash);
        if (NULL == offsets)
        {
			offsets = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
			release_offset = true;
        }

        // Add the offset
        CFArrayAppendValue(offsets, index_offset_value);

        // set the key value pair in the dictionary
        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);
            //munmap(kAnchorTable, local_anchorTableSize);
            local_anchorTable = NULL;
            local_anchorTableSize = 0;
        }
    }
	
	return result;	
}

static CFArrayRef InitializeEscrowCertificates(const char* path_ptr)
{	
	CFArrayRef result = NULL;
	CFDataRef file_data = NULL;
	
	const char* dir_path = path_ptr;
	if (NULL == dir_path)
	{
		file_data = SecFrameworkCopyResourceContents(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)
			{
				result = CFArrayCreateCopy(kCFAllocatorDefault, certs);
			}
			CFRelease(certsDictionary);
		}
		CFRelease(file_data);
	}
	
	return result;
}


static SecOTAPKIRef SecOTACreate()
{
	TestOTALog("In SecOTACreate\n");
	
	SecOTAPKIRef otapkiref = NULL;

    otapkiref = CFTypeAllocate(SecOTAPKI, struct _OpaqueSecOTAPKI , kCFAllocatorDefault);

	if (NULL == otapkiref)
	{
		return otapkiref;
	}
	
	// Mkae suer that if this routine has to bail that the clean up 
	// will do the right thing
	otapkiref->_blackListSet = NULL;
	otapkiref->_grayListSet = NULL;
	otapkiref->_escrowCertificates = NULL;
	otapkiref->_evPolicyToAnchorMapping = NULL;
	otapkiref->_anchorLookupTable = NULL;
	otapkiref->_anchorTable = NULL;
	otapkiref->_assetVersion = 0;
	
	// Start off by getting the correct asset directory info
	int asset_version = 0;
	const char* path_ptr = InitOTADirectory(&asset_version);
	otapkiref->_assetVersion = asset_version;
	
	TestOTALog("SecOTACreate: asset_path = %s\n", path_ptr);	
	TestOTALog("SecOTACreate: asset_version = %d\n", asset_version);
	
	// Get the set of black listed keys
	CFSetRef blackKeysSet = InitializeBlackList(path_ptr);
	if (NULL == blackKeysSet)
	{
		CFReleaseNull(otapkiref);
		return otapkiref;
	}
	otapkiref->_blackListSet = blackKeysSet;
	
	// Get the set of gray listed keys
	CFSetRef grayKeysSet = InitializeGrayList(path_ptr);
	if (NULL == grayKeysSet)
	{
		CFReleaseNull(otapkiref);
		return otapkiref;
	}
	otapkiref->_grayListSet = grayKeysSet;
	
	CFArrayRef escrowCerts = InitializeEscrowCertificates(path_ptr);
	if (NULL == escrowCerts)
	{
		CFReleaseNull(otapkiref);
		return otapkiref;
	}
	otapkiref->_escrowCertificates = escrowCerts;
	
	// Geht the mapping of EV Policy OIDs to Anchor digest
	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 (NULL != 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;
}

CFArrayRef SecOTAPKICopyEscrowCertificates(SecOTAPKIRef otapkiRef)
{
	CFArrayRef result = NULL;
	if (NULL == otapkiRef)
	{
		return result;
	}
	
	result = otapkiRef->_escrowCertificates;
	CFRetainSafe(result);
	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;
}

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(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(otapkiref);
	CFRelease(otapkiref);
	
	if (NULL == result)
	{
		SecError(errSecInternal, error, CFSTR("Could not get the array of escrow certificates form 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;	
}