[106042] trunk/dports/security/certsync

landonf at macports.org landonf at macports.org
Mon May 13 18:20:04 PDT 2013


Revision: 106042
          https://trac.macports.org/changeset/106042
Author:   landonf at macports.org
Date:     2013-05-13 18:20:04 -0700 (Mon, 13 May 2013)
Log Message:
-----------
Add support for automatically regenerating the output file when the system keychain or trust settings are modified.

Modified Paths:
--------------
    trunk/dports/security/certsync/Portfile
    trunk/dports/security/certsync/files/certsync.m

Modified: trunk/dports/security/certsync/Portfile
===================================================================
--- trunk/dports/security/certsync/Portfile	2013-05-14 01:06:32 UTC (rev 106041)
+++ trunk/dports/security/certsync/Portfile	2013-05-14 01:20:04 UTC (rev 106042)
@@ -2,8 +2,7 @@
 
 PortSystem 1.0
 name			certsync
-version			1.0.1
-revision		1
+version			1.0.2
 categories		security
 conflicts		curl-ca-bundle
 maintainers		landonf openmaintainer
@@ -17,11 +16,11 @@
 
 use_configure no
 
-# TODO: How should we run this automatically? Attempts to use SecKeychainAddCallback()
-# in a blocking daemon haven't been successful; the callback is never called.
-# One possible hack is observing events on /System/Library/Keychains
-#startupitem.create yes
-#startupitem.start "${prefix}/bin/update-ca-certificates"
+# TODO: Ideally this would be run by default, rather than
+# requiring 'port load'. It doesn't run any network services, but rather,
+# simply ensures that the certificate store is always up-to-date.
+startupitem.create yes
+startupitem.start "${prefix}/bin/certsync -s -o '${prefix}/etc/openssl/cert.pem'"
 
 build {
 	file mkdir "${worksrcpath}"
@@ -31,7 +30,7 @@
 		-Wall \
 		${filespath}/certsync.m -o ${worksrcpath}/certsync \
 		${configure.ldflags} \
-		-framework Foundation -framework Security"
+		-framework Foundation -framework Security -framework CoreServices"
 	file copy "${filespath}/update-ca-certificates" "${worksrcpath}/update-ca-certificates"
 	reinplace "s|@PREFIX@|${prefix}|g" "${worksrcpath}/update-ca-certificates"
 }

Modified: trunk/dports/security/certsync/files/certsync.m
===================================================================
--- trunk/dports/security/certsync/files/certsync.m	2013-05-14 01:06:32 UTC (rev 106041)
+++ trunk/dports/security/certsync/files/certsync.m	2013-05-14 01:20:04 UTC (rev 106042)
@@ -33,6 +33,22 @@
 
 #import <objc/message.h>
 
+/* A wrapper class that may be used to pass configuration through the
+ * FSEvent callback API */
+ at interface MPCertSyncConfig : NSObject {
+ at public
+    BOOL userAnchors;
+    NSString *outputFile;
+}
+ at end
+
+ at implementation MPCertSyncConfig
+- (void) dealloc {
+    [outputFile release];
+    [super dealloc];
+}
+ at end
+
 /**
  * Add CoreFoundation object to the current autorelease pool.
  *
@@ -56,6 +72,7 @@
     NSString *str;
     str = (NSString *) CFStringCreateWithFormatAndArguments(NULL, NULL, (CFStringRef) format, args);
     retval = fprintf(stream, "%s", [str UTF8String]);
+    [str release];
     
     return retval;
 }
@@ -95,6 +112,7 @@
  * @return Returns a (possibly empty) array of certificates on success, nil on failure.
  */
 static NSArray *certificatesForTrustDomain (SecTrustSettingsDomain domain, NSError **outError) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     CFArrayRef certs = nil;
     OSStatus err;
     
@@ -104,12 +122,15 @@
         PLCFAutorelease(certs);
     } else if (err == errSecNoTrustSettings ) {
         /* No data */
+        
+        [pool release];
         return [NSArray array];
-        
     } else if (err != errSecSuccess) {
         /* Lookup failed */
         if (outError != NULL)
             *outError = [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil];
+        
+        [pool release];
         return nil;
     }
     
@@ -149,11 +170,15 @@
             }
         }
     }
-    
-    return results;
+
+    [results retain];
+    [pool release];
+    return [results autorelease];
 }
 
 static int exportCertificates (BOOL userAnchors, NSString *outputFile) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
     /*
      * Fetch all certificates
      */
@@ -169,6 +194,7 @@
             [anchors addObjectsFromArray: result];
         } else {
             nsfprintf(stderr, @"Failed to fetch user anchors: %@\n", error);
+            [pool release];
             return EXIT_FAILURE;
         }
     }
@@ -179,6 +205,7 @@
         [anchors addObjectsFromArray: result];
     } else {
         nsfprintf(stderr, @"Failed to fetch admin anchors: %@\n", error);
+        [pool release];
         return EXIT_FAILURE;
     }
     
@@ -188,6 +215,7 @@
         [anchors addObjectsFromArray: result];
     } else {
         nsfprintf(stderr, @"Failed to fetch system anchors: %@\n", error);
+        [pool release];
         return EXIT_FAILURE;
     }
     
@@ -207,6 +235,7 @@
 
         if (subject == NULL) {
             nsfprintf(stderr, @"Failed to extract certificate description: %@\n", cferror);
+            [pool release];
             return EXIT_FAILURE;
         } else {
             nsfprintf(stderr, @"Extracting %@\n", subject);
@@ -229,9 +258,11 @@
 #else
     err = SecKeychainItemExport((CFArrayRef) anchors, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &pemData);
 #endif
-    
+    PLCFAutorelease(pemData);
+
     if (err != errSecSuccess) {
         nsfprintf(stderr, @"Failed to export certificates: %@\n", [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo:nil]);
+        [pool release];
         return EXIT_FAILURE;
     }
 
@@ -241,33 +272,50 @@
     } else {
         if (![(NSData *) pemData writeToFile: outputFile options: NSDataWritingAtomic error: &error]) {
             nsfprintf(stderr, @"Failed to write to pem output file: %@\n", error);
+            [pool release];
             return EXIT_FAILURE;
         }
     }
     
+    [pool release];
     return EXIT_SUCCESS;
 }
 
 static void usage (const char *progname) {
     fprintf(stderr, "Usage: %s [-u] [-o <output file>]\n", progname);
     fprintf(stderr, "\t-u\t\t\tInclude the current user's anchor certificates.\n");
+    fprintf(stderr, "\t-s\t\t\tDo not exit; observe the system keychain(s) for changes and update the output file accordingly.");
     fprintf(stderr, "\t-o <output file>\tWrite the PEM certificates to the target file, rather than stdout\n");
 }
 
+static void certsync_keychain_cb (ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
+{
+    MPCertSyncConfig *config = (MPCertSyncConfig *) clientCallBackInfo;
+
+    int ret;
+    if ((ret = exportCertificates(config->userAnchors, config->outputFile)) != EXIT_SUCCESS)
+        exit(ret);
+}
+
 int main (int argc, char * const argv[]) {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
     /* Parse the command line arguments */
     BOOL userAnchors = NO;
+    BOOL runServer = NO;
     NSString *outputFile = nil;
     
     int ch;
-    while ((ch = getopt(argc, argv, "huo:")) != -1) {
+    while ((ch = getopt(argc, argv, "hsuo:")) != -1) {
         switch (ch) {
             case 'u':
                 userAnchors = YES;
                 break;
                 
+            case 's':
+                runServer = YES;
+                break;
+                
             case 'o':
                 outputFile = [NSString stringWithUTF8String: optarg];
                 break;
@@ -284,11 +332,50 @@
     argc -= optind;
     argv += optind;
     
-    /* Perform export  */
-    int ret = exportCertificates(userAnchors, outputFile);
+    /* Perform single-shot export  */
+    if (!runServer)
+        return exportCertificates(userAnchors, outputFile);
+    
+    /* Formulate the list of directories to observe; We use FSEvents rather than SecKeychainAddCallback(), as during testing the keychain
+     * API never actually fired a callback for the target keychains. */
+    NSSearchPathDomainMask searchPathDomains = NSLocalDomainMask|NSSystemDomainMask;
+    if (userAnchors)
+        searchPathDomains |= NSUserDomainMask;
 
+    NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, searchPathDomains, YES);
+    NSMutableArray *keychainDirectories = [NSMutableArray arrayWithCapacity: [libraryDirectories count]];
+    for (NSString *dir in libraryDirectories) {
+        [keychainDirectories addObject: [dir stringByAppendingPathComponent: @"Keychains"]];
+        [keychainDirectories addObject: [dir stringByAppendingPathComponent: @"Security/Trust Settings"]];
+    }
+
+    /* Configure the listener */
+    FSEventStreamRef eventStream;
+    MPCertSyncConfig *config = [[[MPCertSyncConfig alloc] init] autorelease];
+    config->userAnchors = userAnchors;
+    config->outputFile = [outputFile retain];
+
+    FSEventStreamContext ctx = {
+        .version = 0,
+        .info = config,
+        .retain = CFRetain,
+        .release = CFRelease,
+        .copyDescription = CFCopyDescription
+    };
+    eventStream = FSEventStreamCreate(NULL, certsync_keychain_cb, &ctx, (CFArrayRef)keychainDirectories, kFSEventStreamEventIdSinceNow, 0.0, kFSEventStreamCreateFlagUseCFTypes);
+    FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+    FSEventStreamStart(eventStream);
+
+    /* Perform an initial one-shot export, and then run forever */
+    int ret;
+    if ((ret = exportCertificates(userAnchors, outputFile)) != EXIT_SUCCESS)
+        return EXIT_FAILURE;
+    
+    CFRunLoopRun();
+
+    FSEventStreamRelease(eventStream);
     [pool release];
-    
-    return ret;
+
+    return EXIT_SUCCESS;
 }
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20130513/ace4d88e/attachment-0001.html>


More information about the macports-changes mailing list