ca_revocation_additions.m
1 /* 2 * Copyright (c) 2020 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * ca_revocation_additions.m 24 */ 25 26 #import <Foundation/Foundation.h> 27 #include <Security/SecTrustSettingsPriv.h> 28 #include <Security/SecCertificatePriv.h> 29 #include <utilities/fileIo.h> 30 #include <utilities/SecCFWrappers.h> 31 32 #include "SecurityCommands.h" 33 34 NSString* secToolAppID = @"com.apple.security"; 35 36 static int addCertFile(const char *fileName, NSMutableArray *array) { 37 SecCertificateRef certRef = NULL; 38 NSData *data = NULL; 39 unsigned char *buf = NULL; 40 size_t numBytes; 41 int rtn = 0; 42 43 if (readFileSizet(fileName, &buf, &numBytes)) { 44 rtn = -1; 45 goto errOut; 46 } 47 48 data = [NSData dataWithBytes:buf length:numBytes]; 49 certRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data); 50 if (!certRef) { 51 certRef = SecCertificateCreateWithPEM(NULL, (__bridge CFDataRef)data); 52 if (!certRef) { 53 rtn = -1; 54 goto errOut; 55 } 56 } 57 58 [array addObject:(__bridge id)certRef]; 59 60 errOut: 61 /* Cleanup */ 62 free(buf); 63 CFReleaseNull(certRef); 64 return rtn; 65 } 66 67 static int returnCFError(CFErrorRef CF_CONSUMED error) { 68 CFStringRef errorString = CFErrorCopyDescription(error); 69 CFStringPerformWithCString(errorString, ^(const char *utf8Str) { 70 fprintf(stderr, "Failed to copy CA revocation additions: %s\n", utf8Str); 71 }); 72 CFIndex errCode = CFErrorGetCode(error); 73 CFReleaseNull(error); 74 return (int)errCode; 75 } 76 77 static int resetRevocationAdditions(bool resetCerts) { 78 bool result = false; 79 CFErrorRef error = NULL; 80 if (resetCerts) { 81 NSDictionary *resetCertsDict = @{ (__bridge NSString*)kSecCARevocationAdditionsKey: @[] }; 82 result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)resetCertsDict, &error); 83 } 84 if (!result) { 85 return returnCFError(error); 86 } 87 return 0; 88 } 89 90 static int addRevocationAdditions(CFStringRef key, NSArray *newAdditions) { 91 CFErrorRef error = NULL; 92 NSDictionary *currentAdditions = CFBridgingRelease(SecTrustStoreCopyCARevocationAdditions((__bridge CFStringRef)secToolAppID, &error)); 93 if (!currentAdditions && error) { 94 return returnCFError(error); 95 } 96 97 NSMutableArray *additionsForKey = nil; 98 if (currentAdditions && currentAdditions[(__bridge NSString*)key]) { 99 additionsForKey = [currentAdditions[(__bridge NSString*)key] mutableCopy]; 100 [additionsForKey addObjectsFromArray:newAdditions]; 101 } else { 102 additionsForKey = [newAdditions copy]; 103 } 104 105 NSDictionary *newAdditionsDict = @{ (__bridge NSString*)key: additionsForKey }; 106 bool result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)newAdditionsDict, &error); 107 if (!result) { 108 return returnCFError(error); 109 } 110 111 return 0; 112 } 113 114 int add_ca_revocation_checking(int argc, char * const *argv) { 115 int arg; 116 117 bool resetCerts = false; 118 119 NSMutableArray *certs = [NSMutableArray array]; 120 NSDictionary *plist = nil; 121 122 /* parse args */ 123 if (argc == 1) { 124 return SHOW_USAGE_MESSAGE; 125 } 126 127 while ((arg = getopt(argc, argv, "c:r:p:")) != -1) { 128 switch(arg) { 129 case 'c': 130 if (addCertFile(optarg, certs)) { 131 fprintf(stderr, "Failed to read cert file\n"); 132 return 1; 133 } 134 break; 135 case 'r': 136 if (!strcmp(optarg, "all")) { 137 resetCerts = true; 138 } else if (!strcmp(optarg, "cert")) { 139 resetCerts = true; 140 } else { 141 return SHOW_USAGE_MESSAGE; 142 } 143 break; 144 case 'p': 145 plist = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithCString:optarg encoding:NSUTF8StringEncoding]]; 146 break; 147 case '?': 148 default: 149 return SHOW_USAGE_MESSAGE; 150 } 151 } 152 153 /* handle reset operation */ 154 if (resetCerts) { 155 return resetRevocationAdditions(resetCerts); 156 } 157 158 /* set plist */ 159 if (plist) { 160 CFErrorRef error = NULL; 161 bool result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)plist, &error); 162 if (!result) { 163 return returnCFError(error); 164 } else { 165 return 0; 166 } 167 } 168 169 /* add certs */ 170 int status = 0; 171 if ([certs count]) { 172 NSMutableArray<NSDictionary *>*valuesForCAsKey = [NSMutableArray arrayWithCapacity:[certs count]]; 173 [certs enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 174 SecCertificateRef cert = (__bridge SecCertificateRef)obj; 175 NSData* hash = CFBridgingRelease(SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert)); 176 NSDictionary *value = @{ (__bridge NSString*)kSecCARevocationHashAlgorithmKey:@"sha256", 177 (__bridge NSString*)kSecCARevocationSPKIHashKey:hash }; 178 [valuesForCAsKey addObject:value]; 179 }]; 180 status = addRevocationAdditions(kSecCARevocationAdditionsKey, valuesForCAsKey); 181 } 182 if (status != 0) { 183 fprintf(stderr, "failed to add cert revocation additions\n"); 184 return status; 185 } 186 187 return 0; 188 } 189 190 static int printRevocationAdditions(CFStringRef key, NSDictionary *allAdditions) { 191 if (!allAdditions || !allAdditions[(__bridge NSString*)key] || 192 [allAdditions[(__bridge NSString*)key] count] == 0) { 193 CFStringPerformWithCString(key, ^(const char *utf8Str) { 194 fprintf(stdout, "No revocation additions for %s\n", utf8Str); 195 }); 196 return 0; 197 } 198 199 NSArray *additionsForKey = allAdditions[(__bridge NSString*)key]; 200 NSMutableString *additionsString = [NSMutableString stringWithFormat:@"\t%@ : [",key]; 201 [additionsForKey enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 202 if ([obj isKindOfClass:[NSString class]]) { 203 if (idx == 0) { 204 [additionsString appendFormat:@"\"%@\"",obj]; 205 } else { 206 [additionsString appendFormat:@", \"%@\"", obj]; 207 } 208 } else if ([obj isKindOfClass:[NSDictionary class]]) { 209 if (idx == 0) { 210 [additionsString appendString:@"\n\t "]; 211 } else { 212 [additionsString appendString:@"\t "]; 213 } 214 [additionsString appendFormat:@"\"%@:", obj[(__bridge NSString*)kSecCARevocationHashAlgorithmKey]]; 215 NSString *hashHex = CFBridgingRelease(CFDataCopyHexString((__bridge CFDataRef)obj[(__bridge NSString*)kSecCARevocationSPKIHashKey])); 216 [additionsString appendString:hashHex]; 217 if ([additionsForKey count] == idx + 1) { // last entry 218 [additionsString appendString:@"\"\n"]; 219 } else { 220 [additionsString appendString:@"\",\n"]; 221 } 222 } 223 }]; 224 [additionsString appendString:@"]\n"]; 225 CFStringPerformWithCString((__bridge CFStringRef)additionsString, ^(const char *utf8Str) { 226 fprintf(stdout, "\n%s\n", utf8Str); 227 }); 228 229 return 0; 230 } 231 232 int show_ca_revocation_checking(int argc, char * const *argv) { 233 int arg; 234 bool allAdditions = false; 235 NSString *identifier = nil; 236 bool certAdditions = false; 237 238 /* parse args */ 239 while ((arg = getopt(argc, argv, "ai:c")) != -1) { 240 switch(arg) { 241 case 'a': 242 allAdditions = true; 243 break; 244 case 'i': 245 identifier = [NSString stringWithCString:optarg encoding:NSUTF8StringEncoding]; 246 break; 247 case 'c': 248 certAdditions = true; 249 break; 250 case '?': 251 default: 252 return SHOW_USAGE_MESSAGE; 253 } 254 } 255 256 if (allAdditions) { 257 identifier = nil; 258 fprintf(stdout, "Showing revocation additions for all apps\n"); 259 } else if (!identifier) { 260 identifier = secToolAppID; 261 } 262 263 if (identifier) { 264 CFStringPerformWithCString((__bridge CFStringRef)identifier, ^(const char *utf8Str) { 265 fprintf(stdout, "Showing revocation additions for %s\n", utf8Str); 266 }); 267 } 268 269 CFErrorRef error = NULL; 270 NSDictionary *results = CFBridgingRelease(SecTrustStoreCopyCARevocationAdditions((__bridge CFStringRef)identifier, &error)); 271 272 /* Copy failed, return error */ 273 if (!results && error) { 274 return returnCFError(error); 275 } 276 277 /* print cert revocation additions */ 278 int status = 0; 279 if (certAdditions) { 280 status = printRevocationAdditions(kSecCARevocationAdditionsKey, results); 281 } 282 if (status != 0) { 283 fprintf(stderr, "failed to print revocation additions\n"); 284 return status; 285 } 286 287 288 return 0; 289 }