Changeset 106042


Ignore:
Timestamp:
May 14, 2013, 1:20:04 AM (4 years ago)
Author:
landonf@…
Message:

Add support for automatically regenerating the output file when the system keychain or trust settings are modified.

Location:
trunk/dports/security/certsync
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/dports/security/certsync/Portfile

    r106026 r106042  
    33PortSystem 1.0
    44name                    certsync
    5 version                 1.0.1
    6 revision                1
     5version                 1.0.2
    76categories              security
    87conflicts               curl-ca-bundle
     
    1817use_configure no
    1918
    20 # TODO: How should we run this automatically? Attempts to use SecKeychainAddCallback()
    21 # in a blocking daemon haven't been successful; the callback is never called.
    22 # One possible hack is observing events on /System/Library/Keychains
    23 #startupitem.create yes
    24 #startupitem.start "${prefix}/bin/update-ca-certificates"
     19# TODO: Ideally this would be run by default, rather than
     20# requiring 'port load'. It doesn't run any network services, but rather,
     21# simply ensures that the certificate store is always up-to-date.
     22startupitem.create yes
     23startupitem.start "${prefix}/bin/certsync -s -o '${prefix}/etc/openssl/cert.pem'"
    2524
    2625build {
     
    3231                ${filespath}/certsync.m -o ${worksrcpath}/certsync \
    3332                ${configure.ldflags} \
    34                 -framework Foundation -framework Security"
     33                -framework Foundation -framework Security -framework CoreServices"
    3534        file copy "${filespath}/update-ca-certificates" "${worksrcpath}/update-ca-certificates"
    3635        reinplace "s|@PREFIX@|${prefix}|g" "${worksrcpath}/update-ca-certificates"
  • trunk/dports/security/certsync/files/certsync.m

    r105985 r106042  
    3434#import <objc/message.h>
    3535
     36/* A wrapper class that may be used to pass configuration through the
     37 * FSEvent callback API */
     38@interface MPCertSyncConfig : NSObject {
     39@public
     40    BOOL userAnchors;
     41    NSString *outputFile;
     42}
     43@end
     44
     45@implementation MPCertSyncConfig
     46- (void) dealloc {
     47    [outputFile release];
     48    [super dealloc];
     49}
     50@end
     51
    3652/**
    3753 * Add CoreFoundation object to the current autorelease pool.
     
    5773    str = (NSString *) CFStringCreateWithFormatAndArguments(NULL, NULL, (CFStringRef) format, args);
    5874    retval = fprintf(stream, "%s", [str UTF8String]);
     75    [str release];
    5976   
    6077    return retval;
     
    96113 */
    97114static NSArray *certificatesForTrustDomain (SecTrustSettingsDomain domain, NSError **outError) {
     115    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    98116    CFArrayRef certs = nil;
    99117    OSStatus err;
     
    105123    } else if (err == errSecNoTrustSettings ) {
    106124        /* No data */
     125       
     126        [pool release];
    107127        return [NSArray array];
    108        
    109128    } else if (err != errSecSuccess) {
    110129        /* Lookup failed */
    111130        if (outError != NULL)
    112131            *outError = [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil];
     132       
     133        [pool release];
    113134        return nil;
    114135    }
     
    150171        }
    151172    }
    152    
    153     return results;
     173
     174    [results retain];
     175    [pool release];
     176    return [results autorelease];
    154177}
    155178
    156179static int exportCertificates (BOOL userAnchors, NSString *outputFile) {
     180    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     181
    157182    /*
    158183     * Fetch all certificates
     
    170195        } else {
    171196            nsfprintf(stderr, @"Failed to fetch user anchors: %@\n", error);
     197            [pool release];
    172198            return EXIT_FAILURE;
    173199        }
     
    180206    } else {
    181207        nsfprintf(stderr, @"Failed to fetch admin anchors: %@\n", error);
     208        [pool release];
    182209        return EXIT_FAILURE;
    183210    }
     
    189216    } else {
    190217        nsfprintf(stderr, @"Failed to fetch system anchors: %@\n", error);
     218        [pool release];
    191219        return EXIT_FAILURE;
    192220    }
     
    208236        if (subject == NULL) {
    209237            nsfprintf(stderr, @"Failed to extract certificate description: %@\n", cferror);
     238            [pool release];
    210239            return EXIT_FAILURE;
    211240        } else {
     
    230259    err = SecKeychainItemExport((CFArrayRef) anchors, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &pemData);
    231260#endif
    232    
     261    PLCFAutorelease(pemData);
     262
    233263    if (err != errSecSuccess) {
    234264        nsfprintf(stderr, @"Failed to export certificates: %@\n", [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil]);
     265        [pool release];
    235266        return EXIT_FAILURE;
    236267    }
     
    242273        if (![(NSData *) pemData writeToFile: outputFile options: NSDataWritingAtomic error: &error]) {
    243274            nsfprintf(stderr, @"Failed to write to pem output file: %@\n", error);
     275            [pool release];
    244276            return EXIT_FAILURE;
    245277        }
    246278    }
    247279   
     280    [pool release];
    248281    return EXIT_SUCCESS;
    249282}
     
    252285    fprintf(stderr, "Usage: %s [-u] [-o <output file>]\n", progname);
    253286    fprintf(stderr, "\t-u\t\t\tInclude the current user's anchor certificates.\n");
     287    fprintf(stderr, "\t-s\t\t\tDo not exit; observe the system keychain(s) for changes and update the output file accordingly.");
    254288    fprintf(stderr, "\t-o <output file>\tWrite the PEM certificates to the target file, rather than stdout\n");
     289}
     290
     291static void certsync_keychain_cb (ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
     292{
     293    MPCertSyncConfig *config = (MPCertSyncConfig *) clientCallBackInfo;
     294
     295    int ret;
     296    if ((ret = exportCertificates(config->userAnchors, config->outputFile)) != EXIT_SUCCESS)
     297        exit(ret);
    255298}
    256299
     
    260303    /* Parse the command line arguments */
    261304    BOOL userAnchors = NO;
     305    BOOL runServer = NO;
    262306    NSString *outputFile = nil;
    263307   
    264308    int ch;
    265     while ((ch = getopt(argc, argv, "huo:")) != -1) {
     309    while ((ch = getopt(argc, argv, "hsuo:")) != -1) {
    266310        switch (ch) {
    267311            case 'u':
     
    269313                break;
    270314               
     315            case 's':
     316                runServer = YES;
     317                break;
     318               
    271319            case 'o':
    272320                outputFile = [NSString stringWithUTF8String: optarg];
     
    285333    argv += optind;
    286334   
    287     /* Perform export  */
    288     int ret = exportCertificates(userAnchors, outputFile);
    289 
     335    /* Perform single-shot export  */
     336    if (!runServer)
     337        return exportCertificates(userAnchors, outputFile);
     338   
     339    /* Formulate the list of directories to observe; We use FSEvents rather than SecKeychainAddCallback(), as during testing the keychain
     340     * API never actually fired a callback for the target keychains. */
     341    NSSearchPathDomainMask searchPathDomains = NSLocalDomainMask|NSSystemDomainMask;
     342    if (userAnchors)
     343        searchPathDomains |= NSUserDomainMask;
     344
     345    NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, searchPathDomains, YES);
     346    NSMutableArray *keychainDirectories = [NSMutableArray arrayWithCapacity: [libraryDirectories count]];
     347    for (NSString *dir in libraryDirectories) {
     348        [keychainDirectories addObject: [dir stringByAppendingPathComponent: @"Keychains"]];
     349        [keychainDirectories addObject: [dir stringByAppendingPathComponent: @"Security/Trust Settings"]];
     350    }
     351
     352    /* Configure the listener */
     353    FSEventStreamRef eventStream;
     354    MPCertSyncConfig *config = [[[MPCertSyncConfig alloc] init] autorelease];
     355    config->userAnchors = userAnchors;
     356    config->outputFile = [outputFile retain];
     357
     358    FSEventStreamContext ctx = {
     359        .version = 0,
     360        .info = config,
     361        .retain = CFRetain,
     362        .release = CFRelease,
     363        .copyDescription = CFCopyDescription
     364    };
     365    eventStream = FSEventStreamCreate(NULL, certsync_keychain_cb, &ctx, (CFArrayRef)keychainDirectories, kFSEventStreamEventIdSinceNow, 0.0, kFSEventStreamCreateFlagUseCFTypes);
     366    FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     367    FSEventStreamStart(eventStream);
     368
     369    /* Perform an initial one-shot export, and then run forever */
     370    int ret;
     371    if ((ret = exportCertificates(userAnchors, outputFile)) != EXIT_SUCCESS)
     372        return EXIT_FAILURE;
     373   
     374    CFRunLoopRun();
     375
     376    FSEventStreamRelease(eventStream);
    290377    [pool release];
    291    
    292     return ret;
    293 }
    294 
     378
     379    return EXIT_SUCCESS;
     380}
     381
Note: See TracChangeset for help on using the changeset viewer.