Implemented X.509 certificate reading using OpenSSL
authorMartin Willi <martin@revosec.ch>
Thu, 20 May 2010 08:09:04 +0000 (08:09 +0000)
committerMartin Willi <martin@revosec.ch>
Fri, 21 May 2010 14:25:51 +0000 (16:25 +0200)
src/libstrongswan/plugins/openssl/Makefile.am
src/libstrongswan/plugins/openssl/openssl_plugin.c
src/libstrongswan/plugins/openssl/openssl_util.c
src/libstrongswan/plugins/openssl/openssl_util.h
src/libstrongswan/plugins/openssl/openssl_x509.c [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_x509.h [new file with mode: 0644]

index a507997..3ec1e37 100644 (file)
@@ -20,7 +20,8 @@ libstrongswan_openssl_la_SOURCES = \
        openssl_rsa_public_key.c openssl_rsa_public_key.h \
        openssl_ec_diffie_hellman.c openssl_ec_diffie_hellman.h \
        openssl_ec_private_key.c openssl_ec_private_key.h \
-       openssl_ec_public_key.c openssl_ec_public_key.h
+       openssl_ec_public_key.c openssl_ec_public_key.h \
+       openssl_x509.c openssl_x509.h
 
 libstrongswan_openssl_la_LDFLAGS = -module -avoid-version
 libstrongswan_openssl_la_LIBADD  = -lcrypto
index 558eba0..86ef332 100644 (file)
@@ -34,6 +34,7 @@
 #include "openssl_rsa_public_key.h"
 #include "openssl_ec_private_key.h"
 #include "openssl_ec_public_key.h"
+#include "openssl_x509.h"
 
 typedef struct private_openssl_plugin_t private_openssl_plugin_t;
 
@@ -191,6 +192,8 @@ static void destroy(private_openssl_plugin_t *this)
                                        (builder_function_t)openssl_ec_private_key_gen);
        lib->creds->remove_builder(lib->creds,
                                        (builder_function_t)openssl_ec_public_key_load);
+       lib->creds->remove_builder(lib->creds,
+                                       (builder_function_t)openssl_x509_load);
 
        ENGINE_cleanup();
        EVP_cleanup();
@@ -317,6 +320,10 @@ plugin_t *openssl_plugin_create()
        lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA,
                                        (builder_function_t)openssl_ec_public_key_load);
 
+       /* X509 certificates */
+       lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509,
+                                       (builder_function_t)openssl_x509_load);
+
        return &this->public.plugin;
 }
 
index 55b18a5..99dca36 100644 (file)
@@ -100,7 +100,6 @@ error:
        return FALSE;
 }
 
-
 /**
  * Described in header.
  */
@@ -124,3 +123,85 @@ bool openssl_bn_split(chunk_t chunk, BIGNUM *a, BIGNUM *b)
        return TRUE;
 }
 
+/**
+ * Described in header.
+ */
+chunk_t openssl_asn1_obj2chunk(ASN1_OBJECT *asn1)
+{
+       if (asn1)
+       {
+               return chunk_create(asn1->data, asn1->length);
+       }
+       return chunk_empty;
+}
+
+/**
+ * Described in header.
+ */
+chunk_t openssl_asn1_str2chunk(ASN1_STRING *asn1)
+{
+       if (asn1)
+       {
+               return chunk_create(ASN1_STRING_data(asn1), ASN1_STRING_length(asn1));
+       }
+       return chunk_empty;
+}
+
+/**
+ * Convert a X509 name to a ID_DER_ASN1_DN identification_t
+ */
+identification_t *openssl_x509_name2id(X509_NAME *name)
+{
+       if (name)
+       {
+               identification_t *id;
+               chunk_t chunk;
+
+               chunk = openssl_i2chunk(X509_NAME, name);
+               if (chunk.len)
+               {
+                       id = identification_create_from_encoding(ID_DER_ASN1_DN, chunk);
+                       free(chunk.ptr);
+                       return id;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * We can't include <asn1/asn1.h>, as the ASN1_ definitions would clash
+ * with OpenSSL. Redeclare what we need.
+ */
+int asn1_known_oid(chunk_t);
+time_t asn1_to_time(chunk_t *,int);
+
+/**
+ * Described in header.
+ */
+int openssl_asn1_known_oid(ASN1_OBJECT *obj)
+{
+       return asn1_known_oid(openssl_asn1_obj2chunk(obj));
+}
+
+/**
+ * Described in header.
+ */
+time_t openssl_asn1_to_time(ASN1_TIME *time)
+{
+       chunk_t chunk;
+
+       if (time)
+       {
+               chunk = openssl_asn1_str2chunk(time);
+               switch (time->type)
+               {
+                       case V_ASN1_UTCTIME:
+                       case V_ASN1_GENERALIZEDTIME:
+                               return asn1_to_time(&chunk, time->type);
+                       default:
+                               break;
+               }
+       }
+       DBG1(DBG_LIB, "invalid ASN1 time");
+       return 0;
+}
index 538008f..405936c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <library.h>
 #include <openssl/bn.h>
+#include <openssl/asn1.h>
 
 /**
  * Returns the length in bytes of a field element
@@ -65,4 +66,57 @@ bool openssl_bn_cat(int len, BIGNUM *a, BIGNUM *b, chunk_t *chunk);
  */
 bool openssl_bn_split(chunk_t chunk, BIGNUM *a, BIGNUM *b);
 
+
+/**
+ * Allocate a chunk using the i2d function of a given object
+ *
+ * @param type_id      type of the object
+ * @param object       object to convert to DER
+ * @returns                    allocated chunk of the object, or chunk_empty
+ */
+#define openssl_i2chunk(type, obj) ({ \
+                                       unsigned char *ptr = NULL; \
+                                       int len = i2d_##type(obj, &ptr); \
+                                       len < 0 ? chunk_empty : chunk_create(ptr, len);})
+
+/**
+ * Convert an OpenSSL ASN1_OBJECT to a chunk.
+ *
+ * @param asn1         asn1 object to convert
+ * @return                     chunk, pointing into asn1 object
+ */
+chunk_t openssl_asn1_obj2chunk(ASN1_OBJECT *asn1);
+
+/**
+ * Convert an OpenSSL ASN1_STRING to a chunk.
+ *
+ * @param asn1         asn1 string to convert
+ * @return                     chunk, pointing into asn1 string
+ */
+chunk_t openssl_asn1_str2chunk(ASN1_STRING *asn1);
+
+/**
+ * Convert an openssl X509_NAME to a identification_t of type ID_DER_ASN1_DN.
+ *
+ * @param name         name to convert
+ * @return                     identification_t, NULL on error
+ */
+identification_t *openssl_x509_name2id(X509_NAME *name);
+
+/**
+ * Check if an ASN1 oid is a an OID known by libstrongswan.
+ *
+ * @param object       openssl ASN1 object
+ * @returns                    OID, as defined in <asn1/oid.h>
+ */
+int openssl_asn1_known_oid(ASN1_OBJECT *obj);
+
+/**
+ * Convert an OpenSSL ASN1_TIME to a time_t.
+ *
+ * @param time         openssl ASN1_TIME
+ * @returns                    time_t, 0 on error
+ */
+time_t openssl_asn1_to_time(ASN1_TIME *time);
+
 #endif /** OPENSSL_UTIL_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_x509.c b/src/libstrongswan/plugins/openssl/openssl_x509.c
new file mode 100644 (file)
index 0000000..0ef29b3
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/*
+ * Copyright (C) 2010 secunet Security Networks AG
+ * Copyright (C) 2010 Thomas Egerer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "openssl_x509.h"
+#include "openssl_util.h"
+
+#include <debug.h>
+#include <asn1/oid.h>
+#include <utils/linked_list.h>
+
+
+typedef struct private_openssl_x509_t private_openssl_x509_t;
+
+/**
+ * Private data of an openssl_x509_t object.
+ */
+struct private_openssl_x509_t {
+
+       /**
+        * Public openssl_x509_t interface.
+        */
+       openssl_x509_t public;
+
+       /**
+        * OpenSSL certificate representation
+        */
+       X509 *x509;
+
+       /**
+        * DER encoded certificate
+        */
+       chunk_t encoding;
+
+       /**
+        * SHA1 hash of the certificate
+        */
+       chunk_t hash;
+
+       /**
+        * X509 flags
+        */
+       x509_flag_t flags;
+
+       /**
+        * Pathlen constraint
+        */
+       int pathlen;
+
+       /**
+        * certificate subject
+        */
+       identification_t *subject;
+
+       /**
+        * certificate issuer
+        */
+       identification_t *issuer;
+
+       /**
+        * Certificates public key
+        */
+       public_key_t *pubkey;
+
+       /**
+        * subjectKeyIdentifier as read from cert
+        */
+       chunk_t subjectKeyIdentifier;
+
+       /**
+        * authorityKeyIdentifier as read from cert
+        */
+       chunk_t authKeyIdentifier;
+
+       /**
+        * Start time of certificate validity
+        */
+       time_t notBefore;
+
+       /**
+        * End time of certificate validity
+        */
+       time_t notAfter;
+
+       /**
+        * Signature scheme of the certificate
+        */
+       signature_scheme_t scheme;
+
+       /**
+        * subjectAltNames
+        */
+       linked_list_t *subjectAltNames;
+
+       /**
+        * issuerAltNames
+        */
+       linked_list_t *issuerAltNames;
+
+       /**
+        * List of CRL URIs
+        */
+       linked_list_t *crl_uris;
+
+       /**
+        * List of OCSP URIs
+        */
+       linked_list_t *ocsp_uris;
+
+       /**
+        * References to this cert
+        */
+       refcount_t ref;
+};
+
+/**
+ * Convert a GeneralName to an identification_t.
+ */
+static identification_t *general_name2id(GENERAL_NAME *name)
+{
+       if (!name)
+       {
+               return NULL;
+       }
+       switch (name->type)
+       {
+               case GEN_EMAIL:
+                       return identification_create_from_encoding(ID_RFC822_ADDR,
+                                       openssl_asn1_str2chunk(name->d.rfc822Name));
+               case GEN_DNS:
+                       return identification_create_from_encoding(ID_FQDN,
+                                       openssl_asn1_str2chunk(name->d.dNSName));
+               case GEN_URI:
+                       return identification_create_from_encoding(ID_DER_ASN1_GN_URI,
+                                       openssl_asn1_str2chunk(name->d.uniformResourceIdentifier));
+               case GEN_IPADD:
+                       return identification_create_from_encoding(ID_IPV4_ADDR,
+                                       openssl_asn1_str2chunk(name->d.iPAddress));
+               case GEN_DIRNAME :
+                       return openssl_x509_name2id(name->d.directoryName);
+               default:
+                       return NULL;
+       }
+}
+
+METHOD(x509_t, get_flags, x509_flag_t,
+       private_openssl_x509_t *this)
+{
+       return this->flags;
+}
+
+METHOD(x509_t, get_serial, chunk_t,
+       private_openssl_x509_t *this)
+{
+       return openssl_asn1_str2chunk(X509_get_serialNumber(this->x509));
+}
+
+METHOD(x509_t, get_subjectKeyIdentifier, chunk_t,
+       private_openssl_x509_t *this)
+{
+       chunk_t fingerprint;
+
+       if (this->subjectKeyIdentifier.len)
+       {
+               return this->subjectKeyIdentifier;
+       }
+       if (this->pubkey->get_fingerprint(this->pubkey, KEY_ID_PUBKEY_SHA1,
+                                                                         &fingerprint))
+       {
+               return fingerprint;
+       }
+       return chunk_empty;
+}
+
+METHOD(x509_t, get_authKeyIdentifier, chunk_t,
+       private_openssl_x509_t *this)
+{
+       if (this->authKeyIdentifier.len)
+       {
+               return this->authKeyIdentifier;
+       }
+       return chunk_empty;
+}
+
+METHOD(x509_t, get_pathLenConstraint, int,
+       private_openssl_x509_t *this)
+{
+       return this->pathlen;
+}
+
+METHOD(x509_t, create_subjectAltName_enumerator, enumerator_t*,
+       private_openssl_x509_t *this)
+{
+       return this->subjectAltNames->create_enumerator(this->subjectAltNames);
+}
+
+METHOD(x509_t, create_crl_uri_enumerator, enumerator_t*,
+       private_openssl_x509_t *this)
+{
+       return this->crl_uris->create_enumerator(this->crl_uris);
+}
+
+METHOD(x509_t, create_ocsp_uri_enumerator, enumerator_t*,
+       private_openssl_x509_t *this)
+{
+       return this->ocsp_uris->create_enumerator(this->ocsp_uris);
+}
+
+METHOD(x509_t, create_ipAddrBlock_enumerator, enumerator_t*,
+       private_openssl_x509_t *this)
+{
+       /* TODO */
+       return enumerator_create_empty();
+}
+
+METHOD(certificate_t, get_type, certificate_type_t,
+       private_openssl_x509_t *this)
+{
+       return CERT_X509;
+}
+
+METHOD(certificate_t, get_subject, identification_t*,
+       private_openssl_x509_t *this)
+{
+       return this->subject;
+}
+
+METHOD(certificate_t, get_issuer, identification_t*,
+       private_openssl_x509_t *this)
+{
+       return this->issuer;
+}
+
+METHOD(certificate_t, has_subject, id_match_t,
+       private_openssl_x509_t *this, identification_t *subject)
+{
+       identification_t *current;
+       enumerator_t *enumerator;
+       id_match_t match, best;
+
+       if (subject->get_type(subject) == ID_KEY_ID)
+       {
+               if (chunk_equals(this->hash, subject->get_encoding(subject)))
+               {
+                       return ID_MATCH_PERFECT;
+               }
+       }
+       best = this->subject->matches(this->subject, subject);
+       enumerator = create_subjectAltName_enumerator(this);
+       while (enumerator->enumerate(enumerator, &current))
+       {
+               match = current->matches(current, subject);
+               if (match > best)
+               {
+                       best = match;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return best;
+}
+
+METHOD(certificate_t, has_issuer, id_match_t,
+       private_openssl_x509_t *this, identification_t *issuer)
+{
+       /* issuerAltNames currently not supported */
+       return this->issuer->matches(this->issuer, issuer);
+}
+
+METHOD(certificate_t, issued_by, bool,
+       private_openssl_x509_t *this, certificate_t *issuer)
+{
+       public_key_t *key;
+       bool valid;
+       x509_t *x509 = (x509_t*)issuer;
+       chunk_t tbs;
+
+       if (&this->public.x509.interface == issuer)
+       {
+               if (this->flags & X509_SELF_SIGNED)
+               {
+                       return TRUE;
+               }
+       }
+       else
+       {
+               if (issuer->get_type(issuer) != CERT_X509)
+               {
+                       return FALSE;
+               }
+               if (!(x509->get_flags(x509) & X509_CA))
+               {
+                       return FALSE;
+               }
+               if (!this->issuer->equals(this->issuer, issuer->get_subject(issuer)))
+               {
+                       return FALSE;
+               }
+       }
+       if (this->scheme == SIGN_UNKNOWN)
+       {
+               return FALSE;
+       }
+       key = issuer->get_public_key(issuer);
+       if (!key)
+       {
+               return FALSE;
+       }
+       tbs = openssl_i2chunk(X509_CINF, this->x509->cert_info);
+       valid = key->verify(key, this->scheme, tbs,
+                                               openssl_asn1_str2chunk(this->x509->signature));
+       free(tbs.ptr);
+       key->destroy(key);
+       return valid;
+}
+
+METHOD(certificate_t, get_public_key, public_key_t*,
+       private_openssl_x509_t *this)
+{
+       return this->pubkey->get_ref(this->pubkey);
+}
+
+METHOD(certificate_t, get_validity, bool,
+       private_openssl_x509_t *this,
+       time_t *when, time_t *not_before, time_t *not_after)
+{
+       time_t t;
+
+       if (when)
+       {
+               t = *when;
+       }
+       else
+       {
+               t = time(NULL);
+       }
+       if (not_before)
+       {
+               *not_before = this->notBefore;
+       }
+       if (not_after)
+       {
+               *not_after = this->notAfter;
+       }
+       return (t >= this->notBefore && t <= this->notAfter);
+}
+
+METHOD(certificate_t, is_newer, bool,
+       private_openssl_x509_t *this, certificate_t *other)
+{
+       time_t this_update, that_update, now = time(NULL);
+       bool new;
+
+       get_validity(this, &now, &this_update, NULL);
+       other->get_validity(other, &now, &that_update, NULL);
+       new = this_update > that_update;
+       DBG1(DBG_LIB, "  certificate from %T is %s - existing certificate "
+                "from %T %s", &this_update, FALSE, new ? "newer":"not newer",
+                &that_update, FALSE, new ? "replaced":"retained");
+       return new;
+}
+
+METHOD(certificate_t, get_encoding, chunk_t,
+       private_openssl_x509_t *this)
+{
+       return chunk_clone(this->encoding);
+}
+
+METHOD(certificate_t, equals, bool,
+       private_openssl_x509_t *this, certificate_t *other)
+{
+       chunk_t encoding;
+       bool equal;
+
+       if (this == (private_openssl_x509_t*)other)
+       {
+               return TRUE;
+       }
+       if (other->get_type(other) != CERT_X509)
+       {
+               return FALSE;
+       }
+       if (other->equals == (void*)equals)
+       {       /* skip allocation if we have the same implementation */
+               encoding = ((private_openssl_x509_t*)other)->encoding;
+               return chunk_equals(this->encoding, encoding);
+       }
+       encoding = other->get_encoding(other);
+       equal = chunk_equals(this->encoding, encoding);
+       free(encoding.ptr);
+       return equal;
+}
+
+METHOD(certificate_t, get_ref, certificate_t*,
+       private_openssl_x509_t *this)
+{
+       ref_get(&this->ref);
+       return &this->public.x509.interface;
+}
+
+METHOD(certificate_t, destroy, void,
+       private_openssl_x509_t *this)
+{
+       if (ref_put(&this->ref))
+       {
+               if (this->x509)
+               {
+                       X509_free(this->x509);
+               }
+               DESTROY_IF(this->subject);
+               DESTROY_IF(this->issuer);
+               DESTROY_IF(this->pubkey);
+               free(this->subjectKeyIdentifier.ptr);
+               free(this->authKeyIdentifier.ptr);
+               free(this->encoding.ptr);
+               free(this->hash.ptr);
+               this->subjectAltNames->destroy_offset(this->subjectAltNames,
+                                                                               offsetof(identification_t, destroy));
+               this->issuerAltNames->destroy_offset(this->issuerAltNames,
+                                                                               offsetof(identification_t, destroy));
+               this->crl_uris->destroy_function(this->crl_uris, free);
+               this->ocsp_uris->destroy_function(this->ocsp_uris, free);
+               free(this);
+       }
+}
+
+/**
+ * Create an empty certificate
+ */
+static private_openssl_x509_t *create_empty()
+{
+       private_openssl_x509_t *this;
+
+       INIT(this,
+               .public = {
+                       .x509 = {
+                               .interface = {
+                                       .get_type = _get_type,
+                                       .get_subject = _get_subject,
+                                       .get_issuer = _get_issuer,
+                                       .has_subject = _has_subject,
+                                       .has_issuer = _has_issuer,
+                                       .issued_by = _issued_by,
+                                       .get_public_key = _get_public_key,
+                                       .get_validity = _get_validity,
+                                       .is_newer = _is_newer,
+                                       .get_encoding = _get_encoding,
+                                       .equals = _equals,
+                                       .get_ref = _get_ref,
+                                       .destroy = _destroy,
+                               },
+                               .get_flags = _get_flags,
+                               .get_serial = _get_serial,
+                               .get_subjectKeyIdentifier = _get_subjectKeyIdentifier,
+                               .get_authKeyIdentifier = _get_authKeyIdentifier,
+                               .get_pathLenConstraint = _get_pathLenConstraint,
+                               .create_subjectAltName_enumerator = _create_subjectAltName_enumerator,
+                               .create_crl_uri_enumerator = _create_crl_uri_enumerator,
+                               .create_ocsp_uri_enumerator = _create_ocsp_uri_enumerator,
+                               .create_ipAddrBlock_enumerator = _create_ipAddrBlock_enumerator,
+                       },
+               },
+               .subjectAltNames = linked_list_create(),
+               .issuerAltNames = linked_list_create(),
+               .crl_uris = linked_list_create(),
+               .ocsp_uris = linked_list_create(),
+               .pathlen = X509_NO_PATH_LEN_CONSTRAINT,
+               .ref = 1,
+       );
+
+       return this;
+}
+
+/**
+ * parse an extionsion containing GENERAL_NAMES into a list
+ */
+static bool parse_generalNames_ext(linked_list_t *list,
+                                                                  X509_EXTENSION *ext)
+{
+       GENERAL_NAMES *names;
+       GENERAL_NAME *name;
+       identification_t *id;
+       int i, num;
+
+       names = X509V3_EXT_d2i(ext);
+       if (!names)
+       {
+               return FALSE;
+       }
+
+       num = sk_GENERAL_NAME_num(names);
+       for (i = 0; i < num; i++)
+       {
+               name = sk_GENERAL_NAME_value(names, i);
+               id = general_name2id(name);
+               if (id)
+               {
+                       list->insert_last(list, id);
+               }
+               GENERAL_NAME_free(name);
+       }
+       sk_GENERAL_NAME_free(names);
+       return TRUE;
+}
+
+/**
+ * parse basic constraints
+ */
+static bool parse_basicConstraints_ext(private_openssl_x509_t *this,
+                                                                          X509_EXTENSION *ext)
+{
+       BASIC_CONSTRAINTS *constraints;
+
+       constraints = (BASIC_CONSTRAINTS*)X509V3_EXT_d2i(ext);
+       if (constraints)
+       {
+               if (constraints->ca)
+               {
+                       this->flags |= X509_CA;
+               }
+               if (constraints->pathlen)
+               {
+                       this->pathlen = ASN1_INTEGER_get(constraints->pathlen);
+               }
+               BASIC_CONSTRAINTS_free(constraints);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * Parse CRL distribution points
+ */
+static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this,
+                                                                                       X509_EXTENSION *ext)
+{
+       CRL_DIST_POINTS *cdps;
+       DIST_POINT *cdp;
+       identification_t *id;
+       char *uri;
+       int i, j, point_num, name_num;
+
+       cdps = X509V3_EXT_d2i(ext);
+       if (!cdps)
+       {
+               return FALSE;
+       }
+       point_num = sk_DIST_POINT_num(cdps);
+       for (i = 0; i < point_num; i++)
+       {
+               cdp = sk_DIST_POINT_value(cdps, i);
+               if (cdp)
+               {
+                       if (cdp->distpoint && cdp->distpoint->type == 0 &&
+                               cdp->distpoint->name.fullname)
+                       {
+                               name_num = sk_GENERAL_NAME_num(cdp->distpoint->name.fullname);
+                               for (j = 0; j < name_num; j++)
+                               {
+                                       id = general_name2id(sk_GENERAL_NAME_value(
+                                                                                       cdp->distpoint->name.fullname, j));
+                                       if (id)
+                                       {
+                                               if (asprintf(&uri, "%Y", id) > 0)
+                                               {
+                                                       this->crl_uris->insert_first(this->crl_uris, uri);
+                                               }
+                                               id->destroy(id);
+                                       }
+                               }
+                       }
+                       DIST_POINT_free(cdp);
+               }
+       }
+       sk_DIST_POINT_free(cdps);
+       return TRUE;
+}
+
+/**
+ * Parse authorityInfoAccess with OCSP URIs
+ */
+static bool parse_authorityInfoAccess_ext(private_openssl_x509_t *this,
+                                                                                 X509_EXTENSION *ext)
+{
+       AUTHORITY_INFO_ACCESS *infos;
+       ACCESS_DESCRIPTION *desc;
+       identification_t *id;
+       int i, num;
+       char *uri;
+
+       infos = X509V3_EXT_d2i(ext);
+       if (!infos)
+       {
+               return FALSE;
+       }
+       num = sk_ACCESS_DESCRIPTION_num(infos);
+       for (i = 0; i < num; i++)
+       {
+               desc = sk_ACCESS_DESCRIPTION_value(infos, i);
+               if (desc)
+               {
+                       if (openssl_asn1_known_oid(desc->method) == OID_OCSP)
+                       {
+                               id = general_name2id(desc->location);
+                               if (id)
+                               {
+                                       if (asprintf(&uri, "%Y", id) > 0)
+                                       {
+                                               this->ocsp_uris->insert_first(this->ocsp_uris, uri);
+                                       }
+                                       id->destroy(id);
+                               }
+                       }
+                       ACCESS_DESCRIPTION_free(desc);
+               }
+       }
+       sk_ACCESS_DESCRIPTION_free(infos);
+       return TRUE;
+}
+
+/**
+ * Parse authorityKeyIdentifier extension
+ */
+static bool parse_authKeyIdentifier_ext(private_openssl_x509_t *this,
+                                                                               X509_EXTENSION *ext)
+{
+       AUTHORITY_KEYID *keyid;
+
+       keyid = (AUTHORITY_KEYID*)X509V3_EXT_d2i(ext);
+       if (keyid)
+       {
+               free(this->authKeyIdentifier.ptr);
+               this->authKeyIdentifier = chunk_clone(
+                                                                               openssl_asn1_str2chunk(keyid->keyid));
+               AUTHORITY_KEYID_free(keyid);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * Parse subjectKeyIdentifier extension
+ */
+static bool parse_subjectKeyIdentifier_ext(private_openssl_x509_t *this,
+                                                                                  X509_EXTENSION *ext)
+{
+       free(this->subjectKeyIdentifier.ptr);
+       this->subjectKeyIdentifier = chunk_clone(openssl_asn1_str2chunk(
+                                                                                               X509_EXTENSION_get_data(ext)));
+       return TRUE;
+}
+
+/**
+ * Parse X509 extensions we are interested in
+ */
+static bool parse_extensions(private_openssl_x509_t *this)
+{
+       STACK_OF(X509_EXTENSION) *extensions;
+       int i, num;
+
+       extensions = this->x509->cert_info->extensions;
+       if (extensions)
+       {
+               num = sk_X509_EXTENSION_num(extensions);
+
+               for (i = 0; i < num; i++)
+               {
+                       X509_EXTENSION *ext;
+                       bool ok;
+
+                       ext = sk_X509_EXTENSION_value(extensions, i);
+                       switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext)))
+                       {
+                               case NID_info_access:
+                                       ok = parse_authorityInfoAccess_ext(this, ext);
+                                       break;
+                               case NID_authority_key_identifier:
+                                       ok = parse_authKeyIdentifier_ext(this, ext);
+                                       break;
+                               case NID_subject_key_identifier:
+                                       ok = parse_subjectKeyIdentifier_ext(this, ext);
+                                       break;
+                               case NID_subject_alt_name:
+                                       ok = parse_generalNames_ext(this->subjectAltNames, ext);
+                                       break;
+                               case NID_issuer_alt_name:
+                                       ok = parse_generalNames_ext(this->issuerAltNames, ext);
+                                       break;
+                               case NID_basic_constraints:
+                                       ok = parse_basicConstraints_ext(this, ext);
+                                       break;
+                               case NID_crl_distribution_points:
+                                       ok = parse_crlDistributionPoints_ext(this, ext);
+                                       break;
+                               default:
+                                       ok = TRUE;
+                                       break;
+                       }
+                       if (!ok)
+                       {
+                               return FALSE;
+                       }
+               }
+       }
+       return TRUE;
+}
+
+/**
+ * Parse a DER encoded x509 certificate
+ */
+static bool parse_certificate(private_openssl_x509_t *this)
+{
+       const unsigned char *ptr = this->encoding.ptr;
+       hasher_t *hasher;
+       chunk_t chunk;
+
+       this->x509 = d2i_X509(NULL, &ptr, this->encoding.len);
+       if (!this->x509)
+       {
+               return FALSE;
+       }
+       this->subject = openssl_x509_name2id(X509_get_subject_name(this->x509));
+       this->issuer = openssl_x509_name2id(X509_get_issuer_name(this->x509));
+
+       switch (openssl_asn1_known_oid(this->x509->cert_info->key->algor->algorithm))
+       {
+               case OID_RSA_ENCRYPTION:
+                       this->pubkey = lib->creds->create(lib->creds,
+                                       CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_ASN1_DER,
+                                       openssl_asn1_str2chunk(X509_get0_pubkey_bitstr(this->x509)),
+                                       BUILD_END);
+                       break;
+               case OID_EC_PUBLICKEY:
+                       /* for ECDSA, we need the full subjectPublicKeyInfo, as it contains
+                        * the curve parameters. */
+                       chunk = openssl_i2chunk(X509_PUBKEY, X509_get_X509_PUBKEY(this->x509));
+                       this->pubkey = lib->creds->create(lib->creds,
+                                       CRED_PUBLIC_KEY, KEY_ECDSA, BUILD_BLOB_ASN1_DER,
+                                       chunk, BUILD_END);
+                       free(chunk.ptr);
+                       break;
+               default:
+                       DBG1(DBG_LIB, "unsupported public key algorithm");
+                       break;
+       }
+       if (!this->subject || !this->issuer || !this->pubkey)
+       {
+               return FALSE;
+       }
+
+       this->notBefore = openssl_asn1_to_time(X509_get_notBefore(this->x509));
+       this->notAfter = openssl_asn1_to_time(X509_get_notAfter(this->x509));
+
+       if (!chunk_equals(
+                       openssl_asn1_obj2chunk(this->x509->cert_info->signature->algorithm),
+                       openssl_asn1_obj2chunk(this->x509->sig_alg->algorithm)))
+       {
+               return FALSE;
+       }
+       this->scheme = signature_scheme_from_oid(openssl_asn1_known_oid(
+                                                                                               this->x509->sig_alg->algorithm));
+
+       if (!parse_extensions(this))
+       {
+               return TRUE;
+       }
+
+       hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+       if (!hasher)
+       {
+               return FALSE;
+       }
+       hasher->allocate_hash(hasher, this->encoding, &this->hash);
+       hasher->destroy(hasher);
+
+       if (issued_by(this, &this->public.x509.interface))
+       {
+               this->flags |= X509_SELF_SIGNED;
+       }
+       return TRUE;
+}
+
+openssl_x509_t *openssl_x509_load(certificate_type_t type, va_list args)
+{
+       chunk_t blob = chunk_empty;
+       x509_flag_t flags = 0;
+
+       while (TRUE)
+       {
+               switch (va_arg(args, builder_part_t))
+               {
+                       case BUILD_BLOB_ASN1_DER:
+                               blob = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_X509_FLAG:
+                               flags |= va_arg(args, x509_flag_t);
+                               continue;
+                       case BUILD_END:
+                               break;
+                       default:
+                               return NULL;
+               }
+               break;
+       }
+
+       if (blob.ptr)
+       {
+               private_openssl_x509_t *this;
+
+               this = create_empty();
+               this->encoding = chunk_clone(blob);
+               this->flags |= flags;
+               if (parse_certificate(this))
+               {
+                       return &this->public;
+               }
+               DBG1(DBG_LIB, "OpenSSL X.509 parsing failed");
+               destroy(this);
+       }
+       return NULL;
+}
diff --git a/src/libstrongswan/plugins/openssl/openssl_x509.h b/src/libstrongswan/plugins/openssl/openssl_x509.h
new file mode 100644 (file)
index 0000000..5255592
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup openssl_x509 openssl_x509
+ * @{ @ingroup openssl_p
+ */
+
+#ifndef OPENSSL_X509_H_
+#define OPENSSL_X509_H_
+
+#include <credentials/certificates/x509.h>
+
+typedef struct openssl_x509_t openssl_x509_t;
+
+/**
+ * X.509 certificate implementation using OpenSSL.
+ */
+struct openssl_x509_t {
+
+       /**
+        * Implements x509_t interface.
+        */
+       x509_t x509;
+};
+
+/**
+ * Load a X.509 certificate.
+ *
+ * This function takes a BUILD_BLOB_ASN1_DER.
+ *
+ * @param type         certificate type, CERT_X509 only
+ * @param args         builder_part_t argument list
+ * @return                     X.509 certificate, NULL on failure
+ */
+openssl_x509_t *openssl_x509_load(certificate_type_t type, va_list args);
+
+#endif /** OPENSSL_X509_H_ @}*/