]>
Commit | Line | Data |
---|---|---|
427c49bc | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2008-2010,2013-2014 Apple Inc. All Rights Reserved. |
427c49bc A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d8f41ccd | 5 | * |
427c49bc A |
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. | |
d8f41ccd | 12 | * |
427c49bc A |
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. | |
d8f41ccd | 20 | * |
427c49bc A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | #include <TargetConditionals.h> | |
25 | #if TARGET_OS_EMBEDDED | |
26 | ||
27 | #include <stdio.h> | |
28 | #include <unistd.h> | |
29 | #include <stdlib.h> | |
30 | #include <fcntl.h> | |
31 | #include <sys/stat.h> | |
32 | #include <sys/types.h> | |
33 | #include <stdint.h> | |
34 | #include <stdbool.h> | |
35 | #include <errno.h> | |
36 | #include <string.h> | |
37 | ||
38 | #include <CoreFoundation/CFData.h> | |
39 | #include <CoreFoundation/CFDictionary.h> | |
40 | #include <CoreFoundation/CFNumber.h> | |
41 | #include <CoreFoundation/CFString.h> | |
42 | #include <Security/SecImportExport.h> | |
43 | #include <Security/SecItem.h> | |
44 | #include <Security/SecCertificate.h> | |
45 | #include <Security/SecIdentity.h> | |
46 | #include <Security/SecTrust.h> | |
47 | #include <Security/SecInternal.h> | |
48 | #include <utilities/array_size.h> | |
49 | ||
50 | #include "SecurityCommands.h" | |
51 | #include "SecurityTool/print_cert.h" | |
52 | ||
53 | static void * | |
54 | read_file(const char * filename, size_t * data_length) | |
55 | { | |
56 | void * data = NULL; | |
57 | int len = 0; | |
58 | int fd = -1; | |
59 | struct stat sb; | |
60 | ||
61 | *data_length = 0; | |
62 | if (stat(filename, &sb) < 0) | |
63 | goto done; | |
64 | if (sb.st_size > INT32_MAX) | |
65 | goto done; | |
66 | len = (uint32_t)sb.st_size; | |
67 | if (len == 0) | |
68 | goto done; | |
69 | ||
70 | data = malloc(len); | |
71 | if (data == NULL) | |
72 | goto done; | |
73 | ||
74 | fd = open(filename, O_RDONLY); | |
75 | if (fd < 0) | |
76 | goto done; | |
77 | ||
78 | if (read(fd, data, len) != len) { | |
79 | goto done; | |
80 | } | |
81 | done: | |
82 | if (fd >= 0) | |
83 | close(fd); | |
84 | if (data) { | |
85 | *data_length = len; | |
86 | } | |
87 | return (data); | |
88 | } | |
89 | ||
90 | static OSStatus | |
91 | add_cert_item(SecCertificateRef cert) | |
92 | { | |
93 | CFDictionaryRef dict; | |
94 | OSStatus status; | |
95 | ||
96 | dict = CFDictionaryCreate(NULL, | |
97 | (const void * *)&kSecValueRef, | |
98 | (const void * *)&cert, 1, | |
99 | &kCFTypeDictionaryKeyCallBacks, | |
100 | &kCFTypeDictionaryValueCallBacks); | |
101 | status = SecItemAdd(dict, NULL); | |
102 | CFReleaseNull(dict); | |
103 | return (status); | |
104 | } | |
105 | ||
106 | static OSStatus | |
107 | remove_cert_item(SecCertificateRef cert) | |
108 | { | |
109 | CFDictionaryRef dict; | |
110 | OSStatus status; | |
111 | ||
112 | dict = CFDictionaryCreate(NULL, | |
113 | (const void * *)&kSecValueRef, | |
114 | (const void * *)&cert, 1, | |
115 | &kCFTypeDictionaryKeyCallBacks, | |
116 | &kCFTypeDictionaryValueCallBacks); | |
117 | status = SecItemDelete(dict); | |
118 | CFReleaseNull(dict); | |
119 | if (status == errSecItemNotFound) | |
120 | status = errSecSuccess; /* already gone, no problem */ | |
121 | return (status); | |
122 | } | |
123 | ||
124 | static CFArrayRef | |
125 | PKCS12FileCreateArray(const char * filename, const char * password) | |
126 | { | |
127 | void * file_data = NULL; | |
128 | size_t file_data_length; | |
129 | CFArrayRef items = NULL; | |
130 | CFDictionaryRef options = NULL; | |
131 | CFDataRef pkcs12_data = NULL; | |
132 | CFStringRef password_cf = NULL; | |
133 | ||
134 | file_data = read_file(filename, &file_data_length); | |
135 | if (file_data == NULL) { | |
136 | int this_error = errno; | |
137 | ||
138 | fprintf(stderr, "failed to read file '%s', %s\n", | |
139 | filename, strerror(this_error)); | |
140 | goto done; | |
141 | } | |
142 | pkcs12_data = CFDataCreate(NULL, file_data, file_data_length); | |
143 | password_cf | |
144 | = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8); | |
145 | ||
146 | options = CFDictionaryCreate(NULL, | |
147 | (const void * *)&kSecImportExportPassphrase, | |
148 | (const void * *)&password_cf, 1, | |
149 | &kCFTypeDictionaryKeyCallBacks, | |
150 | &kCFTypeDictionaryValueCallBacks); | |
151 | if (SecPKCS12Import(pkcs12_data, options, &items) != 0) { | |
152 | fprintf(stderr, "failed to import PKCS12 '%s'\n", | |
153 | filename); | |
154 | } | |
155 | done: | |
156 | if (file_data != NULL) { | |
157 | free(file_data); | |
158 | } | |
159 | CFReleaseNull(pkcs12_data); | |
160 | CFReleaseNull(password_cf); | |
161 | CFReleaseNull(options); | |
162 | return (items); | |
163 | } | |
164 | ||
165 | static void | |
166 | find_identity_using_handle(CFTypeRef identity_handle) | |
167 | { | |
168 | CFDictionaryRef dict; | |
169 | CFTypeRef identity_ref; | |
170 | const void * keys[] = { kSecClass, | |
171 | kSecReturnRef, | |
172 | kSecValuePersistentRef }; | |
173 | const void * values[] = { kSecClassIdentity, | |
174 | kCFBooleanTrue, | |
175 | identity_handle }; | |
176 | OSStatus status; | |
177 | ||
178 | /* find the identity using the persistent handle */ | |
179 | dict = CFDictionaryCreate(NULL, keys, values, | |
180 | (array_size(keys)), | |
181 | &kCFTypeDictionaryKeyCallBacks, | |
182 | &kCFTypeDictionaryValueCallBacks); | |
183 | status = SecItemCopyMatching(dict, &identity_ref); | |
184 | CFReleaseNull(dict); | |
185 | if (status != errSecSuccess) { | |
186 | fprintf(stderr, "SecItemCopyMatching() failed %d\n", | |
187 | (int)status); | |
188 | } | |
189 | else { | |
190 | printf("Found identity:\n"); | |
191 | fflush(stdout); | |
192 | fflush(stderr); | |
193 | CFShow(identity_ref); | |
194 | CFReleaseNull(identity_ref); | |
195 | } | |
196 | return; | |
197 | } | |
198 | ||
199 | static bool | |
200 | PKCS12ArrayAddSecItems(CFArrayRef items, bool verbose) | |
201 | { | |
202 | CFIndex count; | |
203 | CFIndex i; | |
204 | bool success = TRUE; | |
205 | ||
206 | count = CFArrayGetCount(items); | |
207 | for (i = 0; i < count; i++) { | |
208 | SecTrustRef trust_ref; | |
209 | SecIdentityRef identity; | |
210 | CFDictionaryRef item_dict = CFArrayGetValueAtIndex(items, 0); | |
211 | OSStatus status; | |
212 | ||
213 | /* add identity */ | |
214 | identity = (SecIdentityRef)CFDictionaryGetValue(item_dict, kSecImportItemIdentity); | |
215 | if (identity != NULL) { | |
216 | if (verbose) { | |
217 | SecCertificateRef cert = NULL; | |
218 | SecIdentityCopyCertificate(identity, &cert); | |
219 | print_cert(cert, false); | |
220 | CFReleaseSafe(cert); | |
221 | } | |
222 | CFDictionaryRef dict; | |
223 | CFTypeRef identity_handle = NULL; | |
224 | const void * keys[] = { kSecReturnPersistentRef, | |
225 | kSecValueRef }; | |
226 | const void * values[] = { kCFBooleanTrue, | |
227 | identity }; | |
228 | dict = CFDictionaryCreate(NULL, | |
229 | keys, values, | |
230 | array_size(keys), | |
231 | &kCFTypeDictionaryKeyCallBacks, | |
232 | &kCFTypeDictionaryValueCallBacks); | |
233 | status = SecItemAdd(dict, &identity_handle); | |
234 | if (identity_handle != NULL) { | |
235 | find_identity_using_handle(identity_handle); | |
236 | } | |
237 | CFReleaseNull(identity_handle); | |
238 | if (status != errSecSuccess) { | |
239 | fprintf(stderr, "SecItemAdd(identity) failed %d\n", | |
240 | (int)status); | |
241 | success = FALSE; | |
242 | } | |
243 | CFReleaseNull(dict); | |
244 | } | |
245 | ||
246 | /* add certs */ | |
247 | trust_ref = (SecTrustRef)CFDictionaryGetValue(item_dict, kSecImportItemTrust); | |
248 | if (trust_ref != NULL) { | |
249 | CFIndex cert_count; | |
250 | CFIndex cert_index; | |
251 | ||
252 | cert_count = SecTrustGetCertificateCount(trust_ref); | |
253 | for (cert_index = 1; cert_index < cert_count; cert_index++) { | |
254 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust_ref, cert_index); | |
255 | if (verbose) | |
256 | print_cert(cert, false); | |
257 | status = add_cert_item(cert); | |
258 | if (status != errSecSuccess) { | |
259 | fprintf(stderr, "add_cert_item %d failed %d\n", (int)cert_index, (int)status); | |
260 | success = FALSE; | |
261 | } | |
262 | } | |
263 | } | |
264 | } | |
265 | return (success); | |
266 | } | |
267 | ||
268 | static bool | |
269 | PKCS12ArrayRemoveSecItems(CFArrayRef items, bool verbose) | |
270 | { | |
271 | CFIndex count; | |
272 | CFIndex i; | |
273 | bool success = TRUE; | |
274 | ||
275 | count = CFArrayGetCount(items); | |
276 | for (i = 0; i < count; i++) { | |
277 | CFTypeRef cert_chain; | |
278 | SecIdentityRef identity; | |
279 | CFDictionaryRef item_dict = CFArrayGetValueAtIndex(items, i); | |
280 | OSStatus status; | |
281 | ||
282 | /* remove identity */ | |
283 | identity = (SecIdentityRef)CFDictionaryGetValue(item_dict, | |
284 | kSecImportItemIdentity); | |
285 | if (identity != NULL) { | |
286 | if (verbose) { | |
287 | SecCertificateRef cert = NULL; | |
288 | SecIdentityCopyCertificate(identity, &cert); | |
289 | print_cert(cert, false); | |
290 | CFReleaseSafe(cert); | |
291 | } | |
292 | CFDictionaryRef dict; | |
293 | ||
294 | dict = CFDictionaryCreate(NULL, | |
295 | (const void * *)&kSecValueRef, | |
296 | (const void * *)&identity, 1, | |
297 | &kCFTypeDictionaryKeyCallBacks, | |
298 | &kCFTypeDictionaryValueCallBacks); | |
299 | status = SecItemDelete(dict); | |
300 | if (status != errSecSuccess) { | |
301 | fprintf(stderr, "SecItemDelete(identity) failed %d\n", | |
302 | (int)status); | |
303 | success = FALSE; | |
304 | } | |
305 | CFReleaseNull(dict); | |
306 | } | |
307 | /* remove cert chain */ | |
308 | cert_chain = CFDictionaryGetValue(item_dict, kSecImportItemCertChain); | |
309 | if (cert_chain != NULL) { | |
310 | CFIndex cert_count; | |
311 | CFIndex cert_index; | |
312 | ||
313 | cert_count = CFArrayGetCount(cert_chain); | |
314 | for (cert_index = 0; cert_index < cert_count; cert_index++) { | |
315 | SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_chain, cert_index); | |
316 | if (verbose) | |
317 | print_cert(cert, false); | |
318 | status = remove_cert_item(cert); | |
319 | if (status != errSecSuccess) { | |
320 | fprintf(stderr, "remove_cert_item %d failed %d\n", (int)cert_index, (int)status); | |
321 | success = FALSE; | |
322 | } | |
323 | } | |
324 | } | |
325 | } | |
326 | return (success); | |
327 | } | |
328 | ||
329 | ||
330 | extern int pkcs12_util(int argc, char * const *argv) | |
331 | { | |
332 | CFArrayRef array; | |
333 | const char * filename = NULL; | |
334 | const char * passphrase = NULL; | |
335 | bool delete = false; | |
336 | bool verbose = false; | |
337 | char ch; | |
338 | ||
339 | while ((ch = getopt(argc, argv, "p:dv")) != -1) | |
340 | { | |
341 | switch (ch) | |
342 | { | |
343 | case 'p': | |
344 | passphrase = optarg; | |
345 | break; | |
346 | case 'd': | |
347 | delete = true; | |
348 | break; | |
349 | case 'v': | |
350 | verbose = true; | |
351 | break; | |
352 | default: | |
353 | return 2; /* Trigger usage message. */ | |
354 | } | |
355 | } | |
356 | ||
357 | argc -= optind; | |
358 | argv += optind; | |
359 | ||
360 | if (argc != 1 || !passphrase) | |
361 | return 2; /* Trigger usage message. */ | |
362 | ||
363 | filename = argv[0]; | |
364 | array = PKCS12FileCreateArray(filename, passphrase); | |
365 | if (array == NULL) | |
366 | return -1; | |
367 | ||
368 | bool success = false; | |
369 | if (delete) | |
370 | success = PKCS12ArrayRemoveSecItems(array, verbose); | |
371 | else | |
372 | success = PKCS12ArrayAddSecItems(array, verbose); | |
373 | ||
374 | CFReleaseNull(array); | |
375 | ||
376 | return success ? 0 : -1; | |
377 | } | |
378 | ||
379 | #endif // TARGET_OS_EMBEDDED |