1// 2// ValidateAsset.c 3// CertificateTool 4// 5// Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. 6// 7 8#include <CoreFoundation/CoreFoundation.h> 9#include <Security/Security.h> 10 11#include <CommonCrypto/CommonDigest.h> 12#include <CommonCrypto/CommonDigestSPI.h> 13#include <CommonCrypto/CommonRSACryptor.h> 14#include <CommonNumerics/CommonBaseXX.h> 15#include <stdio.h> 16#include <sys/types.h> 17#include <dirent.h> 18#include <string.h> 19#include <unistd.h> 20 21 22static int ValidateFilesInDirectory(const char* dir_path, int num_files, const char * files[]) 23{ 24 25 int result = 0; // Assume all is well 26 DIR* dirp = NULL; 27 struct dirent* dp = NULL; 28 29 dirp = opendir(dir_path); 30 if (NULL == dirp) 31 { 32 return -1; 33 } 34 35 for (int iCnt = 0; iCnt < num_files; iCnt++) 36 { 37 int name_length = (int)strlen(files[iCnt]); 38 int found = 0; 39 while (NULL != (dp = readdir(dirp))) 40 { 41 if (dp->d_namlen == name_length && 0 == strcmp(dp->d_name, files[iCnt])) 42 { 43 found = 1; 44 break; 45 } 46 } 47 if (0 == found) 48 { 49 (void)closedir(dirp); 50 51 return -1; 52 } 53 rewinddir(dirp); 54 } 55 (void)closedir(dirp); 56 return result; 57} 58 59static int ReadFileIntoCFDataRef(const char* file_path, CFDataRef* out_data) 60{ 61 int result = -1; // guilt until proven 62 FILE* infile = NULL; 63 void* buffer = NULL; 64 int numbytes = 0; 65 66 if (NULL == file_path || NULL == out_data) 67 { 68 return result; 69 } 70 71 infile = fopen(file_path, "r"); 72 if (NULL == infile) 73 { 74 return result; 75 } 76 77 fseek(infile, 0L, SEEK_END); 78 numbytes = (int)ftell(infile); 79 80 fseek(infile, 0L, SEEK_SET); 81 buffer = calloc(numbytes, sizeof(char)); 82 if (NULL == buffer) 83 { 84 fclose(infile); 85 return result; 86 } 87 88 fread(buffer, sizeof(char), numbytes, infile); 89 fclose(infile); 90 91 *out_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, numbytes); 92 free(buffer); 93 result = (NULL != *out_data) ? 0 : -1; 94 return result; 95} 96 97static int CreateHashForData(CFDataRef cfData, int useSHA1, CFDataRef* out_hash) 98{ 99 int result = -1; // Guilty until proven 100 CCDigestAlgorithm algo = (useSHA1) ? kCCDigestSHA1 :kCCDigestSHA256; 101 size_t digest_length = (useSHA1) ? CC_SHA1_DIGEST_LENGTH : CC_SHA256_DIGEST_LENGTH; 102 UInt8 buffer[digest_length]; 103 104 if (NULL == cfData || NULL == out_hash) 105 { 106 return result; 107 } 108 109 *out_hash = NULL; 110 111 memset(buffer, 0, digest_length); 112 113 if (!CCDigest(algo, CFDataGetBytePtr(cfData), CFDataGetLength(cfData), buffer)) 114 { 115 *out_hash = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, digest_length); 116 } 117 else 118 { 119 return result; 120 } 121 122 result = (NULL == *out_hash) ? -1 : 0; 123 return result; 124} 125 126static int Base64Data(CFDataRef cfData, int for_encoding, CFDataRef* encoded_data) 127{ 128 int result = -1; // Guilty until proven 129 CNEncodings encoding = kCNEncodingBase64; 130 CNStatus status = kCCSuccess; 131 CNEncodingDirection direction = (for_encoding) ? kCNEncode : kCNDecode; 132 unsigned char buffer[1024]; 133 size_t encoded_data_length = 1024; 134 135 if (NULL == cfData || NULL == encoded_data) 136 { 137 return result; 138 } 139 memset(buffer, 0, 1024); 140 *encoded_data = NULL; 141 142 status = CNEncode(encoding, direction, CFDataGetBytePtr(cfData), CFDataGetLength(cfData), buffer, &encoded_data_length); 143 if (kCCSuccess == status) 144 { 145 *encoded_data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buffer, encoded_data_length); 146 result = (NULL == *encoded_data) ? -1 : 0; 147 } 148 return result; 149} 150 151static int CreatePropertyListFromData(CFDataRef prop_data, CFTypeID output_type, CFTypeRef* plistRef) 152{ 153 int result = -1; // Guilt until proven 154 CFPropertyListRef aPlistRef = NULL; 155 CFPropertyListFormat list_format = kCFPropertyListXMLFormat_v1_0; 156 CFErrorRef error = NULL; 157 158 if (NULL == prop_data || NULL == plistRef) 159 { 160 return result; 161 } 162 163 *plistRef = NULL; 164 165 aPlistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, prop_data, 0, &list_format, &error); 166 if (NULL != error || NULL == aPlistRef) 167 { 168 if (NULL != aPlistRef) 169 { 170 CFRelease(aPlistRef); 171 } 172 173 if (NULL != error) 174 { 175 CFRelease(error); 176 } 177 return result; 178 } 179 180 if (CFGetTypeID(aPlistRef) != output_type) 181 { 182 CFRelease(aPlistRef); 183 return result; 184 } 185 186 *plistRef = aPlistRef; 187 result = (NULL == *plistRef) ? -1 : 0; 188 return result; 189} 190 191 192 193int ValidateAsset(const char* asset_dir_path, unsigned long current_version) 194{ 195 const char* files[] = 196 { 197 "AssertVersion.plist", 198 "Blocked.plist", 199 "EVRoots.plist", 200 "certsIndex.data", 201 "certsTable.data", 202 "manifest.data" 203 }; 204 int num_files = (sizeof(files) / sizeof(const char*)); 205 int iCnt = 0; 206 const char* file_name = NULL; 207 char wd_buf[1024]; 208 209 const char* current_working_directory_path = getcwd(wd_buf, 1024); 210 CFDataRef file_data = NULL; 211 CFDataRef hash_data = NULL; 212 CFDataRef manifest_hash_data = NULL; 213 CFStringRef key_name = NULL; 214 CFDictionaryRef manifest_dict = NULL; 215 CFNumberRef manifest_version = NULL; 216 unsigned long manifest_verson_number; 217 int iResult = -1; 218 OSStatus err = errSecSuccess; 219 CMSDecoderRef cmsDecoder = NULL; 220 size_t numSigners = 0; 221 SecPolicyRef x509Policy = NULL; 222 CMSSignerStatus signedStatus = kCMSSignerUnsigned; 223 OSStatus resultCode = errSecSuccess; 224 CFDataRef cmsMsg = NULL; 225 226 // parameter check 227 if (NULL == asset_dir_path) 228 { 229 return iResult; 230 } 231 232 if (ValidateFilesInDirectory(asset_dir_path, num_files, files)) 233 { 234 return iResult; 235 } 236 237 if (chdir(asset_dir_path)) 238 { 239 return iResult; 240 } 241 242 if (ReadFileIntoCFDataRef("manifest.data", &file_data)) 243 { 244 (void)chdir(current_working_directory_path); 245 return iResult; 246 } 247 248 if (NULL == file_data) 249 { 250 return iResult; 251 } 252 253 err = CMSDecoderCreate(&cmsDecoder); 254 if (errSecSuccess != err) 255 { 256 CFRelease(file_data); 257 (void)chdir(current_working_directory_path); 258 return iResult; 259 } 260 261 err = CMSDecoderUpdateMessage(cmsDecoder, CFDataGetBytePtr(file_data), CFDataGetLength(file_data)); 262 CFRelease(file_data); 263 if (errSecSuccess != err) 264 { 265 CFRelease(cmsDecoder); 266 (void)chdir(current_working_directory_path); 267 return iResult; 268 } 269 270 err = CMSDecoderFinalizeMessage(cmsDecoder); 271 if (errSecSuccess != err) 272 { 273 CFRelease(cmsDecoder); 274 (void)chdir(current_working_directory_path); 275 return iResult; 276 } 277 278 err = CMSDecoderGetNumSigners(cmsDecoder, &numSigners); 279 if (errSecSuccess != err || 0 == numSigners) 280 { 281 CFRelease(cmsDecoder); 282 (void)chdir(current_working_directory_path); 283 return iResult; 284 } 285 286 x509Policy = SecPolicyCreateBasicX509(); 287 if (NULL == x509Policy) 288 { 289 CFRelease(cmsDecoder); 290 (void)chdir(current_working_directory_path); 291 return iResult; 292 } 293 294 err = CMSDecoderCopySignerStatus(cmsDecoder, 0, x509Policy, true, &signedStatus, NULL, &resultCode); 295 CFRelease(x509Policy); 296 if (errSecSuccess != err || kCMSSignerValid != signedStatus || errSecSuccess != resultCode) 297 { 298 CFRelease(cmsDecoder); 299 (void)chdir(current_working_directory_path); 300 return iResult; 301 } 302 303 err = CMSDecoderCopyContent(cmsDecoder, &cmsMsg); 304 CFRelease(cmsDecoder); 305 if (errSecSuccess != err) 306 { 307 (void)chdir(current_working_directory_path); 308 return iResult; 309 } 310 311 if (CreatePropertyListFromData(cmsMsg, CFDictionaryGetTypeID(), (CFTypeRef *)&manifest_dict)) 312 { 313 if (NULL != manifest_dict) 314 { 315 CFRelease(manifest_dict); 316 } 317 CFRelease(cmsMsg); 318 (void)chdir(current_working_directory_path); 319 return iResult; 320 } 321 CFRelease(cmsMsg); 322 323 // Validate the hash for the files in the manifest 324 for (iCnt = 0; iCnt < num_files; iCnt++) 325 { 326 file_name = files[iCnt]; 327 // bypass the manifest file for now 328 if (!strcmp("manifest.data", file_name)) 329 { 330 continue; 331 } 332 333 if (ReadFileIntoCFDataRef(file_name, &file_data)) 334 { 335 CFRelease(manifest_dict); 336 (void)chdir(current_working_directory_path); 337 return iResult; 338 } 339 340 if (CreateHashForData(file_data, 0, &hash_data)) 341 { 342 if (NULL != file_data) 343 { 344 CFRelease(file_data); 345 } 346 CFRelease(manifest_dict); 347 (void)chdir(current_working_directory_path); 348 return iResult; 349 } 350 CFRelease(file_data); 351 352 key_name = CFStringCreateWithCString(kCFAllocatorDefault, file_name, kCFStringEncodingUTF8); 353 if (NULL == key_name) 354 { 355 CFRelease(manifest_dict); 356 (void)chdir(current_working_directory_path); 357 return iResult; 358 } 359 360 manifest_hash_data = (CFDataRef)CFDictionaryGetValue(manifest_dict, key_name); 361 if (NULL == manifest_hash_data) 362 { 363 CFRelease(key_name); 364 CFRelease(hash_data); 365 CFRelease(manifest_dict); 366 (void)chdir(current_working_directory_path); 367 return iResult; 368 } 369 CFRelease(key_name); 370 371 if (!CFEqual(hash_data, manifest_hash_data)) 372 { 373 CFRelease(hash_data); 374 CFRelease(manifest_dict); 375 (void)chdir(current_working_directory_path); 376 return iResult; 377 } 378 CFRelease(hash_data); 379 } 380 381 382 // Get the version 383 manifest_version = (CFNumberRef)CFDictionaryGetValue(manifest_dict, CFSTR("VersionNumber")); 384 if (NULL == manifest_version) 385 { 386 CFRelease(manifest_dict); 387 (void)chdir(current_working_directory_path); 388 return iResult; 389 } 390 391 if (!CFNumberGetValue(manifest_version, kCFNumberLongType, &manifest_verson_number)) 392 { 393 CFRelease(manifest_version); 394 CFRelease(manifest_dict); 395 (void)chdir(current_working_directory_path); 396 return iResult; 397 } 398 399 CFRelease(manifest_version); 400 if (manifest_verson_number < current_version) 401 { 402 CFRelease(manifest_dict); 403 (void)chdir(current_working_directory_path); 404 return iResult; 405 } 406 407 iResult = 0; 408 return iResult; 409} 410