aboutsummaryrefslogtreecommitdiffstats
path: root/src/libstrongswan/plugins/x509
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/x509')
-rw-r--r--src/libstrongswan/plugins/x509/Makefile.am13
-rw-r--r--src/libstrongswan/plugins/x509/x509_cert.c1273
-rw-r--r--src/libstrongswan/plugins/x509/x509_cert.h47
-rw-r--r--src/libstrongswan/plugins/x509/x509_crl.c717
-rw-r--r--src/libstrongswan/plugins/x509/x509_crl.h48
-rw-r--r--src/libstrongswan/plugins/x509/x509_ocsp_request.c603
-rw-r--r--src/libstrongswan/plugins/x509/x509_ocsp_request.h54
-rw-r--r--src/libstrongswan/plugins/x509/x509_ocsp_response.c928
-rw-r--r--src/libstrongswan/plugins/x509/x509_ocsp_response.h47
-rw-r--r--src/libstrongswan/plugins/x509/x509_plugin.c75
-rw-r--r--src/libstrongswan/plugins/x509/x509_plugin.h47
11 files changed, 3852 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/x509/Makefile.am b/src/libstrongswan/plugins/x509/Makefile.am
new file mode 100644
index 000000000..12441b357
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/Makefile.am
@@ -0,0 +1,13 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libstrongswan-x509.la
+
+libstrongswan_x509_la_SOURCES = x509_plugin.h x509_plugin.c \
+ x509_cert.h x509_cert.c x509_crl.h x509_crl.c \
+ x509_ocsp_request.h x509_ocsp_request.c \
+ x509_ocsp_response.h x509_ocsp_response.c
+libstrongswan_x509_la_LDFLAGS = -module
+
diff --git a/src/libstrongswan/plugins/x509/x509_cert.c b/src/libstrongswan/plugins/x509/x509_cert.c
new file mode 100644
index 000000000..47a841c51
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_cert.c
@@ -0,0 +1,1273 @@
+/*
+ * Copyright (C) 2000 Andreas Hess, Patric Lichtsteiner, Roger Wegmann
+ * Copyright (C) 2001 Marco Bertossa, Andreas Schleiss
+ * Copyright (C) 2002 Mario Strasser
+ * Copyright (C) 2000-2006 Andreas Steffen
+ * Copyright (C) 2006-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#define _GNU_SOURCE
+
+#include "x509_cert.h"
+
+#include <gmp.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <crypto/hashers/hasher.h>
+#include <library.h>
+#include <debug.h>
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <asn1/pem.h>
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+
+/**
+ * Different kinds of generalNames
+ */
+typedef enum {
+ GN_OTHER_NAME = 0,
+ GN_RFC822_NAME = 1,
+ GN_DNS_NAME = 2,
+ GN_X400_ADDRESS = 3,
+ GN_DIRECTORY_NAME = 4,
+ GN_EDI_PARTY_NAME = 5,
+ GN_URI = 6,
+ GN_IP_ADDRESS = 7,
+ GN_REGISTERED_ID = 8,
+} generalNames_t;
+
+
+typedef struct private_x509_cert_t private_x509_cert_t;
+
+/**
+ * Private data of a x509_cert_t object.
+ */
+struct private_x509_cert_t {
+ /**
+ * Public interface for this certificate.
+ */
+ x509_cert_t public;
+
+ /**
+ * DER encoded X.509 certificate
+ */
+ chunk_t certificate;
+
+ /**
+ * X.509 certificate body over which signature is computed
+ */
+ chunk_t tbsCertificate;
+
+ /**
+ * Version of the X.509 certificate
+ */
+ u_int version;
+
+ /**
+ * Serial number of the X.509 certificate
+ */
+ chunk_t serialNumber;
+
+ /**
+ * ID representing the certificate issuer
+ */
+ identification_t *issuer;
+
+ /**
+ * Start time of certificate validity
+ */
+ time_t notBefore;
+
+ /**
+ * End time of certificate validity
+ */
+ time_t notAfter;
+
+ /**
+ * ID representing the certificate subject
+ */
+ identification_t *subject;
+
+ /**
+ * List of subjectAltNames as identification_t
+ */
+ linked_list_t *subjectAltNames;
+
+ /**
+ * List of crlDistributionPoints as allocated char*
+ */
+ linked_list_t *crl_uris;
+
+ /**
+ * List ocspAccessLocations as identification_t
+ */
+ linked_list_t *ocsp_uris;
+
+ /**
+ * certificates embedded public key
+ */
+ public_key_t *public_key;
+
+ /**
+ * Subject Key Identifier
+ */
+ chunk_t subjectKeyID;
+
+ /**
+ * Authority Key Identifier
+ */
+ identification_t *authKeyIdentifier;
+
+ /**
+ * Authority Key Serial Number
+ */
+ chunk_t authKeySerialNumber;
+
+ /**
+ * x509 constraints and other flags
+ */
+ x509_flag_t flags;
+
+ /**
+ * Signature algorithm
+ */
+ int algorithm;
+
+ /**
+ * Signature
+ */
+ chunk_t signature;
+
+ /**
+ * reference count
+ */
+ refcount_t ref;
+};
+
+/**
+ * ASN.1 definition of generalName
+ */
+static const asn1Object_t generalNameObjects[] = {
+ { 0, "otherName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_BODY }, /* 0 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 1 */
+ { 0, "rfc822Name", ASN1_CONTEXT_S_1, ASN1_OPT|ASN1_BODY }, /* 2 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 3 */
+ { 0, "dnsName", ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 4 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 5 */
+ { 0, "x400Address", ASN1_CONTEXT_S_3, ASN1_OPT|ASN1_BODY }, /* 6 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 7 */
+ { 0, "directoryName", ASN1_CONTEXT_C_4, ASN1_OPT|ASN1_BODY }, /* 8 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 9 */
+ { 0, "ediPartyName", ASN1_CONTEXT_C_5, ASN1_OPT|ASN1_BODY }, /* 10 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 11 */
+ { 0, "URI", ASN1_CONTEXT_S_6, ASN1_OPT|ASN1_BODY }, /* 12 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 13 */
+ { 0, "ipAddress", ASN1_CONTEXT_S_7, ASN1_OPT|ASN1_BODY }, /* 14 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 15 */
+ { 0, "registeredID", ASN1_CONTEXT_S_8, ASN1_OPT|ASN1_BODY }, /* 16 */
+ { 0, "end choice", ASN1_EOC, ASN1_END } /* 17 */
+};
+
+#define GN_OBJ_OTHER_NAME 0
+#define GN_OBJ_RFC822_NAME 2
+#define GN_OBJ_DNS_NAME 4
+#define GN_OBJ_X400_ADDRESS 6
+#define GN_OBJ_DIRECTORY_NAME 8
+#define GN_OBJ_EDI_PARTY_NAME 10
+#define GN_OBJ_URI 12
+#define GN_OBJ_IP_ADDRESS 14
+#define GN_OBJ_REGISTERED_ID 16
+#define GN_OBJ_ROOF 18
+
+/**
+ * ASN.1 definition of otherName
+ */
+static const asn1Object_t otherNameObjects[] = {
+ {0, "type-id", ASN1_OID, ASN1_BODY }, /* 0 */
+ {0, "value", ASN1_CONTEXT_C_0, ASN1_BODY } /* 1 */
+};
+
+#define ON_OBJ_ID_TYPE 0
+#define ON_OBJ_VALUE 1
+#define ON_OBJ_ROOF 2
+
+/**
+ * ASN.1 definition of a basicConstraints extension
+ */
+static const asn1Object_t basicConstraintsObjects[] = {
+ { 0, "basicConstraints", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "CA", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 1 */
+ { 1, "pathLenConstraint", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 2 */
+ { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */
+};
+
+#define BASIC_CONSTRAINTS_CA 1
+#define BASIC_CONSTRAINTS_ROOF 4
+
+/**
+ * ASN.1 definition of a keyIdentifier
+ */
+static const asn1Object_t keyIdentifierObjects[] = {
+ { 0, "keyIdentifier", ASN1_OCTET_STRING, ASN1_BODY } /* 0 */
+};
+
+/**
+ * ASN.1 definition of a authorityKeyIdentifier extension
+ */
+static const asn1Object_t authorityKeyIdentifierObjects[] = {
+ { 0, "authorityKeyIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "keyIdentifier", ASN1_CONTEXT_S_0, ASN1_OPT|ASN1_OBJ }, /* 1 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */
+ { 1, "authorityCertIssuer", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_OBJ }, /* 3 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */
+ { 1, "authorityCertSerialNumber",ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 5 */
+ { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */
+};
+
+#define AUTH_KEY_ID_KEY_ID 1
+#define AUTH_KEY_ID_CERT_ISSUER 3
+#define AUTH_KEY_ID_CERT_SERIAL 5
+#define AUTH_KEY_ID_ROOF 7
+
+/**
+ * ASN.1 definition of a authorityInfoAccess extension
+ */
+static const asn1Object_t authorityInfoAccessObjects[] = {
+ { 0, "authorityInfoAccess", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */
+ { 1, "accessDescription", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "accessMethod", ASN1_OID, ASN1_BODY }, /* 2 */
+ { 2, "accessLocation", ASN1_EOC, ASN1_RAW }, /* 3 */
+ { 0, "end loop", ASN1_EOC, ASN1_END } /* 4 */
+};
+
+#define AUTH_INFO_ACCESS_METHOD 2
+#define AUTH_INFO_ACCESS_LOCATION 3
+#define AUTH_INFO_ACCESS_ROOF 5
+
+/**
+ * ASN.1 definition of a extendedKeyUsage extension
+ */
+static const asn1Object_t extendedKeyUsageObjects[] = {
+ { 0, "extendedKeyUsage", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */
+ { 1, "keyPurposeID", ASN1_OID, ASN1_BODY }, /* 1 */
+ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */
+};
+
+#define EXT_KEY_USAGE_PURPOSE_ID 1
+#define EXT_KEY_USAGE_ROOF 3
+
+/**
+ * ASN.1 definition of generalNames
+ */
+static const asn1Object_t generalNamesObjects[] = {
+ { 0, "generalNames", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */
+ { 1, "generalName", ASN1_EOC, ASN1_RAW }, /* 1 */
+ { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */
+};
+
+#define GENERAL_NAMES_GN 1
+#define GENERAL_NAMES_ROOF 3
+
+
+/**
+ * ASN.1 definition of crlDistributionPoints
+ */
+static const asn1Object_t crlDistributionPointsObjects[] = {
+ { 0, "crlDistributionPoints", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */
+ { 1, "DistributionPoint", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "distributionPoint", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_LOOP }, /* 2 */
+ { 3, "fullName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_OBJ }, /* 3 */
+ { 3, "end choice", ASN1_EOC, ASN1_END }, /* 4 */
+ { 3, "nameRelToCRLIssuer",ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 5 */
+ { 3, "end choice", ASN1_EOC, ASN1_END }, /* 6 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 7 */
+ { 2, "reasons", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 8 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 9 */
+ { 2, "crlIssuer", ASN1_CONTEXT_C_2, ASN1_OPT|ASN1_BODY }, /* 10 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 11 */
+ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 12 */
+};
+
+#define CRL_DIST_POINTS_FULLNAME 3
+#define CRL_DIST_POINTS_ROOF 13
+
+/**
+ * ASN.1 definition of an X.509v3 x509_cert
+ */
+static const asn1Object_t certObjects[] = {
+ { 0, "x509", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */
+ { 1, "tbsCertificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */
+ { 2, "DEFAULT v1", ASN1_CONTEXT_C_0, ASN1_DEF }, /* 2 */
+ { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */
+ { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 4 */
+ { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 5 */
+ { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */
+ { 2, "validity", ASN1_SEQUENCE, ASN1_NONE }, /* 7 */
+ { 3, "notBefore", ASN1_EOC, ASN1_RAW }, /* 8 */
+ { 3, "notAfter", ASN1_EOC, ASN1_RAW }, /* 9 */
+ { 2, "subject", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */
+ { 2, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_NONE }, /* 11 */
+ { 3, "algorithm", ASN1_EOC, ASN1_RAW }, /* 12 */
+ { 3, "subjectPublicKey", ASN1_BIT_STRING, ASN1_NONE }, /* 13 */
+ { 4, "RSAPublicKey", ASN1_SEQUENCE, ASN1_RAW }, /* 14 */
+ { 2, "issuerUniqueID", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 15 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 16 */
+ { 2, "subjectUniqueID", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 17 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 18 */
+ { 2, "optional extensions", ASN1_CONTEXT_C_3, ASN1_OPT }, /* 19 */
+ { 3, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */
+ { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */
+ { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */
+ { 5, "critical", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 23 */
+ { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */
+ { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */
+ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */
+ { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */
+};
+
+#define X509_OBJ_TBS_CERTIFICATE 1
+#define X509_OBJ_VERSION 3
+#define X509_OBJ_SERIAL_NUMBER 4
+#define X509_OBJ_SIG_ALG 5
+#define X509_OBJ_ISSUER 6
+#define X509_OBJ_NOT_BEFORE 8
+#define X509_OBJ_NOT_AFTER 9
+#define X509_OBJ_SUBJECT 10
+#define X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM 12
+#define X509_OBJ_SUBJECT_PUBLIC_KEY 13
+#define X509_OBJ_RSA_PUBLIC_KEY 14
+#define X509_OBJ_EXTN_ID 22
+#define X509_OBJ_CRITICAL 23
+#define X509_OBJ_EXTN_VALUE 24
+#define X509_OBJ_ALGORITHM 27
+#define X509_OBJ_SIGNATURE 28
+#define X509_OBJ_ROOF 29
+
+
+static u_char ASN1_sAN_oid_buf[] = {
+ 0x06, 0x03, 0x55, 0x1D, 0x11
+};
+static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_sAN_oid_buf);
+
+/**
+ * extracts the basicConstraints extension
+ */
+static bool parse_basicConstraints(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ bool isCA = FALSE;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < BASIC_CONSTRAINTS_ROOF) {
+
+ if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
+ {
+ break;
+ }
+ if (objectID == BASIC_CONSTRAINTS_CA)
+ {
+ isCA = object.len && *object.ptr;
+ DBG2(" %s", isCA ? "TRUE" : "FALSE");
+ }
+ objectID++;
+ }
+ return isCA;
+}
+
+/*
+ * extracts an otherName
+ */
+static bool parse_otherName(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ int oid = OID_UNKNOWN;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < ON_OBJ_ROOF)
+ {
+ if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ switch (objectID)
+ {
+ case ON_OBJ_ID_TYPE:
+ oid = known_oid(object);
+ break;
+ case ON_OBJ_VALUE:
+ if (oid == OID_XMPP_ADDR)
+ {
+ if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING,
+ level + 1, "xmppAddr"))
+ {
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+/*
+ * extracts a generalName
+ */
+static identification_t *parse_generalName(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ int objectID = 0;
+ u_int level;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < GN_OBJ_ROOF)
+ {
+ id_type_t id_type = ID_ANY;
+
+ if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx))
+ {
+ return NULL;
+ }
+ switch (objectID)
+ {
+ case GN_OBJ_RFC822_NAME:
+ id_type = ID_RFC822_ADDR;
+ break;
+ case GN_OBJ_DNS_NAME:
+ id_type = ID_FQDN;
+ break;
+ case GN_OBJ_URI:
+ id_type = ID_DER_ASN1_GN_URI;
+ break;
+ case GN_OBJ_DIRECTORY_NAME:
+ id_type = ID_DER_ASN1_DN;
+ break;
+ case GN_OBJ_IP_ADDRESS:
+ id_type = ID_IPV4_ADDR;
+ break;
+ case GN_OBJ_OTHER_NAME:
+ if (!parse_otherName(object, level + 1))
+ return NULL;
+ break;
+ case GN_OBJ_X400_ADDRESS:
+ case GN_OBJ_EDI_PARTY_NAME:
+ case GN_OBJ_REGISTERED_ID:
+ break;
+ default:
+ break;
+ }
+ if (id_type != ID_ANY)
+ {
+ identification_t *gn = identification_create_from_encoding(id_type, object);
+ DBG2(" '%D'", gn);
+ return gn;
+ }
+ objectID++;
+ }
+ return NULL;
+}
+
+
+/**
+ * extracts one or several GNs and puts them into a chained list
+ */
+void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, level0, implicit, FALSE);
+ while (objectID < GENERAL_NAMES_ROOF)
+ {
+ if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx))
+ {
+ return;
+ }
+ if (objectID == GENERAL_NAMES_GN)
+ {
+ identification_t *gn = parse_generalName(object, level+1);
+
+ if (gn != NULL)
+ {
+ list->insert_last(list, (void *)gn);
+ }
+ }
+ objectID++;
+ }
+ return;
+}
+
+/**
+ * extracts a keyIdentifier
+ */
+static chunk_t parse_keyIdentifier(chunk_t blob, int level0, bool implicit)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, level0, implicit, FALSE);
+ if (!extract_object(keyIdentifierObjects, &objectID, &object, &level, &ctx))
+ {
+ return chunk_empty;
+ }
+ return object;
+}
+
+/**
+ * extracts an authoritykeyIdentifier
+ */
+identification_t* x509_parse_authorityKeyIdentifier(chunk_t blob, int level0,
+ chunk_t *authKeySerialNumber)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ identification_t *authKeyIdentifier = NULL;
+
+ *authKeySerialNumber = chunk_empty;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < AUTH_KEY_ID_ROOF)
+ {
+ if (!extract_object(authorityKeyIdentifierObjects, &objectID, &object, &level, &ctx))
+ {
+ return NULL;
+ }
+ switch (objectID)
+ {
+ case AUTH_KEY_ID_KEY_ID:
+ {
+ chunk_t authKeyID = parse_keyIdentifier(object, level+1, TRUE);
+
+ if (authKeyID.ptr == NULL)
+ {
+ return NULL;
+ }
+ authKeyIdentifier = identification_create_from_encoding(
+ ID_PUBKEY_SHA1, authKeyID);
+ break;
+ }
+ case AUTH_KEY_ID_CERT_ISSUER:
+ {
+ /* TODO: parse_generalNames(object, level+1, TRUE); */
+ break;
+ }
+ case AUTH_KEY_ID_CERT_SERIAL:
+ *authKeySerialNumber = object;
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return authKeyIdentifier;
+}
+
+/**
+ * extracts an authorityInfoAcess location
+ */
+static void parse_authorityInfoAccess(chunk_t blob, int level0,
+ private_x509_cert_t *this)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ int accessMethod = OID_UNKNOWN;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < AUTH_INFO_ACCESS_ROOF)
+ {
+ if (!extract_object(authorityInfoAccessObjects, &objectID, &object, &level, &ctx))
+ {
+ return;
+ }
+ switch (objectID)
+ {
+ case AUTH_INFO_ACCESS_METHOD:
+ accessMethod = known_oid(object);
+ break;
+ case AUTH_INFO_ACCESS_LOCATION:
+ {
+ switch (accessMethod)
+ {
+ case OID_OCSP:
+ case OID_CA_ISSUERS:
+ {
+ identification_t *id;
+ char *uri;
+
+ id = parse_generalName(object, level+1);
+ if (id == NULL)
+ { /* parsing went wrong - abort */
+ return;
+ }
+ DBG2(" '%D'", id);
+ if (accessMethod == OID_OCSP &&
+ asprintf(&uri, "%D", id) > 0)
+ {
+ this->ocsp_uris->insert_last(this->ocsp_uris, uri);
+ }
+ id->destroy(id);
+ }
+ break;
+ default:
+ /* unkown accessMethod, ignoring */
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ objectID++;
+ }
+}
+
+/**
+ * extracts extendedKeyUsage OIDs
+ */
+static bool parse_extendedKeyUsage(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < EXT_KEY_USAGE_ROOF)
+ {
+ if (!extract_object(extendedKeyUsageObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ if (objectID == EXT_KEY_USAGE_PURPOSE_ID &&
+ known_oid(object) == OID_OCSP_SIGNING)
+ {
+ return TRUE;
+ }
+ objectID++;
+ }
+ return FALSE;
+}
+
+/**
+ * extracts one or several crlDistributionPoints into a list
+ */
+static void parse_crlDistributionPoints(chunk_t blob, int level0,
+ private_x509_cert_t *this)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ linked_list_t *list;
+ identification_t *id;
+ char *uri;
+
+ list = linked_list_create();
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < CRL_DIST_POINTS_ROOF)
+ {
+ if (!extract_object(crlDistributionPointsObjects, &objectID, &object, &level, &ctx))
+ {
+ list->destroy_offset(list, offsetof(identification_t, destroy));
+ return;
+ }
+ if (objectID == CRL_DIST_POINTS_FULLNAME)
+ { /* append extracted generalNames to existing chained list */
+ parse_generalNames(object, level+1, TRUE, list);
+
+ while (list->remove_last(list, (void**)&id) == SUCCESS)
+ {
+ if (asprintf(&uri, "%D", id) > 0)
+ {
+ this->crl_uris->insert_last(this->crl_uris, uri);
+ }
+ id->destroy(id);
+ }
+ }
+ objectID++;
+ }
+ list->destroy(list);
+}
+
+/**
+ * Parses an X.509v3 certificate
+ */
+static bool parse_certificate(private_x509_cert_t *this)
+{
+ asn1_ctx_t ctx;
+ bool critical;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ int extn_oid = OID_UNKNOWN;
+ int key_alg = 0;
+ int sig_alg = 0;
+ chunk_t subjectPublicKey = chunk_empty;
+
+ asn1_init(&ctx, this->certificate, 0, FALSE, FALSE);
+ while (objectID < X509_OBJ_ROOF)
+ {
+ if (!extract_object(certObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ /* those objects which will parsed further need the next higher level */
+ level++;
+ switch (objectID)
+ {
+ case X509_OBJ_TBS_CERTIFICATE:
+ this->tbsCertificate = object;
+ break;
+ case X509_OBJ_VERSION:
+ this->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
+ DBG2(" v%d", this->version);
+ break;
+ case X509_OBJ_SERIAL_NUMBER:
+ this->serialNumber = object;
+ break;
+ case X509_OBJ_SIG_ALG:
+ sig_alg = parse_algorithmIdentifier(object, level, NULL);
+ break;
+ case X509_OBJ_ISSUER:
+ this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+ DBG2(" '%D'", this->issuer);
+ break;
+ case X509_OBJ_NOT_BEFORE:
+ this->notBefore = parse_time(object, level);
+ break;
+ case X509_OBJ_NOT_AFTER:
+ this->notAfter = parse_time(object, level);
+ break;
+ case X509_OBJ_SUBJECT:
+ this->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+ DBG2(" '%D'", this->subject);
+ break;
+ case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM:
+ key_alg = parse_algorithmIdentifier(object, level, NULL);
+ break;
+ case X509_OBJ_SUBJECT_PUBLIC_KEY:
+ if (ctx.blobs[4].len > 0 && *ctx.blobs[4].ptr == 0x00)
+ {
+ /* skip initial bit string octet defining 0 unused bits */
+ ctx.blobs[4].ptr++; ctx.blobs[4].len--;
+ }
+ break;
+ case X509_OBJ_RSA_PUBLIC_KEY:
+ subjectPublicKey = object;
+ switch (key_alg)
+ {
+ case OID_RSA_ENCRYPTION:
+ this->public_key = lib->creds->create(lib->creds,
+ CRED_PUBLIC_KEY, KEY_RSA,
+ BUILD_BLOB_ASN1_DER, chunk_clone(subjectPublicKey),
+ BUILD_END);
+ break;
+ default:
+ DBG1("parsing key type %d failed", key_alg);
+ return FALSE;
+ }
+ break;
+ case X509_OBJ_EXTN_ID:
+ extn_oid = known_oid(object);
+ break;
+ case X509_OBJ_CRITICAL:
+ critical = object.len && *object.ptr;
+ DBG2(" %s", critical ? "TRUE" : "FALSE");
+ break;
+ case X509_OBJ_EXTN_VALUE:
+ {
+ switch (extn_oid)
+ {
+ case OID_SUBJECT_KEY_ID:
+ this->subjectKeyID = parse_keyIdentifier(object, level, FALSE);
+ break;
+ case OID_SUBJECT_ALT_NAME:
+ parse_generalNames(object, level, FALSE, this->subjectAltNames);
+ break;
+ case OID_BASIC_CONSTRAINTS:
+ if (parse_basicConstraints(object, level))
+ {
+ this->flags |= X509_CA;
+ }
+ break;
+ case OID_CRL_DISTRIBUTION_POINTS:
+ parse_crlDistributionPoints(object, level, this);
+ break;
+ case OID_AUTHORITY_KEY_ID:
+ this->authKeyIdentifier = x509_parse_authorityKeyIdentifier(object,
+ level, &this->authKeySerialNumber);
+ break;
+ case OID_AUTHORITY_INFO_ACCESS:
+ parse_authorityInfoAccess(object, level, this);
+ break;
+ case OID_EXTENDED_KEY_USAGE:
+ if (parse_extendedKeyUsage(object, level))
+ {
+ this->flags |= X509_OCSP_SIGNER;
+ }
+ break;
+ case OID_NS_REVOCATION_URL:
+ case OID_NS_CA_REVOCATION_URL:
+ case OID_NS_CA_POLICY_URL:
+ case OID_NS_COMMENT:
+ if (!parse_asn1_simple_object(&object, ASN1_IA5STRING,
+ level, oid_names[extn_oid].name))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case X509_OBJ_ALGORITHM:
+ this->algorithm = parse_algorithmIdentifier(object, level, NULL);
+ if (this->algorithm != sig_alg)
+ {
+ DBG1(" signature algorithms do not agree");
+ return FALSE;
+ }
+ break;
+ case X509_OBJ_SIGNATURE:
+ this->signature = object;
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+/**
+ * Implementation of certificate_t.get_type
+ */
+static certificate_type_t get_type(private_x509_cert_t *this)
+{
+ return CERT_X509;
+}
+
+/**
+ * Implementation of certificate_t.get_subject
+ */
+static identification_t* get_subject(private_x509_cert_t *this)
+{
+ return this->subject;
+}
+
+/**
+ * Implementation of certificate_t.get_issuer
+ */
+static identification_t* get_issuer(private_x509_cert_t *this)
+{
+ return this->issuer;
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_subject(private_x509_cert_t *this, identification_t *subject)
+{
+ identification_t *current;
+ enumerator_t *enumerator;
+ id_match_t match, best;
+
+ best = this->subject->matches(this->subject, subject);
+ enumerator = this->subjectAltNames->create_enumerator(this->subjectAltNames);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ match = current->matches(current, subject);
+ if (match > best)
+ {
+ best = match;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return best;
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_issuer(private_x509_cert_t *this, identification_t *issuer)
+{
+ /* issuerAltNames currently not supported */
+ return this->issuer->matches(this->issuer, issuer);
+}
+
+/**
+ * Implementation of certificate_t.issued_by
+ */
+static bool issued_by(private_x509_cert_t *this, certificate_t *issuer,
+ bool sigcheck)
+{
+ public_key_t *key;
+ signature_scheme_t scheme;
+ bool valid;
+ x509_t *x509 = (x509_t*)issuer;
+
+ if (&this->public.interface.interface == issuer &&
+ (this->flags & X509_SELF_SIGNED))
+ {
+ return TRUE;
+ }
+ if (issuer->get_type(issuer) != CERT_X509)
+ {
+ return FALSE;
+ }
+ if (!this->issuer->equals(this->issuer, issuer->get_subject(issuer)))
+ {
+ return FALSE;
+ }
+ if (!(x509->get_flags(x509) & X509_CA))
+ {
+ return FALSE;
+ }
+ if (!sigcheck)
+ {
+ return TRUE;
+ }
+ /* TODO: generic OID to scheme mapper? */
+ switch (this->algorithm)
+ {
+ case OID_MD5_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_MD5;
+ break;
+ case OID_SHA1_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
+ break;
+ case OID_SHA256_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA256;
+ break;
+ case OID_SHA384_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA384;
+ break;
+ case OID_SHA512_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA512;
+ break;
+ default:
+ return FALSE;
+ }
+ key = issuer->get_public_key(issuer);
+ if (key == NULL)
+ {
+ return FALSE;
+ }
+ /* TODO: add a lightweight check option (comparing auth/subject keyids only) */
+ valid = key->verify(key, scheme, this->tbsCertificate, this->signature);
+ key->destroy(key);
+ return valid;
+}
+
+/**
+ * Implementation of certificate_t.get_public_key
+ */
+static public_key_t* get_public_key(private_x509_cert_t *this)
+{
+ this->public_key->get_ref(this->public_key);
+ return this->public_key;
+}
+
+/**
+ * Implementation of certificate_t.asdf
+ */
+static private_x509_cert_t* get_ref(private_x509_cert_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implementation of x509_cert_t.set_flags.
+ */
+static void set_flags(private_x509_cert_t *this, x509_flag_t flags)
+{
+ this->flags = flags;
+}
+
+/**
+ * Implementation of x509_cert_t.get_flags.
+ */
+static x509_flag_t get_flags(private_x509_cert_t *this)
+{
+ return this->flags;
+}
+
+/**
+ * Implementation of x509_cert_t.get_validity.
+ */
+static bool get_validity(private_x509_cert_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_after)
+ {
+ *not_after = this->notAfter;
+ }
+ if (not_before)
+ {
+ *not_before = this->notBefore;
+ }
+ return (t >= this->notBefore && t <= this->notAfter);
+}
+
+/**
+ * Implementation of certificate_t.get_encoding.
+ */
+static chunk_t get_encoding(private_x509_cert_t *this)
+{
+ return chunk_clone(this->certificate);
+}
+
+/**
+ * Implementation of certificate_t.equals.
+ */
+static bool equals(private_x509_cert_t *this, certificate_t *other)
+{
+ if (this == (private_x509_cert_t*)other)
+ {
+ return TRUE;
+ }
+ if (other->get_type(other) != CERT_X509)
+ {
+ return FALSE;
+ }
+ /* check if we have the same X509 implementation */
+ if (other->equals == (void*)equals)
+ {
+ if (this->signature.len == 0)
+ {
+ return FALSE;
+ }
+ return chunk_equals(this->signature, ((private_x509_cert_t*)other)->signature);
+ }
+ /* TODO: compare against other implementation */
+ return FALSE;
+}
+
+/**
+ * Implementation of x509_t.get_serial.
+ */
+static chunk_t get_serial(private_x509_cert_t *this)
+{
+ return this->serialNumber;
+}
+
+/**
+ * Implementation of x509_t.get_authKeyIdentifier.
+ */
+static identification_t *get_authKeyIdentifier(private_x509_cert_t *this)
+{
+ return this->authKeyIdentifier;
+}
+
+/**
+ * Implementation of x509_cert_t.create_subjectAltName_enumerator.
+ */
+static enumerator_t* create_subjectAltName_enumerator(private_x509_cert_t *this)
+{
+ return this->subjectAltNames->create_enumerator(this->subjectAltNames);
+}
+
+/**
+ * Implementation of x509_cert_t.create_ocsp_uri_enumerator.
+ */
+static enumerator_t* create_ocsp_uri_enumerator(private_x509_cert_t *this)
+{
+ return this->ocsp_uris->create_enumerator(this->ocsp_uris);
+}
+
+/**
+ * Implementation of x509_cert_t.create_crl_uri_enumerator.
+ */
+static enumerator_t* create_crl_uri_enumerator(private_x509_cert_t *this)
+{
+ return this->crl_uris->create_enumerator(this->crl_uris);
+}
+
+/**
+ * Implementation of certificate_t.asdf
+ */
+static void destroy(private_x509_cert_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ this->subjectAltNames->destroy_offset(this->subjectAltNames,
+ offsetof(identification_t, destroy));
+ this->crl_uris->destroy_function(this->crl_uris, free);
+ this->ocsp_uris->destroy_function(this->ocsp_uris, free);
+ DESTROY_IF(this->issuer);
+ DESTROY_IF(this->subject);
+ DESTROY_IF(this->public_key);
+ DESTROY_IF(this->authKeyIdentifier);
+ chunk_free(&this->certificate);
+ free(this);
+ }
+}
+
+/**
+ * load x509 certificate from a chunk
+ */
+static x509_cert_t *load(chunk_t chunk)
+{
+ private_x509_cert_t *this = malloc_thing(private_x509_cert_t);
+
+ this->public.interface.interface.get_type = (certificate_type_t (*)(certificate_t *this))get_type;
+ this->public.interface.interface.get_subject = (identification_t* (*)(certificate_t *this))get_subject;
+ this->public.interface.interface.get_issuer = (identification_t* (*)(certificate_t *this))get_issuer;
+ this->public.interface.interface.has_subject = (id_match_t (*)(certificate_t*, identification_t *subject))has_subject;
+ this->public.interface.interface.has_issuer = (id_match_t (*)(certificate_t*, identification_t *issuer))has_issuer;
+ this->public.interface.interface.issued_by = (bool (*)(certificate_t *this, certificate_t *issuer,bool))issued_by;
+ this->public.interface.interface.get_public_key = (public_key_t* (*)(certificate_t *this))get_public_key;
+ this->public.interface.interface.get_validity = (bool (*)(certificate_t*, time_t *when, time_t *, time_t*))get_validity;
+ this->public.interface.interface.get_encoding = (chunk_t (*)(certificate_t*))get_encoding;
+ this->public.interface.interface.equals = (bool (*)(certificate_t*, certificate_t *other))equals;
+ this->public.interface.interface.get_ref = (certificate_t* (*)(certificate_t *this))get_ref;
+ this->public.interface.interface.destroy = (void (*)(certificate_t *this))destroy;
+ this->public.interface.set_flags = (void (*)(x509_t*, x509_flag_t flags))set_flags;
+ this->public.interface.get_flags = (x509_flag_t (*)(x509_t*))get_flags;
+ this->public.interface.get_serial = (chunk_t (*)(x509_t*))get_serial;
+ this->public.interface.get_authKeyIdentifier = (identification_t* (*)(x509_t*))get_authKeyIdentifier;
+ this->public.interface.create_subjectAltName_enumerator = (enumerator_t* (*)(x509_t*))create_subjectAltName_enumerator;
+ this->public.interface.create_crl_uri_enumerator = (enumerator_t* (*)(x509_t*))create_crl_uri_enumerator;
+ this->public.interface.create_ocsp_uri_enumerator = (enumerator_t* (*)(x509_t*))create_ocsp_uri_enumerator;
+
+ this->certificate = chunk;
+ this->public_key = NULL;
+ this->subject = NULL;
+ this->issuer = NULL;
+ this->subjectAltNames = linked_list_create();
+ this->crl_uris = linked_list_create();
+ this->ocsp_uris = linked_list_create();
+ this->subjectKeyID = chunk_empty;
+ this->authKeyIdentifier = NULL;
+ this->authKeySerialNumber = chunk_empty;
+ this->flags = 0;
+ this->ref = 1;
+
+ if (!parse_certificate(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+ if (issued_by(this, &this->public.interface.interface, FALSE))
+ {
+ this->flags |= X509_SELF_SIGNED;
+ }
+ return &this->public;
+}
+
+typedef struct private_builder_t private_builder_t;
+/**
+ * Builder implementation for certificate loading
+ */
+struct private_builder_t {
+ /** implements the builder interface */
+ builder_t public;
+ /** loaded certificate */
+ x509_cert_t *cert;
+};
+
+/**
+ * Implementation of builder_t.build
+ */
+static x509_cert_t *build(private_builder_t *this)
+{
+ x509_cert_t *cert = this->cert;
+
+ free(this);
+ return cert;
+}
+
+/**
+ * Implementation of builder_t.add
+ */
+static void add(private_builder_t *this, builder_part_t part, ...)
+{
+ va_list args;
+
+ if (this->cert)
+ {
+ DBG1("ignoring surplus build part %N", builder_part_names, part);
+ return;
+ }
+
+ switch (part)
+ {
+ case BUILD_BLOB_ASN1_DER:
+ {
+ va_start(args, part);
+ this->cert = load(va_arg(args, chunk_t));
+ va_end(args);
+ break;
+ }
+ default:
+ DBG1("ignoring unsupported build part %N", builder_part_names, part);
+ break;
+ }
+}
+
+/**
+ * Builder construction function
+ */
+builder_t *x509_cert_builder(certificate_type_t type)
+{
+ private_builder_t *this;
+
+ if (type != CERT_X509)
+ {
+ return NULL;
+ }
+
+ this = malloc_thing(private_builder_t);
+
+ this->cert = NULL;
+ this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
+ this->public.build = (void*(*)(builder_t *this))build;
+
+ return &this->public;
+}
+
diff --git a/src/libstrongswan/plugins/x509/x509_cert.h b/src/libstrongswan/plugins/x509/x509_cert.h
new file mode 100644
index 000000000..be6e41b4d
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_cert.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 x509_cert x509_cert
+ * @{ @ingroup x509_p
+ */
+
+#ifndef X509_CERT_H_
+#define X509_CERT_H_
+
+typedef struct x509_cert_t x509_cert_t;
+
+#include <credentials/certificates/x509.h>
+
+/**
+ * Implementation of x509_t/certificate_t using own ASN1 parser.
+ */
+struct x509_cert_t {
+
+ /**
+ * Implements the x509_t interface
+ */
+ x509_t interface;
+};
+
+/**
+ * Create the building facility for x509 certificates
+ *
+ * @param type certificate type, CERT_X509 only
+ * @return builder instance to build certificate
+ */
+builder_t *x509_cert_builder(certificate_type_t type);
+
+#endif /* X509_CERT_H_ @}*/
diff --git a/src/libstrongswan/plugins/x509/x509_crl.c b/src/libstrongswan/plugins/x509/x509_crl.c
new file mode 100644
index 000000000..7e2bdf2b6
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_crl.c
@@ -0,0 +1,717 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "x509_crl.h"
+
+typedef struct private_x509_crl_t private_x509_crl_t;
+typedef struct revoked_t revoked_t;
+
+#include <debug.h>
+#include <library.h>
+#include <asn1/asn1.h>
+#include <credentials/certificates/x509.h>
+
+/**
+ * entry for a revoked certificate
+ */
+struct revoked_t {
+ /**
+ * serial of the revoked certificate
+ */
+ chunk_t serial;
+
+ /**
+ * date of revocation
+ */
+ time_t date;
+
+ /**
+ * reason for revocation
+ */
+ crl_reason_t reason;
+};
+
+/**
+ * private data of x509_crl
+ */
+struct private_x509_crl_t {
+
+ /**
+ * public functions
+ */
+ x509_crl_t public;
+
+ /**
+ * X.509 crl in DER format
+ */
+ chunk_t certificateList;
+
+ /**
+ * X.509 crl body over which signature is computed
+ */
+ chunk_t tbsCertList;
+
+ /**
+ * Version of the X.509 crl
+ */
+ u_int version;
+
+ /**
+ * ID representing the crl issuer
+ */
+ identification_t *issuer;
+
+ /**
+ * CRL number
+ */
+ chunk_t crlNumber;
+
+ /**
+ * Time when the crl was generated
+ */
+ time_t thisUpdate;
+
+ /**
+ * Time when an update crl will be available
+ */
+ time_t nextUpdate;
+
+ /**
+ * list of revoked certificates as revoked_t
+ */
+ linked_list_t *revoked;
+
+ /**
+ * Authority Key Identifier
+ */
+ identification_t *authKeyIdentifier;
+
+ /**
+ * Authority Key Serial Number
+ */
+ chunk_t authKeySerialNumber;
+
+ /**
+ * Signature algorithm
+ */
+ int algorithm;
+
+ /**
+ * Signature
+ */
+ chunk_t signature;
+
+ /**
+ * reference counter
+ */
+ refcount_t ref;
+};
+
+/**
+ * from x509_cert
+ */
+extern identification_t* x509_parse_authorityKeyIdentifier(
+ chunk_t blob, int level0,
+ chunk_t *authKeySerialNumber);
+
+/**
+ * ASN.1 definition of an X.509 certificate revocation list
+ */
+static const asn1Object_t crlObjects[] = {
+ { 0, "certificateList", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */
+ { 1, "tbsCertList", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */
+ { 2, "version", ASN1_INTEGER, ASN1_OPT |
+ ASN1_BODY }, /* 2 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
+ { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 4 */
+ { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */
+ { 2, "thisUpdate", ASN1_EOC, ASN1_RAW }, /* 6 */
+ { 2, "nextUpdate", ASN1_EOC, ASN1_RAW }, /* 7 */
+ { 2, "revokedCertificates", ASN1_SEQUENCE, ASN1_OPT |
+ ASN1_LOOP }, /* 8 */
+ { 3, "certList", ASN1_SEQUENCE, ASN1_NONE }, /* 9 */
+ { 4, "userCertificate", ASN1_INTEGER, ASN1_BODY }, /* 10 */
+ { 4, "revocationDate", ASN1_EOC, ASN1_RAW }, /* 11 */
+ { 4, "crlEntryExtensions", ASN1_SEQUENCE, ASN1_OPT |
+ ASN1_LOOP }, /* 12 */
+ { 5, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */
+ { 6, "extnID", ASN1_OID, ASN1_BODY }, /* 14 */
+ { 6, "critical", ASN1_BOOLEAN, ASN1_DEF |
+ ASN1_BODY }, /* 15 */
+ { 6, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 16 */
+ { 4, "end opt or loop", ASN1_EOC, ASN1_END }, /* 17 */
+ { 2, "end opt or loop", ASN1_EOC, ASN1_END }, /* 18 */
+ { 2, "optional extensions", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 19 */
+ { 3, "crlExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */
+ { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */
+ { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */
+ { 5, "critical", ASN1_BOOLEAN, ASN1_DEF |
+ ASN1_BODY }, /* 23 */
+ { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */
+ { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */
+ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */
+ { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */
+ };
+
+#define CRL_OBJ_TBS_CERT_LIST 1
+#define CRL_OBJ_VERSION 2
+#define CRL_OBJ_SIG_ALG 4
+#define CRL_OBJ_ISSUER 5
+#define CRL_OBJ_THIS_UPDATE 6
+#define CRL_OBJ_NEXT_UPDATE 7
+#define CRL_OBJ_USER_CERTIFICATE 10
+#define CRL_OBJ_REVOCATION_DATE 11
+#define CRL_OBJ_CRL_ENTRY_EXTN_ID 14
+#define CRL_OBJ_CRL_ENTRY_CRITICAL 15
+#define CRL_OBJ_CRL_ENTRY_EXTN_VALUE 16
+#define CRL_OBJ_EXTN_ID 22
+#define CRL_OBJ_CRITICAL 23
+#define CRL_OBJ_EXTN_VALUE 24
+#define CRL_OBJ_ALGORITHM 27
+#define CRL_OBJ_SIGNATURE 28
+#define CRL_OBJ_ROOF 29
+
+/**
+ * Parses an X.509 Certificate Revocation List (CRL)
+ */
+static bool parse(private_x509_crl_t *this)
+{
+ asn1_ctx_t ctx;
+ bool critical;
+ chunk_t extnID;
+ chunk_t userCertificate = chunk_empty;
+ revoked_t *revoked = NULL;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ asn1_init(&ctx, this->certificateList, 0, FALSE, FALSE);
+ while (objectID < CRL_OBJ_ROOF)
+ {
+ if (!extract_object(crlObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+
+ /* those objects which will parsed further need the next higher level */
+ level++;
+
+ switch (objectID)
+ {
+ case CRL_OBJ_TBS_CERT_LIST:
+ this->tbsCertList = object;
+ break;
+ case CRL_OBJ_VERSION:
+ this->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
+ DBG2(" v%d", this->version);
+ break;
+ case CRL_OBJ_SIG_ALG:
+ this->algorithm = parse_algorithmIdentifier(object, level, NULL);
+ break;
+ case CRL_OBJ_ISSUER:
+ this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+ DBG2(" '%D'", this->issuer);
+ break;
+ case CRL_OBJ_THIS_UPDATE:
+ this->thisUpdate = parse_time(object, level);
+ break;
+ case CRL_OBJ_NEXT_UPDATE:
+ this->nextUpdate = parse_time(object, level);
+ break;
+ case CRL_OBJ_USER_CERTIFICATE:
+ userCertificate = object;
+ break;
+ case CRL_OBJ_REVOCATION_DATE:
+ revoked = malloc_thing(revoked_t);
+ revoked->serial = userCertificate;
+ revoked->date = parse_time(object, level);
+ revoked->reason = CRL_UNSPECIFIED;
+ this->revoked->insert_last(this->revoked, (void *)revoked);
+ break;
+ case CRL_OBJ_CRL_ENTRY_EXTN_ID:
+ case CRL_OBJ_EXTN_ID:
+ extnID = object;
+ break;
+ case CRL_OBJ_CRL_ENTRY_CRITICAL:
+ case CRL_OBJ_CRITICAL:
+ critical = object.len && *object.ptr;
+ DBG2(" %s", critical ? "TRUE" : "FALSE");
+ break;
+ case CRL_OBJ_CRL_ENTRY_EXTN_VALUE:
+ case CRL_OBJ_EXTN_VALUE:
+ {
+ int extn_oid = known_oid(extnID);
+
+ if (revoked && extn_oid == OID_CRL_REASON_CODE)
+ {
+ if (*object.ptr == ASN1_ENUMERATED &&
+ asn1_length(&object) == 1)
+ {
+ revoked->reason = *object.ptr;
+ }
+ DBG2(" '%N'", crl_reason_names, revoked->reason);
+ }
+ else if (extn_oid == OID_AUTHORITY_KEY_ID)
+ {
+
+ this->authKeyIdentifier = x509_parse_authorityKeyIdentifier(object,
+ level, &this->authKeySerialNumber);
+ }
+ else if (extn_oid == OID_CRL_NUMBER)
+ {
+ if (!parse_asn1_simple_object(&object, ASN1_INTEGER,
+ level, "crlNumber"))
+ {
+ return FALSE;
+ }
+ this->crlNumber = object;
+ }
+ }
+ break;
+ case CRL_OBJ_ALGORITHM:
+ {
+ int algo = parse_algorithmIdentifier(object, level, NULL);
+ if (this->algorithm != algo)
+ {
+ DBG1(" signature algorithms do not agree");
+ return FALSE;
+ }
+ break;
+ }
+ case CRL_OBJ_SIGNATURE:
+ this->signature = object;
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+/**
+ * enumerator filter callback for create_enumerator
+ */
+static bool filter(void *data, revoked_t *revoked, chunk_t *serial, void *p2,
+ time_t *date, void *p3, crl_reason_t *reason)
+{
+ if (serial)
+ {
+ *serial = revoked->serial;
+ }
+ if (date)
+ {
+ *date = revoked->date;
+ }
+ if (reason)
+ {
+ *reason = revoked->reason;
+ }
+ return TRUE;
+}
+
+/**
+ * Implementation of crl_t.is_newer.
+ */
+static bool is_newer(private_x509_crl_t *this, crl_t *that)
+{
+ chunk_t that_crlNumber = that->get_serial(that);
+ bool new;
+
+ /* compare crlNumbers if available - otherwise use thisUpdate */
+ if (this->crlNumber.ptr != NULL && that_crlNumber.ptr != NULL)
+ {
+ new = chunk_compare(this->crlNumber, that_crlNumber) > 0;
+ DBG1(" crl #%#B is %s - existing crl #%#B %s",
+ &this->crlNumber, new ? "newer":"not newer",
+ &that_crlNumber, new ? "replaced":"retained");
+ }
+ else
+ {
+ certificate_t *this_cert = &this->public.crl.certificate;
+ certificate_t *that_cert = &that->certificate;
+
+ time_t this_update, that_update, now = time(NULL);
+
+ this_cert->get_validity(this_cert, &now, &this_update, NULL);
+ that_cert->get_validity(that_cert, &now, &that_update, NULL);
+ new = this_update > that_update;
+ DBG1(" crl from %#T is %s - existing crl from %#T %s",
+ &this_update, FALSE, new ? "newer":"not newer",
+ &that_update, FALSE, new ? "replaced":"retained");
+ }
+ return new;
+}
+
+/**
+ * Implementation of crl_t.get_serial.
+ */
+static chunk_t get_serial(private_x509_crl_t *this)
+{
+ return this->crlNumber;
+}
+
+/**
+ * Implementation of crl_t.get_authKeyIdentifier.
+ */
+static identification_t* get_authKeyIdentifier(private_x509_crl_t *this)
+{
+ return this->authKeyIdentifier;
+}
+/**
+ * Implementation of crl_t.create_enumerator.
+ */
+static enumerator_t* create_enumerator(private_x509_crl_t *this)
+{
+ return enumerator_create_filter(
+ this->revoked->create_enumerator(this->revoked),
+ (void*)filter, NULL, NULL);
+}
+
+/**
+ * Implementation of certificate_t.get_type
+ */
+static certificate_type_t get_type(private_x509_crl_t *this)
+{
+ return CERT_X509_CRL;
+}
+
+/**
+ * Implementation of certificate_t.get_subject
+ */
+static identification_t* get_subject(private_x509_crl_t *this)
+{
+ return this->issuer;
+}
+
+/**
+ * Implementation of certificate_t.get_issuer
+ */
+static identification_t* get_issuer(private_x509_crl_t *this)
+{
+ return this->issuer;
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_subject(private_x509_crl_t *this, identification_t *subject)
+{
+ return ID_MATCH_NONE;
+}
+
+/**
+ * Implementation of certificate_t.has_issuer.
+ */
+static id_match_t has_issuer(private_x509_crl_t *this, identification_t *issuer)
+{
+ id_match_t match;
+
+ if (issuer->get_type(issuer) == ID_PUBKEY_SHA1)
+ {
+ if (this->authKeyIdentifier)
+ {
+ match = issuer->matches(issuer, this->authKeyIdentifier);
+ }
+ else
+ {
+ match = ID_MATCH_NONE;
+ }
+ }
+ else
+ {
+ match = this->issuer->matches(this->issuer, issuer);
+ }
+ return match;
+}
+
+/**
+ * Implementation of certificate_t.issued_by
+ */
+static bool issued_by(private_x509_crl_t *this, certificate_t *issuer,
+ bool sigcheck)
+{
+ public_key_t *key;
+ signature_scheme_t scheme;
+ bool valid;
+ x509_t *x509 = (x509_t*)issuer;
+
+ /* check if issuer is an X.509 CA certificate */
+ if (issuer->get_type(issuer) != CERT_X509)
+ {
+ return FALSE;
+ }
+ if (!(x509->get_flags(x509) & X509_CA))
+ {
+ return FALSE;
+ }
+
+ /* get the public key of the issuer */
+ key = issuer->get_public_key(issuer);
+
+ /* compare keyIdentifiers if available, otherwise use DNs */
+ if (this->authKeyIdentifier && key)
+ {
+ identification_t *subjectKeyIdentifier = key->get_id(key, ID_PUBKEY_SHA1);
+
+ if (!subjectKeyIdentifier->equals(subjectKeyIdentifier,
+ this->authKeyIdentifier))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!this->issuer->equals(this->issuer, issuer->get_subject(issuer)))
+ {
+ return FALSE;
+ }
+ }
+
+ if (!sigcheck)
+ {
+ return TRUE;
+ }
+ /* TODO: generic OID to scheme mapper? */
+ switch (this->algorithm)
+ {
+ case OID_MD5_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_MD5;
+ break;
+ case OID_SHA1_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
+ break;
+ case OID_SHA256_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA256;
+ break;
+ case OID_SHA384_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA384;
+ break;
+ case OID_SHA512_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA512;
+ break;
+ default:
+ return FALSE;
+ }
+ if (key == NULL)
+ {
+ return FALSE;
+ }
+ valid = key->verify(key, scheme, this->tbsCertList, this->signature);
+ key->destroy(key);
+ return valid;
+}
+
+/**
+ * Implementation of certificate_t.get_public_key
+ */
+static public_key_t* get_public_key(private_x509_crl_t *this)
+{
+ return NULL;
+}
+
+/**
+ * Implementation of certificate_t.asdf
+ */
+static private_x509_crl_t* get_ref(private_x509_crl_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implementation of certificate_t.get_validity.
+ */
+static bool get_validity(private_x509_crl_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_after)
+ {
+ *not_after = this->nextUpdate;
+ }
+ if (not_before)
+ {
+ *not_before = this->thisUpdate;
+ }
+ return (t <= this->nextUpdate);
+}
+
+/**
+ * Implementation of certificate_t.get_encoding.
+ */
+static chunk_t get_encoding(private_x509_crl_t *this)
+{
+ return chunk_clone(this->certificateList);
+}
+
+/**
+ * Implementation of certificate_t.equals.
+ */
+static bool equals(private_x509_crl_t *this, certificate_t *other)
+{
+ if ((certificate_t*)this == other)
+ {
+ return TRUE;
+ }
+ if (other->equals == (void*)equals)
+ { /* same implementation */
+ return chunk_equals(this->signature,
+ ((private_x509_crl_t*)other)->signature);
+ }
+ /* TODO: compare against other implementations */
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.destroy
+ */
+static void destroy(private_x509_crl_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ this->revoked->destroy_function(this->revoked, free);
+ DESTROY_IF(this->issuer);
+ DESTROY_IF(this->authKeyIdentifier);
+ free(this->certificateList.ptr);
+ free(this);
+ }
+}
+
+/**
+ * load a X509 CRL from a chunk of date (ASN1 DER)
+ */
+static x509_crl_t *load(chunk_t chunk)
+{
+ private_x509_crl_t *this = malloc_thing(private_x509_crl_t);
+
+ this->public.crl.is_newer = (bool (*)(crl_t*,crl_t*))is_newer;
+ this->public.crl.get_serial = (chunk_t (*)(crl_t*))get_serial;
+ this->public.crl.get_authKeyIdentifier = (identification_t* (*)(crl_t*))get_authKeyIdentifier;
+ this->public.crl.create_enumerator = (enumerator_t* (*)(crl_t*))create_enumerator;
+ this->public.crl.certificate.get_type = (certificate_type_t (*)(certificate_t *this))get_type;
+ this->public.crl.certificate.get_subject = (identification_t* (*)(certificate_t *this))get_subject;
+ this->public.crl.certificate.get_issuer = (identification_t* (*)(certificate_t *this))get_issuer;
+ this->public.crl.certificate.has_subject = (id_match_t (*)(certificate_t*, identification_t *subject))has_subject;
+ this->public.crl.certificate.has_issuer = (id_match_t (*)(certificate_t*, identification_t *issuer))has_issuer;
+ this->public.crl.certificate.issued_by = (bool (*)(certificate_t *this, certificate_t *issuer,bool))issued_by;
+ this->public.crl.certificate.get_public_key = (public_key_t* (*)(certificate_t *this))get_public_key;
+ this->public.crl.certificate.get_validity = (bool (*)(certificate_t*, time_t *when, time_t *, time_t*))get_validity;
+ this->public.crl.certificate.get_encoding = (chunk_t (*)(certificate_t*))get_encoding;
+ this->public.crl.certificate.equals = (bool (*)(certificate_t*, certificate_t *other))equals;
+ this->public.crl.certificate.get_ref = (certificate_t* (*)(certificate_t *this))get_ref;
+ this->public.crl.certificate.destroy = (void (*)(certificate_t *this))destroy;
+
+ this->certificateList = chunk;
+ this->tbsCertList = chunk_empty;
+ this->issuer = NULL;
+ this->crlNumber = chunk_empty;
+ this->revoked = linked_list_create();
+ this->authKeyIdentifier = NULL;
+ this->authKeySerialNumber = chunk_empty;
+ this->ref = 1;
+
+ if (!parse(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
+
+typedef struct private_builder_t private_builder_t;
+/**
+ * Builder implementation for certificate loading
+ */
+struct private_builder_t {
+ /** implements the builder interface */
+ builder_t public;
+ /** loaded CRL */
+ x509_crl_t *crl;
+};
+
+/**
+ * Implementation of builder_t.build
+ */
+static x509_crl_t *build(private_builder_t *this)
+{
+ x509_crl_t *crl = this->crl;
+
+ free(this);
+ return crl;
+}
+
+/**
+ * Implementation of builder_t.add
+ */
+static void add(private_builder_t *this, builder_part_t part, ...)
+{
+ va_list args;
+
+ if (this->crl)
+ {
+ DBG1("ignoring surplus build part %N", builder_part_names, part);
+ return;
+ }
+
+ switch (part)
+ {
+ case BUILD_BLOB_ASN1_DER:
+ {
+ va_start(args, part);
+ this->crl = load(va_arg(args, chunk_t));
+ va_end(args);
+ break;
+ }
+ default:
+ DBG1("ignoring unsupported build part %N", builder_part_names, part);
+ break;
+ }
+}
+
+/**
+ * Builder construction function
+ */
+builder_t *x509_crl_builder(certificate_type_t type)
+{
+ private_builder_t *this;
+
+ if (type != CERT_X509_CRL)
+ {
+ return NULL;
+ }
+
+ this = malloc_thing(private_builder_t);
+
+ this->crl = NULL;
+ this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
+ this->public.build = (void*(*)(builder_t *this))build;
+
+ return &this->public;
+}
+
diff --git a/src/libstrongswan/plugins/x509/x509_crl.h b/src/libstrongswan/plugins/x509/x509_crl.h
new file mode 100644
index 000000000..0d9e5cca4
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_crl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 x509_crl x509_crl
+ * @{ @ingroup x509_p
+ */
+
+#ifndef X509_CRL_H_
+#define X509_CRL_H_
+
+typedef struct x509_crl_t x509_crl_t;
+
+#include <credentials/certificates/crl.h>
+
+/**
+ * Implementation of the X509 certification revocation list.
+ */
+struct x509_crl_t {
+
+ /**
+ * Implements the crl_t interface
+ */
+ crl_t crl;
+};
+
+
+/**
+ * Create the building facility for x509 certificate revocation lists.
+ *
+ * @param type certificate type, CERT_X509_CRL only
+ * @return builder instance to build certificate
+ */
+builder_t *x509_crl_builder(certificate_type_t type);
+
+#endif /* X509_CRL_H_ @}*/
diff --git a/src/libstrongswan/plugins/x509/x509_ocsp_request.c b/src/libstrongswan/plugins/x509/x509_ocsp_request.c
new file mode 100644
index 000000000..7e3230412
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_ocsp_request.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2007 Andreas Steffen
+ * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "x509_ocsp_request.h"
+
+#include <library.h>
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <utils/identification.h>
+#include <utils/randomizer.h>
+#include <utils/linked_list.h>
+#include <debug.h>
+#include <credentials/certificates/x509.h>
+
+#define NONCE_LEN 16
+
+typedef struct private_x509_ocsp_request_t private_x509_ocsp_request_t;
+
+/**
+ * private data of x509_ocsp_request
+ */
+struct private_x509_ocsp_request_t {
+
+ /**
+ * public functions
+ */
+ x509_ocsp_request_t public;
+
+ /**
+ * CA the candidates belong to
+ */
+ x509_t *ca;
+
+ /**
+ * Requestor name, subject of cert used if not set
+ */
+ identification_t *requestor;
+
+ /**
+ * Requestor certificate, included in request
+ */
+ certificate_t *cert;
+
+ /**
+ * Requestor private key to sign request
+ */
+ private_key_t *key;
+
+ /**
+ * list of certificates to check, x509_t
+ */
+ linked_list_t *candidates;
+
+ /**
+ * nonce used in request
+ */
+ chunk_t nonce;
+
+ /**
+ * encoded OCSP request
+ */
+ chunk_t encoding;
+
+ /**
+ * reference count
+ */
+ refcount_t ref;
+};
+
+static u_char ASN1_nonce_oid_str[] = {
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02
+};
+
+static u_char ASN1_response_oid_str[] = {
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04
+};
+
+static u_char ASN1_response_content_str[] = {
+ 0x04, 0x0D,
+ 0x30, 0x0B,
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
+};
+
+static const chunk_t ASN1_nonce_oid = chunk_from_buf(ASN1_nonce_oid_str);
+static const chunk_t ASN1_response_oid = chunk_from_buf(ASN1_response_oid_str);
+static const chunk_t ASN1_response_content = chunk_from_buf(ASN1_response_content_str);
+
+/**
+ * build requestorName
+ */
+static chunk_t build_requestorName(private_x509_ocsp_request_t *this)
+{
+ if (this->requestor || this->cert)
+ { /* use requestor name, fallback to his cert subject */
+ if (!this->requestor)
+ {
+ this->requestor = this->cert->get_subject(this->cert);
+ this->requestor = this->requestor->clone(this->requestor);
+ }
+ return asn1_wrap(ASN1_CONTEXT_C_1, "m",
+ asn1_simple_object(ASN1_CONTEXT_C_4,
+ this->requestor->get_encoding(this->requestor)));
+
+ }
+ return chunk_empty;
+}
+
+/**
+ * build Request, not using singleRequestExtensions
+ */
+static chunk_t build_Request(private_x509_ocsp_request_t *this,
+ chunk_t issuerNameHash, chunk_t issuerKeyHash,
+ chunk_t serialNumber)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "m",
+ asn1_wrap(ASN1_SEQUENCE, "cmmm",
+ asn1_algorithmIdentifier(OID_SHA1),
+ asn1_simple_object(ASN1_OCTET_STRING, issuerNameHash),
+ asn1_simple_object(ASN1_OCTET_STRING, issuerKeyHash),
+ asn1_simple_object(ASN1_INTEGER, serialNumber)));
+}
+
+/**
+ * build requestList
+ */
+static chunk_t build_requestList(private_x509_ocsp_request_t *this)
+{
+ chunk_t issuerNameHash, issuerKeyHash;
+ identification_t *issuer;
+ x509_t *x509;
+ certificate_t *cert;
+ chunk_t list = chunk_empty;
+ public_key_t *public;
+
+ cert = (certificate_t*)this->ca;
+ public = cert->get_public_key(cert);
+ if (public)
+ {
+ hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ if (hasher)
+ {
+ identification_t *keyid = public->get_id(public, ID_PUBKEY_SHA1);
+ if (keyid)
+ {
+ enumerator_t *enumerator;
+
+ issuerKeyHash = keyid->get_encoding(keyid);
+
+ issuer = cert->get_subject(cert);
+ hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
+ &issuerNameHash);
+ hasher->destroy(hasher);
+
+ enumerator = this->candidates->create_enumerator(this->candidates);
+ while (enumerator->enumerate(enumerator, &x509))
+ {
+ chunk_t request, serialNumber;
+
+ serialNumber = x509->get_serial(x509);
+ request = build_Request(this, issuerNameHash, issuerKeyHash,
+ serialNumber);
+ list = chunk_cat("mm", list, request);
+ }
+ enumerator->destroy(enumerator);
+ chunk_free(&issuerNameHash);
+ }
+ }
+ else
+ {
+ DBG1("creating OCSP request failed, SHA1 not supported");
+ }
+ public->destroy(public);
+ }
+ else
+ {
+ DBG1("creating OCSP request failed, CA certificate has no public key");
+ }
+ return asn1_wrap(ASN1_SEQUENCE, "m", list);
+}
+
+/**
+ * build nonce extension
+ */
+static chunk_t build_nonce(private_x509_ocsp_request_t *this)
+{
+ randomizer_t *randomizer;
+
+ randomizer = randomizer_create();
+ randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN, &this->nonce);
+ randomizer->destroy(randomizer);
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm", ASN1_nonce_oid,
+ asn1_simple_object(ASN1_OCTET_STRING, this->nonce));
+}
+
+/**
+ * build acceptableResponses extension
+ */
+static chunk_t build_acceptableResponses(private_x509_ocsp_request_t *this)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "cc",
+ ASN1_response_oid,
+ ASN1_response_content);
+}
+
+/**
+ * build requestExtensions
+ */
+static chunk_t build_requestExtensions(private_x509_ocsp_request_t *this)
+{
+ return asn1_wrap(ASN1_CONTEXT_C_2, "m",
+ asn1_wrap(ASN1_SEQUENCE, "mm",
+ build_nonce(this),
+ build_acceptableResponses(this)));
+}
+
+/**
+ * build tbsRequest
+ */
+static chunk_t build_tbsRequest(private_x509_ocsp_request_t *this)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "mmm",
+ build_requestorName(this),
+ build_requestList(this),
+ build_requestExtensions(this));
+}
+
+/**
+ * Build the optionalSignature
+ */
+static chunk_t build_optionalSignature(private_x509_ocsp_request_t *this,
+ chunk_t tbsRequest)
+{
+ int oid;
+ signature_scheme_t scheme;
+ chunk_t certs, signature;
+
+ switch (this->key->get_type(this->key))
+ {
+ /* TODO: use a generic mapping function */
+ case KEY_RSA:
+ oid = OID_SHA1_WITH_RSA;
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
+ break;
+ default:
+ DBG1("unable to sign OCSP request, %N signature not supported",
+ key_type_names, this->key->get_type(this->key));
+ return chunk_empty;
+ }
+
+ if (!this->key->sign(this->key, scheme, tbsRequest, &signature))
+ {
+ DBG1("creating OCSP signature failed, skipped");
+ return chunk_empty;
+ }
+ if (this->cert)
+ {
+ certs = asn1_wrap(ASN1_CONTEXT_C_0, "m",
+ asn1_wrap(ASN1_SEQUENCE, "m",
+ this->cert->get_encoding(this->cert)));
+ }
+ return asn1_wrap(ASN1_CONTEXT_C_0, "m",
+ asn1_wrap(ASN1_SEQUENCE, "cmm",
+ asn1_algorithmIdentifier(oid),
+ asn1_bitstring("m", signature),
+ certs));
+}
+
+/**
+ * Build the OCSPRequest data
+ *
+ */
+static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this)
+{
+ chunk_t tbsRequest, optionalSignature = chunk_empty;
+
+ tbsRequest = build_tbsRequest(this);
+ if (this->key)
+ {
+ optionalSignature = build_optionalSignature(this, tbsRequest);
+ }
+ return asn1_wrap(ASN1_SEQUENCE, "mm", tbsRequest, optionalSignature);
+}
+
+
+/**
+ * Implementation of certificate_t.get_type
+ */
+static certificate_type_t get_type(private_x509_ocsp_request_t *this)
+{
+ return CERT_X509_OCSP_REQUEST;
+}
+
+/**
+ * Implementation of certificate_t.get_subject
+ */
+static identification_t* get_subject(private_x509_ocsp_request_t *this)
+{
+ certificate_t *ca = (certificate_t*)this->ca;
+
+ if (this->requestor)
+ {
+ return this->requestor;
+ }
+ if (this->cert)
+ {
+ return this->cert->get_subject(this->cert);
+ }
+ return ca->get_subject(ca);
+}
+
+/**
+ * Implementation of certificate_t.get_issuer
+ */
+static identification_t* get_issuer(private_x509_ocsp_request_t *this)
+{
+ certificate_t *ca = (certificate_t*)this->ca;
+
+ return ca->get_subject(ca);
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_subject(private_x509_ocsp_request_t *this,
+ identification_t *subject)
+{
+ certificate_t *current;
+ enumerator_t *enumerator;
+ id_match_t match, best = ID_MATCH_NONE;
+
+ enumerator = this->candidates->create_enumerator(this->candidates);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ match = current->has_subject(current, subject);
+ if (match > best)
+ {
+ best = match;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return best;
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_issuer(private_x509_ocsp_request_t *this,
+ identification_t *issuer)
+{
+ certificate_t *ca = (certificate_t*)this->ca;
+
+ return ca->has_subject(ca, issuer);
+}
+
+/**
+ * Implementation of certificate_t.issued_by
+ */
+static bool issued_by(private_x509_ocsp_request_t *this, certificate_t *issuer,
+ bool sigcheck)
+{
+ DBG1("OCSP request validation not implemented!");
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.get_public_key
+ */
+static public_key_t* get_public_key(private_x509_ocsp_request_t *this)
+{
+ return NULL;
+}
+
+/**
+ * Implementation of x509_cert_t.get_validity.
+ */
+static bool get_validity(private_x509_ocsp_request_t *this, time_t *when,
+ time_t *not_before, time_t *not_after)
+{
+ certificate_t *cert;
+
+ if (this->cert)
+ {
+ cert = this->cert;
+ }
+ else
+ {
+ cert = (certificate_t*)this->ca;
+ }
+ return cert->get_validity(cert, when, not_before, not_after);
+}
+
+/**
+ * Implementation of certificate_t.get_encoding.
+ */
+static chunk_t get_encoding(private_x509_ocsp_request_t *this)
+{
+ return chunk_clone(this->encoding);
+}
+
+/**
+ * Implementation of certificate_t.equals.
+ */
+static bool equals(private_x509_ocsp_request_t *this, certificate_t *other)
+{
+ if (this == (private_x509_ocsp_request_t*)other)
+ {
+ return TRUE;
+ }
+ if (other->get_type(other) != CERT_X509_OCSP_REQUEST)
+ {
+ return FALSE;
+ }
+ /* check if we have the same X509 implementation */
+ if (other->equals == (void*)equals)
+ {
+ return chunk_equals(this->encoding,
+ ((private_x509_ocsp_request_t*)other)->encoding);
+ }
+ /* TODO: compare against other implementation */
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.asdf
+ */
+static private_x509_ocsp_request_t* get_ref(private_x509_ocsp_request_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implementation of x509_ocsp_request_t.destroy
+ */
+static void destroy(private_x509_ocsp_request_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ DESTROY_IF((certificate_t*)this->ca);
+ DESTROY_IF(this->requestor);
+ DESTROY_IF(this->cert);
+ DESTROY_IF(this->key);
+ this->candidates->destroy_offset(this->candidates, offsetof(certificate_t, destroy));
+ chunk_free(&this->nonce);
+ chunk_free(&this->encoding);
+ free(this);
+ }
+}
+
+/**
+ * create an empty but initialized OCSP request
+ */
+static private_x509_ocsp_request_t *create_empty()
+{
+ private_x509_ocsp_request_t *this = malloc_thing(private_x509_ocsp_request_t);
+
+ this->public.interface.interface.get_type = (certificate_type_t (*)(certificate_t *this))get_type;
+ this->public.interface.interface.get_subject = (identification_t* (*)(certificate_t *this))get_subject;
+ this->public.interface.interface.get_issuer = (identification_t* (*)(certificate_t *this))get_issuer;
+ this->public.interface.interface.has_subject = (id_match_t(*)(certificate_t*, identification_t *subject))has_subject;
+ this->public.interface.interface.has_issuer = (id_match_t(*)(certificate_t*, identification_t *issuer))has_issuer;
+ this->public.interface.interface.issued_by = (bool (*)(certificate_t *this, certificate_t *issuer,bool))issued_by;
+ this->public.interface.interface.get_public_key = (public_key_t* (*)(certificate_t *this))get_public_key;
+ this->public.interface.interface.get_validity = (bool(*)(certificate_t*, time_t *when, time_t *, time_t*))get_validity;
+ this->public.interface.interface.get_encoding = (chunk_t(*)(certificate_t*))get_encoding;
+ this->public.interface.interface.equals = (bool(*)(certificate_t*, certificate_t *other))equals;
+ this->public.interface.interface.get_ref = (certificate_t* (*)(certificate_t *this))get_ref;
+ this->public.interface.interface.destroy = (void (*)(certificate_t *this))destroy;
+
+ this->ca = NULL;
+ this->requestor = NULL;
+ this->cert = NULL;
+ this->key = NULL;
+ this->nonce = chunk_empty;
+ this->encoding = chunk_empty;
+ this->candidates = linked_list_create();
+ this->ref = 1;
+
+ return this;
+}
+
+typedef struct private_builder_t private_builder_t;
+/**
+ * Builder implementation for certificate loading
+ */
+struct private_builder_t {
+ /** implements the builder interface */
+ builder_t public;
+ /** OCSP request to build */
+ private_x509_ocsp_request_t *req;
+};
+
+/**
+ * Implementation of builder_t.build
+ */
+static x509_ocsp_request_t *build(private_builder_t *this)
+{
+ private_x509_ocsp_request_t *req;
+
+ req = this->req;
+ free(this);
+ if (req->ca)
+ {
+ req->encoding = build_OCSPRequest(req);
+ return &req->public;
+ }
+ destroy(req);
+ return NULL;
+}
+
+/**
+ * Implementation of builder_t.add
+ */
+static void add(private_builder_t *this, builder_part_t part, ...)
+{
+ va_list args;
+ certificate_t *cert;
+
+ va_start(args, part);
+ switch (part)
+ {
+ case BUILD_CA_CERT:
+ cert = va_arg(args, certificate_t*);
+ if (cert->get_type(cert) == CERT_X509)
+ {
+ this->req->ca = (x509_t*)cert;
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ break;
+ case BUILD_CERT:
+ cert = va_arg(args, certificate_t*);
+ if (cert->get_type(cert) == CERT_X509)
+ {
+ this->req->candidates->insert_last(this->req->candidates, cert);
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ break;
+ case BUILD_SIGNING_CERT:
+ this->req->cert = va_arg(args, certificate_t*);
+ break;
+ case BUILD_SIGNING_KEY:
+ this->req->key = va_arg(args, private_key_t*);
+ break;
+ case BUILD_SUBJECT:
+ this->req->requestor = va_arg(args, identification_t*);
+ break;
+ default:
+ DBG1("ignoring unsupported build part %N", builder_part_names, part);
+ break;
+ }
+ va_end(args);
+}
+
+/**
+ * Builder construction function
+ */
+builder_t *x509_ocsp_request_builder(certificate_type_t type)
+{
+ private_builder_t *this;
+
+ if (type != CERT_X509_OCSP_REQUEST)
+ {
+ return NULL;
+ }
+
+ this = malloc_thing(private_builder_t);
+
+ this->req = create_empty();
+ this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
+ this->public.build = (void*(*)(builder_t *this))build;
+
+ return &this->public;
+}
+
diff --git a/src/libstrongswan/plugins/x509/x509_ocsp_request.h b/src/libstrongswan/plugins/x509/x509_ocsp_request.h
new file mode 100644
index 000000000..0a4016f65
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_ocsp_request.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 x509_ocsp_request x509_ocsp_request
+ * @{ @ingroup x509_p
+ */
+
+#ifndef X509_OCSP_REQUEST_H_
+#define X509_OCSP_REQUEST_H_
+
+#include <credentials/certificates/ocsp_request.h>
+
+typedef struct x509_ocsp_request_t x509_ocsp_request_t;
+
+/**
+ * Implementation of ocsp_request_t using own ASN1 parser.
+ */
+struct x509_ocsp_request_t {
+
+ /**
+ * Implements the ocsp_request_t interface
+ */
+ ocsp_request_t interface;
+};
+
+/**
+ * Create the building facility for OCSP requests.
+ *
+ * The resulting builder accepts:
+ * BUILD_CA_CERT: CA of the checked certificates, exactly one
+ * BUILD_CERT: certificates to check with the request, at least one
+ * BUILD_SUBJECT: subject requesting check, optional
+ * BUILD_SIGNING_CERT: certificate to create requestor signature, optional
+ * BUILD_SIGNING_KEY: private key to create requestor signature, optional
+ *
+ * @param type certificate type, CERT_X509_OCSP_REQUEST only
+ * @return builder instance to build OCSP requests
+ */
+builder_t *x509_ocsp_request_builder(certificate_type_t type);
+
+#endif /* X509_OCSP_REQUEST_H_ @}*/
diff --git a/src/libstrongswan/plugins/x509/x509_ocsp_response.c b/src/libstrongswan/plugins/x509/x509_ocsp_response.c
new file mode 100644
index 000000000..4ea2871d2
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_ocsp_response.c
@@ -0,0 +1,928 @@
+/**
+ * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2007 Andreas Steffen
+ * Hochschule für Technik Rapperswil
+ * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "x509_ocsp_response.h"
+
+#include <time.h>
+
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <utils/identification.h>
+#include <utils/linked_list.h>
+#include <debug.h>
+
+#include <library.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+
+typedef struct private_x509_ocsp_response_t private_x509_ocsp_response_t;
+
+/**
+ * Private data of a ocsp_t object.
+ */
+struct private_x509_ocsp_response_t {
+ /**
+ * Public interface for this ocsp object.
+ */
+ x509_ocsp_response_t public;
+
+ /**
+ * complete encoded OCSP response
+ */
+ chunk_t data;
+
+ /**
+ * data for signature verficiation
+ */
+ chunk_t tbsResponseData;
+
+ /**
+ * signature algorithm (OID)
+ */
+ int signatureAlgorithm;
+
+ /**
+ * signature value
+ */
+ chunk_t signature;
+
+ /**
+ * name or keyid of the responder
+ */
+ identification_t *responderId;
+
+ /**
+ * time of response production
+ */
+ time_t producedAt;
+
+ /**
+ * list of included certificates
+ */
+ linked_list_t *certs;
+
+ /**
+ * Linked list of OCSP responses, single_response_t
+ */
+ linked_list_t *responses;
+
+ /**
+ * Nonce required for ocsp request and response
+ */
+ chunk_t nonce;
+
+ /**
+ * reference counter
+ */
+ refcount_t ref;
+};
+
+/**
+ * single response contained in OCSP response
+ */
+typedef struct {
+ /** hash algorithm OID to for the two hashes */
+ int hashAlgorithm;
+ /** hash of issuer DN */
+ chunk_t issuerNameHash;
+ /** issuerKeyID */
+ chunk_t issuerKeyHash;
+ /** serial number of certificate */
+ chunk_t serialNumber;
+ /** OCSP certificate status */
+ cert_validation_t status;
+ /** time of revocation, if revoked */
+ time_t revocationTime;
+ /** revocation reason, if revoked */
+ crl_reason_t revocationReason;
+ /** creation of associated CRL */
+ time_t thisUpdate;
+ /** creation of next CRL */
+ time_t nextUpdate;
+} single_response_t;
+
+/* our OCSP response version implementation */
+#define OCSP_BASIC_RESPONSE_VERSION 1
+
+/* some OCSP specific prefabricated ASN.1 constants */
+static u_char ASN1_nonce_oid_str[] = {
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02
+};
+
+static u_char ASN1_response_oid_str[] = {
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04
+};
+
+static u_char ASN1_response_content_str[] = {
+ 0x04, 0x0D,
+ 0x30, 0x0B,
+ 0x06, 0x09,
+ 0x2B, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
+};
+
+static const chunk_t ASN1_nonce_oid = chunk_from_buf(ASN1_nonce_oid_str);
+static const chunk_t ASN1_response_oid = chunk_from_buf(ASN1_response_oid_str);
+static const chunk_t ASN1_response_content = chunk_from_buf(ASN1_response_content_str);
+
+/* asn.1 definitions for parsing */
+
+static const asn1Object_t ocspResponseObjects[] = {
+ { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */
+ { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */
+ { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */
+ { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */
+ { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */
+ { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */
+};
+
+#define OCSP_RESPONSE_STATUS 1
+#define OCSP_RESPONSE_TYPE 4
+#define OCSP_RESPONSE 5
+#define OCSP_RESPONSE_ROOF 7
+
+static const asn1Object_t basicResponseObjects[] = {
+ { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */
+ { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE |
+ ASN1_DEF }, /* 2 */
+ { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */
+ { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */
+ { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */
+ { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */
+ { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */
+ { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */
+ { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */
+ { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */
+ { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */
+ { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */
+ { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */
+ { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */
+ { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */
+ { 5, "critical", ASN1_BOOLEAN, ASN1_BODY |
+ ASN1_DEF }, /* 16 */
+ { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */
+ { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */
+ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */
+ { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */
+ { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */
+ { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */
+ { 3, "certificate", ASN1_SEQUENCE, ASN1_RAW }, /* 24 */
+ { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */
+ { 1, "end opt", ASN1_EOC, ASN1_END } /* 26 */
+};
+
+#define BASIC_RESPONSE_TBS_DATA 1
+#define BASIC_RESPONSE_VERSION 3
+#define BASIC_RESPONSE_ID_BY_NAME 5
+#define BASIC_RESPONSE_ID_BY_KEY 8
+#define BASIC_RESPONSE_PRODUCED_AT 10
+#define BASIC_RESPONSE_RESPONSES 11
+#define BASIC_RESPONSE_EXT_ID 15
+#define BASIC_RESPONSE_CRITICAL 16
+#define BASIC_RESPONSE_EXT_VALUE 17
+#define BASIC_RESPONSE_ALGORITHM 20
+#define BASIC_RESPONSE_SIGNATURE 21
+#define BASIC_RESPONSE_CERTIFICATE 24
+#define BASIC_RESPONSE_ROOF 27
+
+static const asn1Object_t responsesObjects[] = {
+ { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */
+ { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */
+ { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */
+};
+
+#define RESPONSES_SINGLE_RESPONSE 1
+#define RESPONSES_ROOF 3
+
+static const asn1Object_t singleResponseObjects[] = {
+ { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */
+ { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */
+ { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */
+ { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */
+ { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */
+ { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */
+ { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */
+ { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */
+ { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */
+ { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */
+ { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */
+ { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */
+ { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */
+ { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */
+ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */
+ { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */
+ { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */
+ { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */
+ { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */
+ { 4, "critical", ASN1_BOOLEAN, ASN1_BODY |
+ ASN1_DEF }, /* 24 */
+ { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */
+ { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */
+ { 1, "end opt", ASN1_EOC, ASN1_END } /* 27 */
+};
+
+#define SINGLE_RESPONSE_ALGORITHM 2
+#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3
+#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4
+#define SINGLE_RESPONSE_SERIAL_NUMBER 5
+#define SINGLE_RESPONSE_CERT_STATUS_GOOD 6
+#define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8
+#define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME 9
+#define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON 11
+#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14
+#define SINGLE_RESPONSE_THIS_UPDATE 16
+#define SINGLE_RESPONSE_NEXT_UPDATE 18
+#define SINGLE_RESPONSE_EXT_ID 23
+#define SINGLE_RESPONSE_CRITICAL 24
+#define SINGLE_RESPONSE_EXT_VALUE 25
+#define SINGLE_RESPONSE_ROOF 28
+
+/**
+ * Implementaiton of ocsp_response_t.get_status
+ */
+static cert_validation_t get_status(private_x509_ocsp_response_t *this,
+ x509_t *subject, x509_t *issuer,
+ time_t *revocation_time,
+ crl_reason_t *revocation_reason,
+ time_t *this_update, time_t *next_update)
+{
+ enumerator_t *enumerator;
+ single_response_t *response;
+ cert_validation_t status = VALIDATION_FAILED;
+ certificate_t *issuercert = &issuer->interface;
+
+ enumerator = this->responses->create_enumerator(this->responses);
+ while (enumerator->enumerate(enumerator, &response))
+ {
+ hasher_t *hasher;
+ identification_t *id;
+ chunk_t hash;
+
+ /* check serial first, is cheaper */
+ if (!chunk_equals(subject->get_serial(subject), response->serialNumber))
+ {
+ continue;
+ }
+ /* check issuerKeyHash if available */
+ if (response->issuerKeyHash.ptr)
+ {
+ public_key_t *public;
+
+ public = issuercert->get_public_key(issuercert);
+ if (!public)
+ {
+ continue;
+ }
+ switch (response->hashAlgorithm)
+ { /* TODO: generic mapper function */
+ case OID_SHA1:
+ id = public->get_id(public, ID_PUBKEY_SHA1);
+ break;
+ default:
+ public->destroy(public);
+ continue;
+ }
+ if (!chunk_equals(response->issuerKeyHash, id->get_encoding(id)))
+ {
+ public->destroy(public);
+ continue;
+ }
+ public->destroy(public);
+ }
+ /* check issuerNameHash, if available */
+ else if (response->issuerNameHash.ptr)
+ {
+ hasher = lib->crypto->create_hasher(lib->crypto,
+ hasher_algorithm_from_oid(response->hashAlgorithm));
+ if (!hasher)
+ {
+ continue;
+ }
+ id = issuercert->get_subject(issuercert);
+ hasher->allocate_hash(hasher, id->get_encoding(id), &hash);
+ hasher->destroy(hasher);
+ if (!chunk_equals(hash, response->issuerNameHash))
+ {
+ continue;
+ }
+ }
+ else
+ {
+ continue;
+ }
+ /* got a match */
+ status = response->status;
+ *revocation_time = response->revocationTime;
+ *revocation_reason = response->revocationReason;
+ *this_update = response->thisUpdate;
+ *next_update = response->nextUpdate;
+
+ break;
+ }
+ enumerator->destroy(enumerator);
+ return status;
+}
+
+/**
+ * Implementation of ocsp_response_t.create_cert_enumerator.
+ */
+static enumerator_t* create_cert_enumerator(private_x509_ocsp_response_t *this)
+{
+ return this->certs->create_enumerator(this->certs);
+}
+
+/**
+ * parse a single OCSP response
+ */
+static bool parse_singleResponse(private_x509_ocsp_response_t *this,
+ chunk_t blob, int level0)
+{
+ u_int level;
+ asn1_ctx_t ctx;
+ chunk_t object;
+ int objectID = 0;
+ single_response_t *response;
+
+ response = malloc_thing(single_response_t);
+ response->hashAlgorithm = OID_UNKNOWN;
+ response->issuerNameHash = chunk_empty;
+ response->issuerKeyHash = chunk_empty;
+ response->serialNumber = chunk_empty;
+ response->status = VALIDATION_FAILED;
+ response->revocationTime = 0;
+ response->revocationReason = CRL_UNSPECIFIED;
+ response->thisUpdate = 0;
+ response->nextUpdate = 0;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < SINGLE_RESPONSE_ROOF)
+ {
+ if (!extract_object(singleResponseObjects, &objectID, &object, &level, &ctx))
+ {
+ free(response);
+ return FALSE;
+ }
+ switch (objectID)
+ {
+ case SINGLE_RESPONSE_ALGORITHM:
+ response->hashAlgorithm = parse_algorithmIdentifier(object, level+1, NULL);
+ break;
+ case SINGLE_RESPONSE_ISSUER_NAME_HASH:
+ response->issuerNameHash = object;
+ break;
+ case SINGLE_RESPONSE_ISSUER_KEY_HASH:
+ response->issuerKeyHash = object;
+ break;
+ case SINGLE_RESPONSE_SERIAL_NUMBER:
+ response->serialNumber = object;
+ break;
+ case SINGLE_RESPONSE_CERT_STATUS_GOOD:
+ response->status = VALIDATION_GOOD;
+ break;
+ case SINGLE_RESPONSE_CERT_STATUS_REVOKED:
+ response->status = VALIDATION_REVOKED;
+ break;
+ case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME:
+ response->revocationTime = asn1totime(&object, ASN1_GENERALIZEDTIME);
+ break;
+ case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON:
+ if (object.len == 1)
+ {
+ response->revocationReason = *object.ptr;
+ }
+ break;
+ case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN:
+ response->status = VALIDATION_FAILED;
+ break;
+ case SINGLE_RESPONSE_THIS_UPDATE:
+ response->thisUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME);
+ break;
+ case SINGLE_RESPONSE_NEXT_UPDATE:
+ response->nextUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME);
+ break;
+ }
+ objectID++;
+ }
+ this->responses->insert_last(this->responses, response);
+ return TRUE;
+}
+
+/**
+ * parse all contained responses
+ */
+static bool parse_responses(private_x509_ocsp_response_t *this,
+ chunk_t blob, int level0)
+{
+ u_int level;
+ asn1_ctx_t ctx;
+ chunk_t object;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < RESPONSES_ROOF)
+ {
+ if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ switch (objectID)
+ {
+ case RESPONSES_SINGLE_RESPONSE:
+ if (!parse_singleResponse(this, object, level+1))
+ {
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+/**
+ * parse a basicOCSPResponse
+ */
+static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this,
+ chunk_t blob, int level0)
+{
+ u_int level, version;
+ asn1_ctx_t ctx;
+ bool critical;
+ chunk_t object, responses = chunk_empty;
+ int objectID = 0;
+ int extn_oid = OID_UNKNOWN;
+ certificate_t *cert;
+
+ asn1_init(&ctx, blob, level0, FALSE, FALSE);
+ while (objectID < BASIC_RESPONSE_ROOF)
+ {
+ if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ switch (objectID)
+ {
+ case BASIC_RESPONSE_TBS_DATA:
+ this->tbsResponseData = object;
+ break;
+ case BASIC_RESPONSE_VERSION:
+ version = (object.len)? (1 + (u_int)*object.ptr) : 1;
+ if (version != OCSP_BASIC_RESPONSE_VERSION)
+ {
+ DBG1("OCSP ResponseData version %d not supported", version);
+ return FALSE;
+ }
+ break;
+ case BASIC_RESPONSE_ID_BY_NAME:
+ this->responderId = identification_create_from_encoding(
+ ID_DER_ASN1_DN, object);
+ DBG3(" %D", this->responderId);
+ break;
+ case BASIC_RESPONSE_ID_BY_KEY:
+ this->responderId = identification_create_from_encoding(
+ ID_PUBKEY_INFO_SHA1, object);
+ DBG3(" %D", this->responderId);
+ break;
+ case BASIC_RESPONSE_PRODUCED_AT:
+ this->producedAt = asn1totime(&object, ASN1_GENERALIZEDTIME);
+ break;
+ case BASIC_RESPONSE_RESPONSES:
+ responses = object;
+ break;
+ case BASIC_RESPONSE_EXT_ID:
+ extn_oid = known_oid(object);
+ break;
+ case BASIC_RESPONSE_CRITICAL:
+ critical = object.len && *object.ptr;
+ DBG3(" %s", critical ? "TRUE" : "FALSE");
+ break;
+ case BASIC_RESPONSE_EXT_VALUE:
+ if (extn_oid == OID_NONCE)
+ {
+ this->nonce = object;
+ }
+ break;
+ case BASIC_RESPONSE_ALGORITHM:
+ this->signatureAlgorithm = parse_algorithmIdentifier(
+ object, level+1, NULL);
+ break;
+ case BASIC_RESPONSE_SIGNATURE:
+ this->signature = object;
+ break;
+ case BASIC_RESPONSE_CERTIFICATE:
+ {
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,CERT_X509,
+ BUILD_BLOB_ASN1_DER, chunk_clone(object),
+ BUILD_END);
+ if (cert)
+ {
+ this->certs->insert_last(this->certs, cert);
+ }
+ break;
+ }
+ }
+ objectID++;
+ }
+ if (!this->responderId)
+ {
+ this->responderId = identification_create_from_encoding(ID_ANY, chunk_empty);
+ }
+ return parse_responses(this, responses, level + 1);
+}
+
+/**
+ * Parse OCSPResponse object
+ */
+static bool parse_OCSPResponse(private_x509_ocsp_response_t *this)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+ int responseType = OID_UNKNOWN;
+ ocsp_status_t status;
+
+ asn1_init(&ctx, this->data, 0, FALSE, FALSE);
+ while (objectID < OCSP_RESPONSE_ROOF)
+ {
+ if (!extract_object(ocspResponseObjects, &objectID, &object, &level, &ctx))
+ {
+ return FALSE;
+ }
+ switch (objectID)
+ {
+ case OCSP_RESPONSE_STATUS:
+ status = (ocsp_status_t)*object.ptr;
+ switch (status)
+ {
+ case OCSP_SUCCESSFUL:
+ break;
+ default:
+ DBG1("OCSP response status: %N",
+ ocsp_status_names, status);
+ return FALSE;
+ }
+ break;
+ case OCSP_RESPONSE_TYPE:
+ responseType = known_oid(object);
+ break;
+ case OCSP_RESPONSE:
+ switch (responseType)
+ {
+ case OID_BASIC:
+ return parse_basicOCSPResponse(this, object, level+1);
+ default:
+ DBG1("OCSP response type %#B not supported", &object);
+ return FALSE;
+ }
+ break;
+ }
+ objectID++;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.get_type
+ */
+static certificate_type_t get_type(private_x509_ocsp_response_t *this)
+{
+ return CERT_X509_OCSP_RESPONSE;
+}
+
+/**
+ * Implementation of certificate_t.get_issuer
+ */
+static identification_t* get_issuer(private_x509_ocsp_response_t *this)
+{
+ return this->responderId;
+}
+
+/**
+ * Implementation of certificate_t.has_subject.
+ */
+static id_match_t has_issuer(private_x509_ocsp_response_t *this,
+ identification_t *issuer)
+{
+ return this->responderId->matches(this->responderId, issuer);
+}
+
+/**
+ * Implementation of certificate_t.issued_by
+ */
+static bool issued_by(private_x509_ocsp_response_t *this, certificate_t *issuer,
+ bool sigcheck)
+{
+ public_key_t *key;
+ signature_scheme_t scheme;
+ bool valid;
+ x509_t *x509 = (x509_t*)issuer;
+
+ if (issuer->get_type(issuer) != CERT_X509)
+ {
+ return FALSE;
+ }
+ if (this->responderId->get_type(this->responderId) == ID_DER_ASN1_DN)
+ {
+ if (!this->responderId->equals(this->responderId,
+ issuer->get_subject(issuer)))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ bool equal;
+ public_key_t *public = issuer->get_public_key(issuer);
+
+ if (public == NULL)
+ {
+ return FALSE;
+ }
+ equal = this->responderId->equals(this->responderId,
+ public->get_id(public, ID_PUBKEY_SHA1));
+ public->destroy(public);
+ if (!equal)
+ {
+ return FALSE;
+ }
+ }
+ if (!(x509->get_flags(x509) & X509_OCSP_SIGNER))
+ {
+ return FALSE;
+ }
+ if (!sigcheck)
+ {
+ return TRUE;
+ }
+ /* TODO: generic OID to scheme mapper? */
+ switch (this->signatureAlgorithm)
+ {
+ case OID_MD5_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_MD5;
+ break;
+ case OID_SHA1_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
+ break;
+ case OID_SHA256_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA256;
+ break;
+ case OID_SHA384_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA384;
+ break;
+ case OID_SHA512_WITH_RSA:
+ scheme = SIGN_RSA_EMSA_PKCS1_SHA512;
+ break;
+ default:
+ return FALSE;
+ }
+ key = issuer->get_public_key(issuer);
+ if (key == NULL)
+ {
+ return FALSE;
+ }
+ valid = key->verify(key, scheme, this->tbsResponseData, this->signature);
+ key->destroy(key);
+ return valid;
+}
+
+/**
+ * Implementation of certificate_t.get_public_key
+ */
+static public_key_t* get_public_key(private_x509_ocsp_response_t *this)
+{
+ return NULL;
+}
+
+/**
+ * Implementation of x509_cert_t.get_validity.
+ */
+static bool get_validity(private_x509_ocsp_response_t *this, time_t *when,
+ time_t *not_before, time_t *not_after)
+{
+ time_t t;
+
+ if (when == NULL)
+ {
+ t = time(NULL);
+ }
+ else
+ {
+ t = *when;
+ }
+ if (not_before)
+ {
+ *not_before = this->producedAt;
+ }
+ if (not_after)
+ {
+ *not_after = ~0;
+ }
+ /* valid from produceAt up to infinity */
+ if (t >= this->producedAt)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.get_encoding.
+ */
+static chunk_t get_encoding(private_x509_ocsp_response_t *this)
+{
+ return chunk_clone(this->data);
+}
+
+/**
+ * Implementation of certificate_t.equals.
+ */
+static bool equals(private_x509_ocsp_response_t *this, certificate_t *other)
+{
+ if (this == (private_x509_ocsp_response_t*)other)
+ {
+ return TRUE;
+ }
+ if (other->get_type(other) != CERT_X509_OCSP_RESPONSE)
+ {
+ return FALSE;
+ }
+ /* check if we have the same X509 implementation */
+ if (other->equals == (void*)equals)
+ {
+ return chunk_equals(this->data,
+ ((private_x509_ocsp_response_t*)other)->data);
+ }
+ /* TODO: compare against other implementation */
+ return FALSE;
+}
+
+/**
+ * Implementation of certificate_t.get_ref
+ */
+static private_x509_ocsp_response_t* get_ref(private_x509_ocsp_response_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implements ocsp_t.destroy.
+ */
+static void destroy(private_x509_ocsp_response_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ this->certs->destroy_offset(this->certs, offsetof(certificate_t, destroy));
+ this->responses->destroy_function(this->responses, free);
+ DESTROY_IF(this->responderId);
+ free(this->data.ptr);
+ free(this);
+ }
+}
+
+/**
+ * load an OCSP response
+ */
+static x509_ocsp_response_t *load(chunk_t data)
+{
+ private_x509_ocsp_response_t *this;
+
+ this = malloc_thing(private_x509_ocsp_response_t);
+
+ this->public.interface.certificate.get_type = (certificate_type_t (*)(certificate_t *this))get_type;
+ this->public.interface.certificate.get_subject = (identification_t* (*)(certificate_t *this))get_issuer;
+ this->public.interface.certificate.get_issuer = (identification_t* (*)(certificate_t *this))get_issuer;
+ this->public.interface.certificate.has_subject = (id_match_t(*)(certificate_t*, identification_t *subject))has_issuer;
+ this->public.interface.certificate.has_issuer = (id_match_t(*)(certificate_t*, identification_t *issuer))has_issuer;
+ this->public.interface.certificate.issued_by = (bool (*)(certificate_t *this, certificate_t *issuer,bool))issued_by;
+ this->public.interface.certificate.get_public_key = (public_key_t* (*)(certificate_t *this))get_public_key;
+ this->public.interface.certificate.get_validity = (bool(*)(certificate_t*, time_t *when, time_t *, time_t*))get_validity;
+ this->public.interface.certificate.get_encoding = (chunk_t(*)(certificate_t*))get_encoding;
+ this->public.interface.certificate.equals = (bool(*)(certificate_t*, certificate_t *other))equals;
+ this->public.interface.certificate.get_ref = (certificate_t* (*)(certificate_t *this))get_ref;
+ this->public.interface.certificate.destroy = (void (*)(certificate_t *this))destroy;
+ this->public.interface.get_status = (cert_validation_t(*)(ocsp_response_t*, x509_t *subject, x509_t *issuer, time_t *revocation_time,crl_reason_t *revocation_reason,time_t *this_update, time_t *next_update))get_status;
+ this->public.interface.create_cert_enumerator = (enumerator_t*(*)(ocsp_response_t*))create_cert_enumerator;
+
+ this->ref = 1;
+ this->data = data;
+ this->tbsResponseData = chunk_empty;
+ this->responderId = NULL;
+ this->producedAt = UNDEFINED_TIME;
+ this->responses = linked_list_create();
+ this->nonce = chunk_empty;
+ this->signatureAlgorithm = OID_UNKNOWN;
+ this->signature = chunk_empty;
+ this->certs = linked_list_create();
+
+ if (!parse_OCSPResponse(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+ return &this->public;
+}
+
+
+typedef struct private_builder_t private_builder_t;
+/**
+ * Builder implementation for certificate loading
+ */
+struct private_builder_t {
+ /** implements the builder interface */
+ builder_t public;
+ /** loaded response */
+ x509_ocsp_response_t *res;
+};
+
+/**
+ * Implementation of builder_t.build
+ */
+static x509_ocsp_response_t *build(private_builder_t *this)
+{
+ x509_ocsp_response_t *res = this->res;
+
+ free(this);
+ return res;
+}
+
+/**
+ * Implementation of builder_t.add
+ */
+static void add(private_builder_t *this, builder_part_t part, ...)
+{
+ va_list args;
+
+ if (this->res)
+ {
+ DBG1("ignoring surplus build part %N", builder_part_names, part);
+ return;
+ }
+
+ switch (part)
+ {
+ case BUILD_BLOB_ASN1_DER:
+ {
+ va_start(args, part);
+ this->res = load(va_arg(args, chunk_t));
+ va_end(args);
+ break;
+ }
+ default:
+ DBG1("ignoring unsupported build part %N", builder_part_names, part);
+ break;
+ }
+}
+
+/**
+ * Builder construction function
+ */
+builder_t *x509_ocsp_response_builder(certificate_type_t type)
+{
+ private_builder_t *this;
+
+ if (type != CERT_X509_OCSP_RESPONSE)
+ {
+ return NULL;
+ }
+
+ this = malloc_thing(private_builder_t);
+
+ this->res = NULL;
+ this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
+ this->public.build = (void*(*)(builder_t *this))build;
+
+ return &this->public;
+}
+
diff --git a/src/libstrongswan/plugins/x509/x509_ocsp_response.h b/src/libstrongswan/plugins/x509/x509_ocsp_response.h
new file mode 100644
index 000000000..8b4c8328d
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_ocsp_response.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 x509_ocsp_response x509_ocsp_response
+ * @{ @ingroup x509_p
+ */
+
+#ifndef X509_OCSP_RESPONSE_H_
+#define X509_OCSP_RESPONSE_H_
+
+#include <credentials/certificates/ocsp_response.h>
+
+typedef struct x509_ocsp_response_t x509_ocsp_response_t;
+
+/**
+ * Implementation of ocsp_response_t using own ASN1 parser.
+ */
+struct x509_ocsp_response_t {
+
+ /**
+ * Implements the ocsp_response_t interface
+ */
+ ocsp_response_t interface;
+};
+
+/**
+ * Create the building facility for OCSP responses.
+ *
+ * @param type certificate type, CERT_X509_OCSP_RESPONSE only
+ * @return builder instance to build OCSP responses
+ */
+builder_t *x509_ocsp_response_builder(certificate_type_t type);
+
+#endif /* X509_OCSP_RESPONSE_H_ @}*/
diff --git a/src/libstrongswan/plugins/x509/x509_plugin.c b/src/libstrongswan/plugins/x509/x509_plugin.c
new file mode 100644
index 000000000..8ddef3bcd
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_plugin.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "x509_plugin.h"
+
+#include <library.h>
+#include "x509_cert.h"
+#include "x509_crl.h"
+#include "x509_ocsp_request.h"
+#include "x509_ocsp_response.h"
+
+typedef struct private_x509_plugin_t private_x509_plugin_t;
+
+/**
+ * private data of x509_plugin
+ */
+struct private_x509_plugin_t {
+
+ /**
+ * public functions
+ */
+ x509_plugin_t public;
+};
+
+/**
+ * Implementation of x509_plugin_t.x509troy
+ */
+static void destroy(private_x509_plugin_t *this)
+{
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)x509_cert_builder);
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)x509_crl_builder);
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)x509_ocsp_request_builder);
+ lib->creds->remove_builder(lib->creds,
+ (builder_constructor_t)x509_ocsp_response_builder);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ private_x509_plugin_t *this = malloc_thing(private_x509_plugin_t);
+
+ this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ (builder_constructor_t)x509_cert_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ (builder_constructor_t)x509_crl_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
+ (builder_constructor_t)x509_ocsp_request_builder);
+ lib->creds->add_builder(lib->creds, CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
+ (builder_constructor_t)x509_ocsp_response_builder);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/libstrongswan/plugins/x509/x509_plugin.h b/src/libstrongswan/plugins/x509/x509_plugin.h
new file mode 100644
index 000000000..9743a2367
--- /dev/null
+++ b/src/libstrongswan/plugins/x509/x509_plugin.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 x509_p x509
+ * @ingroup plugins
+ *
+ * @defgroup x509_plugin x509_plugin
+ * @{ @ingroup x509_p
+ */
+
+#ifndef X509_PLUGIN_H_
+#define X509_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct x509_plugin_t x509_plugin_t;
+
+/**
+ * Plugin implementing x509, CRL and OCSP certificates.
+ */
+struct x509_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a x509_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* X509_PLUGIN_H_ @}*/