]>
Commit | Line | Data |
---|---|---|
d64be36e A |
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 | } |