source: trunk/dports/security/certsync/files/certsync.m @ 105985

Last change on this file since 105985 was 105985, checked in by landonf@…, 4 years ago

Additional fixes for 10.6.

File size: 9.5 KB
Line 
1/*
2 * Author: Landon Fuller <landonf@plausiblelabs.com>
3 * Copyright (c) 2008-2013 Plausible Labs Cooperative, Inc.
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#import <Foundation/Foundation.h>
29#import <AvailabilityMacros.h>
30
31#import <unistd.h>
32#import <stdio.h>
33
34#import <objc/message.h>
35
36/**
37 * Add CoreFoundation object to the current autorelease pool.
38 *
39 * @param cfObj Object to add to the current autorelease pool.
40 */
41CFTypeRef PLCFAutorelease (CFTypeRef cfObj) {
42    /* ARC forbids the use of @selector(autorelease), so we have to get creative */
43    static SEL autorelease;
44    static dispatch_once_t pred;
45    dispatch_once(&pred, ^{
46        autorelease = sel_getUid("autorelease");
47    });
48   
49    /* Cast and hand-dispatch */
50    return ((CFTypeRef (*)(CFTypeRef, SEL)) objc_msgSend)(cfObj, autorelease);
51}
52
53int nsvfprintf (FILE *stream, NSString *format, va_list args) {
54    int retval;
55   
56    NSString *str;
57    str = (NSString *) CFStringCreateWithFormatAndArguments(NULL, NULL, (CFStringRef) format, args);
58    retval = fprintf(stream, "%s", [str UTF8String]);
59   
60    return retval;
61}
62
63int nsfprintf (FILE *stream, NSString *format, ...) {
64    va_list ap;
65    int retval;
66   
67    va_start(ap, format);
68    {
69        retval = nsvfprintf(stream, format, ap);
70    }
71    va_end(ap);
72   
73    return retval;
74}
75
76int nsprintf (NSString *format, ...) {
77    va_list ap;
78    int retval;
79   
80    va_start(ap, format);
81    {
82        retval = nsvfprintf(stderr, format, ap);
83    }
84    va_end(ap);
85   
86    return retval;
87}
88
89/**
90 * Fetch all trusted roots for the given @a domain.
91 *
92 * @param domain The trust domain to query.
93 * @param outError On error, will contain an NSError instance describing the failure.
94 *
95 * @return Returns a (possibly empty) array of certificates on success, nil on failure.
96 */
97static NSArray *certificatesForTrustDomain (SecTrustSettingsDomain domain, NSError **outError) {
98    CFArrayRef certs = nil;
99    OSStatus err;
100   
101    /* Fetch all certificates in the given domain */
102    err = SecTrustSettingsCopyCertificates(domain, &certs);
103    if (err == errSecSuccess) {
104        PLCFAutorelease(certs);
105    } else if (err == errSecNoTrustSettings ) {
106        /* No data */
107        return [NSArray array];
108       
109    } else if (err != errSecSuccess) {
110        /* Lookup failed */
111        if (outError != NULL)
112            *outError = [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil];
113        return nil;
114    }
115   
116    /* Extract trusted roots */
117    NSMutableArray *results = [NSMutableArray arrayWithCapacity: CFArrayGetCount(certs)];
118    for (id certObj in (NSArray *) certs) {
119        SecCertificateRef cert = (SecCertificateRef) certObj;
120       
121        /* Fetch the trust settings */
122        CFArrayRef trustSettings = nil;
123        err = SecTrustSettingsCopyTrustSettings(cert, domain, &trustSettings);
124        if (err != errSecSuccess) {
125            /* Shouldn't happen */
126            nsfprintf(stderr, @"Failed to fetch trust settings\n");
127            continue;
128        } else {
129            PLCFAutorelease(trustSettings);
130        }
131       
132        /* If empty, trust for everything (as per the Security Framework documentation) */
133        if (CFArrayGetCount(trustSettings) == 0) {
134            [results addObject: certObj];
135        } else {
136            /* Otherwise, walk the properties and evaluate the trust settings result */
137            for (NSDictionary *trustProps in (NSArray *) trustSettings) {
138                CFNumberRef settingsResultNum;
139                SInt32 settingsResult;
140               
141                settingsResultNum = (CFNumberRef) [trustProps objectForKey: (id) kSecTrustSettingsResult];
142                CFNumberGetValue(settingsResultNum, kCFNumberSInt32Type, &settingsResult);
143               
144                /* If a root, add to the result set */
145                if (settingsResult == kSecTrustSettingsResultTrustRoot || settingsResult == kSecTrustSettingsResultTrustAsRoot) {
146                    [results addObject: certObj];
147                    break;
148                }
149            }
150        }
151    }
152   
153    return results;
154}
155
156static int exportCertificates (BOOL userAnchors, NSString *outputFile) {
157    /*
158     * Fetch all certificates
159     */
160   
161    NSMutableArray *anchors = [NSMutableArray array];
162    NSArray *result;
163    NSError *error;
164   
165    /* Current user */
166    if (userAnchors) {
167        result = certificatesForTrustDomain(kSecTrustSettingsDomainUser, &error);
168        if (result != nil) {
169            [anchors addObjectsFromArray: result];
170        } else {
171            nsfprintf(stderr, @"Failed to fetch user anchors: %@\n", error);
172            return EXIT_FAILURE;
173        }
174    }
175   
176    /* Admin */
177    result = certificatesForTrustDomain(kSecTrustSettingsDomainAdmin, &error);
178    if (result != nil) {
179        [anchors addObjectsFromArray: result];
180    } else {
181        nsfprintf(stderr, @"Failed to fetch admin anchors: %@\n", error);
182        return EXIT_FAILURE;
183    }
184   
185    /* System */
186    result = certificatesForTrustDomain(kSecTrustSettingsDomainSystem, &error);
187    if (result != nil) {
188        [anchors addObjectsFromArray: result];
189    } else {
190        nsfprintf(stderr, @"Failed to fetch system anchors: %@\n", error);
191        return EXIT_FAILURE;
192    }
193   
194    for (id certObj in result) {
195        CFErrorRef cferror = NULL;
196        CFStringRef subject;
197
198#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6
199        if (SecCertificateCopyShortDescription != NULL) {
200            subject = PLCFAutorelease(SecCertificateCopyShortDescription(NULL, (SecCertificateRef) certObj, &cferror));
201        } else {
202            subject = PLCFAutorelease(SecCertificateCopySubjectSummary((SecCertificateRef) certObj));
203        }
204#else
205        subject = PLCFAutorelease(SecCertificateCopySubjectSummary((SecCertificateRef) certObj));
206#endif
207
208        if (subject == NULL) {
209            nsfprintf(stderr, @"Failed to extract certificate description: %@\n", cferror);
210            return EXIT_FAILURE;
211        } else {
212            nsfprintf(stderr, @"Extracting %@\n", subject);
213        }
214    }
215   
216    /*
217     * Perform export
218     */
219    CFDataRef pemData;
220    OSStatus err;
221   
222    /* Prefer the non-deprecated SecItemExport on Mac OS X >= 10.7. We use an ifdef to keep the code buildable with earlier SDKs, too. */
223#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6
224    if (SecItemExport != NULL) {
225        err = SecItemExport((CFArrayRef) anchors, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &pemData);
226    } else {
227        err = SecKeychainItemExport((CFArrayRef) anchors, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &pemData);
228    }
229#else
230    err = SecKeychainItemExport((CFArrayRef) anchors, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &pemData);
231#endif
232   
233    if (err != errSecSuccess) {
234        nsfprintf(stderr, @"Failed to export certificates: %@\n", [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil]);
235        return EXIT_FAILURE;
236    }
237
238    if (outputFile == nil) {
239        NSString *str = [[[NSString alloc] initWithData: (NSData *) pemData encoding:NSUTF8StringEncoding] autorelease];
240        nsfprintf(stdout, @"%@", str);
241    } else {
242        if (![(NSData *) pemData writeToFile: outputFile options: NSDataWritingAtomic error: &error]) {
243            nsfprintf(stderr, @"Failed to write to pem output file: %@\n", error);
244            return EXIT_FAILURE;
245        }
246    }
247   
248    return EXIT_SUCCESS;
249}
250
251static void usage (const char *progname) {
252    fprintf(stderr, "Usage: %s [-u] [-o <output file>]\n", progname);
253    fprintf(stderr, "\t-u\t\t\tInclude the current user's anchor certificates.\n");
254    fprintf(stderr, "\t-o <output file>\tWrite the PEM certificates to the target file, rather than stdout\n");
255}
256
257int main (int argc, char * const argv[]) {
258    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
259
260    /* Parse the command line arguments */
261    BOOL userAnchors = NO;
262    NSString *outputFile = nil;
263   
264    int ch;
265    while ((ch = getopt(argc, argv, "huo:")) != -1) {
266        switch (ch) {
267            case 'u':
268                userAnchors = YES;
269                break;
270               
271            case 'o':
272                outputFile = [NSString stringWithUTF8String: optarg];
273                break;
274
275            case 'h':
276                usage(argv[0]);
277                exit(EXIT_SUCCESS);
278
279            default:
280                usage(argv[0]);
281                exit(EXIT_FAILURE);
282        }
283    }
284    argc -= optind;
285    argv += optind;
286   
287    /* Perform export  */
288    int ret = exportCertificates(userAnchors, outputFile);
289
290    [pool release];
291   
292    return ret;
293}
294
Note: See TracBrowser for help on using the repository browser.