keychain: support on-the-fly enumeration of trusted/untrusted certificates
authorMartin Willi <martin@revosec.ch>
Tue, 30 Apr 2013 09:59:01 +0000 (11:59 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 18 Jul 2013 10:17:54 +0000 (12:17 +0200)
src/libstrongswan/plugins/keychain/Makefile.am
src/libstrongswan/plugins/keychain/keychain_creds.c

index e0d25b6..508a4b0 100644 (file)
@@ -13,4 +13,5 @@ libstrongswan_keychain_la_SOURCES = \
        keychain_plugin.h keychain_plugin.c \
        keychain_creds.h keychain_creds.c
 
-libstrongswan_keychain_la_LDFLAGS = -module -avoid-version
+libstrongswan_keychain_la_LDFLAGS = -module -avoid-version \
+       -framework Security -framework CoreFoundation
index d3331fa..08ef826 100644 (file)
@@ -17,6 +17,8 @@
 
 #include <utils/debug.h>
 
+#include <Security/Security.h>
+
 typedef struct private_keychain_creds_t private_keychain_creds_t;
 
 /**
@@ -30,10 +32,124 @@ struct private_keychain_creds_t {
        keychain_creds_t public;
 };
 
+/**
+ * Enumerator for certificates
+ */
+typedef struct {
+       /* implements enumerator_t */
+       enumerator_t public;
+       /* currently enumerating certificate */
+       certificate_t *current;
+       /* id to filter for */
+       identification_t *id;
+       /* certificate public key type we are looking for */
+       key_type_t type;
+       /* array of binary certificates to enumerate */
+       CFArrayRef certs;
+       /* current position in array */
+       int i;
+} cert_enumerator_t;
+
+METHOD(enumerator_t, enumerate_certs, bool,
+       cert_enumerator_t *this, certificate_t **out)
+{
+       DESTROY_IF(this->current);
+       this->current = NULL;
+
+       while (this->i < CFArrayGetCount(this->certs))
+       {
+               certificate_t *cert;
+               public_key_t *key;
+               CFDataRef data;
+               chunk_t chunk;
+
+               data = CFArrayGetValueAtIndex(this->certs, this->i++);
+               if (data)
+               {
+                       chunk = chunk_create((char*)CFDataGetBytePtr(data),
+                                                                CFDataGetLength(data));
+                       cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+                                                                         BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+                       if (cert)
+                       {
+                               if (!this->id || cert->has_subject(cert, this->id))
+                               {
+                                       key = cert->get_public_key(cert);
+                                       if (key)
+                                       {
+                                               if (this->type == KEY_ANY ||
+                                                       this->type == key->get_type(key))
+                                               {
+                                                       key->destroy(key);
+                                                       this->current = cert;
+                                                       *out = cert;
+                                                       return TRUE;
+                                               }
+                                               key->destroy(key);
+                                       }
+                               }
+                               cert->destroy(cert);
+                       }
+               }
+       }
+       return FALSE;
+}
+
+METHOD(enumerator_t, destroy_certs, void,
+       cert_enumerator_t *this)
+{
+       DESTROY_IF(this->current);
+       CFRelease(this->certs);
+       free(this);
+}
+
 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
        private_keychain_creds_t *this, certificate_type_t cert, key_type_t key,
        identification_t *id, bool trusted)
 {
+       cert_enumerator_t *enumerator;
+       OSStatus status;
+       CFDictionaryRef query;
+       CFArrayRef result;
+       const void* keys[] = {
+               kSecReturnData,
+               kSecMatchLimit,
+               kSecClass,
+               kSecAttrCanVerify,
+               kSecMatchTrustedOnly,
+       };
+       const void* values[] = {
+               kCFBooleanTrue,
+               kSecMatchLimitAll,
+               kSecClassCertificate,
+               kCFBooleanTrue,
+               trusted ? kCFBooleanTrue : kCFBooleanFalse,
+       };
+
+       if (cert == CERT_ANY || cert == CERT_X509)
+       {
+               query = CFDictionaryCreate(NULL, keys, values, countof(keys),
+                                                                  &kCFTypeDictionaryKeyCallBacks,
+                                                                  &kCFTypeDictionaryValueCallBacks);
+               if (query)
+               {
+                       status = SecItemCopyMatching(query, (CFTypeRef*)&result);
+                       CFRelease(query);
+                       if (status == errSecSuccess)
+                       {
+                               INIT(enumerator,
+                                       .public = {
+                                               .enumerate = (void*)_enumerate_certs,
+                                               .destroy = _destroy_certs,
+                                       },
+                                       .certs = result,
+                                       .id = id,
+                                       .type = key,
+                               );
+                               return &enumerator->public;
+                       }
+               }
+       }
        return enumerator_create_empty();
 }