1/*
2 * Copyright (c) 2008-2010 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
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
53static void *
54read_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
90static OSStatus
91add_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
106static OSStatus
107remove_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
124static CFArrayRef
125PKCS12FileCreateArray(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
165static void
166find_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
199static bool
200PKCS12ArrayAddSecItems(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
268static bool
269PKCS12ArrayRemoveSecItems(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
330extern 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
380