[124828] trunk/dports/security/certsync
cal at macports.org
cal at macports.org
Thu Aug 28 10:22:36 PDT 2014
Revision: 124828
https://trac.macports.org/changeset/124828
Author: cal at macports.org
Date: 2014-08-28 10:22:36 -0700 (Thu, 28 Aug 2014)
Log Message:
-----------
certsync: Don't export certificates marked as untrusted, export manually added certificates
- provide a function that reads a certificate's subject, if any (and supported)
- verify that a CA is trusted by the system, filtering (a) expired, and (b)
explicitly untrusted ones
Modified Paths:
--------------
trunk/dports/security/certsync/Portfile
trunk/dports/security/certsync/files/certsync.m
trunk/dports/security/certsync/files/compat.h
Modified: trunk/dports/security/certsync/Portfile
===================================================================
--- trunk/dports/security/certsync/Portfile 2014-08-28 15:02:57 UTC (rev 124827)
+++ trunk/dports/security/certsync/Portfile 2014-08-28 17:22:36 UTC (rev 124828)
@@ -3,7 +3,7 @@
PortSystem 1.0
name certsync
-version 1.0.7
+version 1.1.0
categories security
conflicts curl-ca-bundle
maintainers landonf openmaintainer
Modified: trunk/dports/security/certsync/files/certsync.m
===================================================================
--- trunk/dports/security/certsync/files/certsync.m 2014-08-28 15:02:57 UTC (rev 124827)
+++ trunk/dports/security/certsync/files/certsync.m 2014-08-28 17:22:36 UTC (rev 124828)
@@ -60,91 +60,192 @@
int nsvfprintf (FILE *stream, NSString *format, va_list args) {
int retval;
-
+
NSString *str;
str = (NSString *) CFStringCreateWithFormatAndArguments(NULL, NULL, (CFStringRef) format, args);
retval = fprintf(stream, "%s", [str UTF8String]);
[str release];
-
+
return retval;
}
int nsfprintf (FILE *stream, NSString *format, ...) {
va_list ap;
int retval;
-
+
va_start(ap, format);
{
retval = nsvfprintf(stream, format, ap);
}
va_end(ap);
-
+
return retval;
}
int nsprintf (NSString *format, ...) {
va_list ap;
int retval;
-
+
va_start(ap, format);
{
retval = nsvfprintf(stderr, format, ap);
}
va_end(ap);
-
+
return retval;
}
/**
- * Verify that the root certificate trusts itself; this filters out certificates that
- * are still marked as trusted by the OS, but are expired or otherwise unusable.
+ * Wrapper method to retrieve the common name (CN) given a @code SecCertificateRef. If retrieving
+ * the CN isn't supported on this platform, returns @code NO, otherwise @code YES and points the
+ * given string ref @a subject to a string containing the common name. If an error occurs, subject
+ * is a NULL reference and *subjectError contains more information about the type of failure.
+ *
+ * @param cert A SecCertificateRef to the certificate for which the CN should be retrieved
+ * @param subject A pointer to a CFStringRef that will hold the CN if the retrieval is successful.
+ * @param subjectError A pointer to an NSError* that will hold an error message if an error occurs.
+ * @return BOOL indicating whether this system supports retrieving CNs from certificates
*/
-static BOOL ValidateSelfTrust (SecCertificateRef cert) {
+static BOOL GetCertSubject(SecCertificateRef cert, CFStringRef *subject, NSError **subjectError) {
+ if (SecCertificateCopyShortDescription != NULL /* 10.7 */) {
+ *subject = PLCFAutorelease(SecCertificateCopyShortDescription(NULL, cert, (CFErrorRef *) subjectError));
+ return YES;
+ }
+
+ if (SecCertificateCopySubjectSummary != NULL /* 10.6 */) {
+ *subject = PLCFAutorelease(SecCertificateCopySubjectSummary(cert));
+ return YES;
+ }
+
+ if (SecCertificateCopyCommonName != NULL /* 10.5 */) {
+ OSStatus err;
+ if ((err = SecCertificateCopyCommonName(cert, subject)) == errSecSuccess && *subject != NULL) {
+ PLCFAutorelease(subject);
+ return YES;
+ }
+
+ /* In the case that the CN is simply unavailable, provide a more useful error code */
+ if (err == errSecSuccess) {
+ err = errSecNoSuchAttr;
+ }
+
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: @"SecCertificateCopyCommonName() failed", NSLocalizedDescriptionKey, nil];
+ *subjectError = [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo: userInfo];
+ *subject = NULL;
+
+ return YES;
+ }
+
+ /* <= 10.4 */
+ return NO;
+}
+
+/**
+ * Verify that the root certificate is trusted by the system; this filters out
+ * certificates that are (1) expired, or (2) in the system keychain but marked
+ * as untrusted by a system administrator (or user).
+ *
+ * @param cert A @code SecCertificateRef representing a certificate to be
+ * checked.
+ *
+ * @return Returns a BOOL indicating that the certificate is trusted (@code
+ * YES), or not (@code NO).
+ */
+static BOOL ValidateSystemTrust(SecCertificateRef cert) {
OSStatus err;
/* Create a new trust evaluation instance */
SecTrustRef trust;
+ {
+ SecPolicyRef policy;
+ if (SecPolicyCreateBasicX509 != NULL) /* >= 10.6 */ {
+ policy = SecPolicyCreateBasicX509();
+ } else /* < 10.6 */ {
+ SecPolicySearchRef searchRef = NULL;
+ const CSSM_OID *policyOID = &CSSMOID_APPLE_X509_BASIC;
+
+ if ((err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &searchRef)) != errSecSuccess) {
+ cssmPerror("SecPolicySearchCreate", err);
+ return NO;
+ }
+ if ((err = SecPolicySearchCopyNext(searchRef, &policy))) {
+ cssmPerror("SecPolicySearchCopyNext", err);
+ return NO;
+ }
+ }
+
+ if ((err = SecTrustCreateWithCertificates((CFTypeRef) cert, policy, &trust)) != errSecSuccess) {
+ /* Shouldn't happen */
+ nsfprintf(stderr, @"Failed to create SecTrustRef: %d\n", err);
+ CFRelease(policy);
+ return NO;
+ }
+
+ CFRelease(policy);
+ }
+
+ /* Allow verifying root certificates (which would otherwise be an error).
+ * Without this, intermediates added as roots aren't exported. */
{
- SecPolicyRef policy = SecPolicyCreateBasicX509();
- if ((err = SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust)) != errSecSuccess) {
- /* Shouldn't happen */
- nsfprintf(stderr, @"Failed to create SecTrustRef: %d\n", err);
- CFRelease(policy);
+ CSSM_APPLE_TP_ACTION_FLAGS actionFlags = CSSM_TP_ACTION_LEAF_IS_CA;
+ CSSM_APPLE_TP_ACTION_DATA actionData;
+ CFDataRef cfActionData = NULL;
+
+ memset(&actionData, 0, sizeof(actionData));
+ actionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
+ actionData.ActionFlags = actionFlags;
+ cfActionData = CFDataCreate(NULL, (UInt8 *) &actionData, sizeof(actionData));
+ if ((err = SecTrustSetParameters(trust, CSSM_TP_ACTION_DEFAULT, cfActionData))) {
+ nsfprintf(stderr, @"Failed to set SecTrustParameters: %d\n", err);
+ CFRelease(cfActionData);
return NO;
}
- CFRelease(policy);
+ CFRelease(cfActionData);
}
-
- /* Set this certificate as the only (self-)anchor */
- {
- CFArrayRef certs = CFArrayCreate(NULL, (const void **) &cert, 1, &kCFTypeArrayCallBacks);
- if ((err = SecTrustSetAnchorCertificates(trust, certs)) != errSecSuccess) {
- nsfprintf(stderr, @"Failed to set anchor certificates on our SecTrustRef: %d\n", err);
- CFRelease(certs);
- CFRelease(trust);
- return NO;
- }
- CFRelease(certs);
- }
-
+
/* Evaluate the certificate trust */
SecTrustResultType rt;
if ((err = SecTrustEvaluate(trust, &rt)) != errSecSuccess) {
nsfprintf(stderr, @"SecTrustEvaluate() failed: %d\n", err);
CFRelease(trust);
+ return NO;
}
-
+
CFRelease(trust);
-
+
/* Check the result */
switch (rt) {
case kSecTrustResultUnspecified:
+ /* Reached a trusted root */
case kSecTrustResultProceed:
- /* Trusted */
+ /* User explicitly trusts */
return YES;
-
+
+ case kSecTrustResultDeny:
+ /* User explicitly distrusts */
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7
+ case kSecTrustResultConfirm:
+ /* The user previously chose to ask for permission when this
+ * certificate is used. This is no longer used on modern OS X. */
+#endif
+ //nsfprintf(stderr, @"Marked as untrustable!\n");
+ return NO;
+
+ case kSecTrustResultRecoverableTrustFailure:
+ /* The chain as-is isn't trusted, but it could be trusted with some
+ * minor change (such as ignoring expired certs or adding an
+ * additional anchor). For certsync this likely means the cert was
+ * expired, which means we don't want to export it. */
+ return NO;
+
+ case kSecTrustResultFatalTrustFailure:
+ /* The chain is defective (e.g., ill-formatted). Don't export this. */
+ return NO;
+
default:
/* Untrusted */
+ nsfprintf(stderr, @"rt = %d\n", rt);
+ cssmPerror("CSSM verify error", err);
return NO;
}
}
@@ -162,7 +263,7 @@
NSMutableArray *trusted = nil;
CFArrayRef certs = nil;
OSStatus err;
-
+
/* Mac OS X >= 10.5 provides SecTrustSettingsCopyCertificates() */
if (SecTrustSettingsCopyCertificates != NULL) {
/* Fetch all certificates in the given domain */
@@ -171,27 +272,27 @@
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] retain];
-
+
[pool release];
[*outError autorelease];
return nil;
}
-
+
/* Extract trusted roots */
trusted = [NSMutableArray arrayWithCapacity: CFArrayGetCount(certs)];
-
+
NSEnumerator *resultEnumerator = [(NSArray *)certs objectEnumerator];
id certObj;
while ((certObj = [resultEnumerator nextObject]) != nil) {
SecCertificateRef cert = (SecCertificateRef) certObj;
-
+
/* Fetch the trust settings */
CFArrayRef trustSettings = nil;
err = SecTrustSettingsCopyTrustSettings(cert, domain, &trustSettings);
@@ -202,7 +303,7 @@
} else {
PLCFAutorelease(trustSettings);
}
-
+
/* If empty, trust for everything (as per the Security Framework documentation) */
if (CFArrayGetCount(trustSettings) == 0) {
[trusted addObject: certObj];
@@ -213,10 +314,10 @@
while ((trustProps = [trustEnumerator nextObject]) != nil) {
CFNumberRef settingsResultNum;
SInt32 settingsResult;
-
+
settingsResultNum = (CFNumberRef) [trustProps objectForKey: (id) kSecTrustSettingsResult];
CFNumberGetValue(settingsResultNum, kCFNumberSInt32Type, &settingsResult);
-
+
/* If a root, add to the result set */
if (settingsResult == kSecTrustSettingsResultTrustRoot || settingsResult == kSecTrustSettingsResultTrustAsRoot) {
[trusted addObject: certObj];
@@ -248,7 +349,7 @@
trusted = [[(NSArray *) certs mutableCopy] autorelease];
CFRelease(certs);
}
-
+
/*
* Filter out any trusted certificates that can not actually be used in verification; eg, they are expired.
*
@@ -265,11 +366,21 @@
id certObj;
while ((certObj = [trustedEnumerator nextObject]) != nil) {
/* If self-trust validation fails, the certificate is expired or otherwise not useable */
- if (!ValidateSelfTrust((SecCertificateRef) certObj)) {
+ if (!ValidateSystemTrust((SecCertificateRef) certObj)) {
+ NSError *subjectError = NULL;
+ CFStringRef subject = NULL;
+
+ if (GetCertSubject((SecCertificateRef) certObj, &subject, &subjectError)) {
+ if (subject != NULL) {
+ nsfprintf(stderr, @"Removing untrusted certificate: %@\n", subject);
+ } else {
+ nsfprintf(stderr, @"Failed to extract certificate description for untrusted certificate: %@\n", subjectError);
+ }
+ }
[trusted removeObject: certObj];
}
}
-
+
[trusted retain];
[pool release];
return [trusted autorelease];
@@ -281,7 +392,7 @@
/*
* Fetch all certificates
*/
-
+
NSMutableArray *anchors = [NSMutableArray array];
NSArray *result;
NSError *error;
@@ -289,6 +400,16 @@
/* Current user */
if (userAnchors) {
+ /* Set the keychain preference domain to user, this causes
+ * ValidateSystemTrust to use the user's keychain */
+ if ((err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainUser)) != errSecSuccess) {
+ CFStringRef errMsg = PLCFAutorelease(SecCopyErrorMessageString(err, NULL));
+ nsfprintf(stderr, @"Failed to set keychain preference domain: %@\n", errMsg);
+
+ [pool release];
+ return EXIT_FAILURE;
+ }
+
result = certificatesForTrustDomain(kSecTrustSettingsDomainUser, &error);
if (result != nil) {
[anchors addObjectsFromArray: result];
@@ -298,7 +419,17 @@
return EXIT_FAILURE;
}
}
-
+
+ /* Admin & System */
+ /* Causes ValidateSystemTrust to ignore the user's keychain */
+ if ((err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem)) != errSecSuccess) {
+ CFStringRef errMsg = PLCFAutorelease(SecCopyErrorMessageString(err, NULL));
+ nsfprintf(stderr, @"Failed to set keychain preference domain: %@\n", errMsg);
+
+ [pool release];
+ return EXIT_FAILURE;
+ }
+
/* Admin */
result = certificatesForTrustDomain(kSecTrustSettingsDomainAdmin, &error);
if (result != nil) {
@@ -308,7 +439,7 @@
[pool release];
return EXIT_FAILURE;
}
-
+
/* System */
result = certificatesForTrustDomain(kSecTrustSettingsDomainSystem, &error);
if (result != nil) {
@@ -318,50 +449,27 @@
[pool release];
return EXIT_FAILURE;
}
-
- NSEnumerator *resultEnumerator = [result objectEnumerator];
+
+ NSEnumerator *resultEnumerator = [anchors objectEnumerator];
id certObj;
while ((certObj = [resultEnumerator nextObject]) != nil) {
NSError *subjectError = NULL;
CFStringRef subject = NULL;
- BOOL subjectUnsupported = NO;
- if (SecCertificateCopyShortDescription != NULL /* 10.7 */) {
- subject = PLCFAutorelease(SecCertificateCopyShortDescription(NULL, (SecCertificateRef) certObj, (CFErrorRef *) &subjectError));
-
- } else if (SecCertificateCopySubjectSummary != NULL /* 10.6 */) {
- subject = PLCFAutorelease(SecCertificateCopySubjectSummary((SecCertificateRef) certObj));
-
- } else if (SecCertificateCopyCommonName != NULL /* 10.5 */) {
- if ((err = SecCertificateCopyCommonName((SecCertificateRef) certObj, &subject)) == errSecSuccess && subject != NULL) {
- PLCFAutorelease(subject);
+ if (GetCertSubject((SecCertificateRef) certObj, &subject, &subjectError)) {
+ if (subject != NULL) {
+ nsfprintf(stderr, @"Found %@\n", subject);
} else {
- /* In the case that the CN is simply unavailable, provide a more useful error code */
- if (err == errSecSuccess)
- err = errSecNoSuchAttr;
-
- NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: @"SecCertificateCopyCommonName() failed", NSLocalizedDescriptionKey, nil];
- subjectError = [NSError errorWithDomain: NSOSStatusErrorDomain code: err userInfo: userInfo];
- subject = NULL;
+ nsfprintf(stderr, @"Failed to extract certificate description: %@\n", subjectError);
}
- } else /* <= 10.4 */ {
- subjectUnsupported = YES;
}
+ }
- if (subject == NULL) {
- /* Don't print an error if fetching the subject is unsupported on the platform (eg, <= 10.4) */
- if (!subjectUnsupported)
- nsfprintf(stderr, @"Failed to extract certificate description: %@\n", subjectError);
- } else {
- nsfprintf(stderr, @"Found %@\n", subject);
- }
- }
-
/*
* Perform export
*/
CFDataRef pemData;
-
+
/* Prefer the non-deprecated SecItemExport on Mac OS X >= 10.7. We use an ifdef to keep the code buildable with earlier SDKs, too. */
nsfprintf(stderr, @"Exporting certificates from the keychain\n");
if (SecItemExport != NULL) {
@@ -388,7 +496,7 @@
return EXIT_FAILURE;
}
}
-
+
[pool release];
return EXIT_SUCCESS;
}
@@ -405,14 +513,14 @@
/* Parse the command line arguments */
BOOL userAnchors = NO;
NSString *outputFile = nil;
-
+
int ch;
while ((ch = getopt(argc, argv, "hsuo:")) != -1) {
switch (ch) {
case 'u':
userAnchors = YES;
break;
-
+
case 'o':
outputFile = [NSString stringWithUTF8String: optarg];
break;
@@ -428,7 +536,7 @@
}
argc -= optind;
argv += optind;
-
+
/* Perform export */
int result = exportCertificates(userAnchors, outputFile);
Modified: trunk/dports/security/certsync/files/compat.h
===================================================================
--- trunk/dports/security/certsync/files/compat.h 2014-08-28 15:02:57 UTC (rev 124827)
+++ trunk/dports/security/certsync/files/compat.h 2014-08-28 17:22:36 UTC (rev 124828)
@@ -74,6 +74,10 @@
/* SecCertificateCopySubjectSummary() was added in 10.6 */
extern CFStringRef SecCertificateCopySubjectSummary (SecCertificateRef certificate) __attribute__((weak_import));
#define SecCertificateCopySubjectSummary ((CFStringRef(*)(SecCertificateRef)) NULL) /* We can't safely weak-link what we don't have */
+
+ /* SecPolicyCreateBasicX509() was added in 10.6 */
+ extern SecPolicyRef SecPolicyCreateBasicX509 (void) __attribute__((weak_import));
+ #define SecPolicyCreateBasicX509 ((SecPolicyRef(*)(void)) NULL) /* We can't safely weak-link what we don't have */
#endif
/* Allow building with SDKs <= 10.6 */
@@ -102,4 +106,4 @@
CFDataRef *exportedData
) __attribute__((weak_import));
#define SecItemExport ((OSStatus(*)(CFTypeRef, SecExternalFormat, SecItemImportExportFlags, const SecItemImportExportKeyParameters *, CFDataRef *)) NULL) /* We can't safely weak-link what we don't have */
-#endif
\ No newline at end of file
+#endif
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/macports-changes/attachments/20140828/0b193448/attachment-0001.html>
More information about the macports-changes
mailing list