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