diff options
-rw-r--r-- | .pc/.quilt_patches | 1 | ||||
-rw-r--r-- | .pc/.quilt_series | 1 | ||||
-rw-r--r-- | .pc/.version | 1 | ||||
-rw-r--r-- | .pc/applied-patches | 1 | ||||
-rw-r--r-- | .pc/snprintf-fix-4.4.0.patch/.timestamp | 0 | ||||
-rw-r--r-- | .pc/snprintf-fix-4.4.0.patch/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c | 533 | ||||
-rw-r--r-- | .pc/snprintf-fix-4.4.0.patch/src/libstrongswan/utils/identification.c | 1032 | ||||
-rw-r--r-- | .pc/snprintf-fix-4.4.0.patch/src/pluto/x509.c | 459 | ||||
-rw-r--r-- | debian/patches/snprintf-fix-4.4.0.patch | 48 | ||||
-rw-r--r-- | src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c | 11 | ||||
-rw-r--r-- | src/libstrongswan/utils/identification.c | 12 | ||||
-rw-r--r-- | src/pluto/x509.c | 4 |
12 files changed, 2075 insertions, 28 deletions
diff --git a/.pc/.quilt_patches b/.pc/.quilt_patches new file mode 100644 index 000000000..6857a8d44 --- /dev/null +++ b/.pc/.quilt_patches @@ -0,0 +1 @@ +debian/patches diff --git a/.pc/.quilt_series b/.pc/.quilt_series new file mode 100644 index 000000000..c2067066a --- /dev/null +++ b/.pc/.quilt_series @@ -0,0 +1 @@ +series diff --git a/.pc/.version b/.pc/.version new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/.pc/.version @@ -0,0 +1 @@ +2 diff --git a/.pc/applied-patches b/.pc/applied-patches new file mode 100644 index 000000000..326403814 --- /dev/null +++ b/.pc/applied-patches @@ -0,0 +1 @@ +snprintf-fix-4.4.0.patch diff --git a/.pc/snprintf-fix-4.4.0.patch/.timestamp b/.pc/snprintf-fix-4.4.0.patch/.timestamp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/.pc/snprintf-fix-4.4.0.patch/.timestamp diff --git a/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c b/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c new file mode 100644 index 000000000..ff3ddeb6f --- /dev/null +++ b/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2007-2009 Andreas Steffen + * + * HSR 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. + */ + +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <utils/linked_list.h> +#include <utils/lexparser.h> + +#include "ietf_attributes.h" + +/** + * Private definition of IETF attribute types + */ +typedef enum { + IETF_ATTRIBUTE_OCTETS = 0, + IETF_ATTRIBUTE_OID = 1, + IETF_ATTRIBUTE_STRING = 2 +} ietf_attribute_type_t; + +typedef struct ietf_attr_t ietf_attr_t; + +/** + * Private definition of an IETF attribute + */ +struct ietf_attr_t { + /** + * IETF attribute type + */ + ietf_attribute_type_t type; + + /** + * IETF attribute value + */ + chunk_t value; + + /** + * Compares two IETF attributes + * + * return -1 if this is earlier in the alphabet than other + * return 0 if this equals other + * return +1 if this is later in the alphabet than other + * + * @param other other object + */ + int (*compare) (ietf_attr_t *this, ietf_attr_t *other); + + /** + * Destroys an ietf_attr_t object. + */ + void (*destroy) (ietf_attr_t *this); +}; + +/** + * Implements ietf_attr_t.compare. + */ +static int ietf_attr_compare(ietf_attr_t *this, ietf_attr_t *other) +{ + int cmp_len, len, cmp_value; + + /* OID attributes are appended after STRING and OCTETS attributes */ + if (this->type != IETF_ATTRIBUTE_OID && other->type == IETF_ATTRIBUTE_OID) + { + return -1; + } + if (this->type == IETF_ATTRIBUTE_OID && other->type != IETF_ATTRIBUTE_OID) + { + return 1; + } + + cmp_len = this->value.len - other->value.len; + len = (cmp_len < 0) ? this->value.len : other->value.len; + cmp_value = memcmp(this->value.ptr, other->value.ptr, len); + + return (cmp_value == 0) ? cmp_len : cmp_value; +} + +/** + * Implements ietf_attr_t.destroy. + */ +static void ietf_attr_destroy(ietf_attr_t *this) +{ + free(this->value.ptr); + free(this); +} + +/** + * Creates an ietf_attr_t object. + */ +static ietf_attr_t* ietf_attr_create(ietf_attribute_type_t type, chunk_t value) +{ + ietf_attr_t *this = malloc_thing(ietf_attr_t); + + /* initialize */ + this->type = type; + this->value = chunk_clone(value); + + /* function */ + this->compare = ietf_attr_compare; + this->destroy = ietf_attr_destroy; + + return this; +} + +typedef struct private_ietf_attributes_t private_ietf_attributes_t; + +/** + * Private data of an ietf_attributes_t object. + */ +struct private_ietf_attributes_t { + /** + * Public interface. + */ + ietf_attributes_t public; + + /** + * Printable representation of the IETF attributes + */ + char *string; + + /** + * Linked list of IETF attributes. + */ + linked_list_t *list; + + /** + * reference count + */ + refcount_t ref; +}; + +/** + * Implementation of ietf_attributes_t.get_string. + */ +static char* get_string(private_ietf_attributes_t *this) +{ + if (this->string == NULL) + { + char buf[BUF_LEN]; + char *pos = buf; + int len = BUF_LEN; + bool first = TRUE; + ietf_attr_t *attr; + enumerator_t *enumerator; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &attr)) + { + int written = 0; + + if (first) + { + first = FALSE; + } + else + { + written = snprintf(pos, len, ", "); + pos += written; + len -= written; + } + + switch (attr->type) + { + case IETF_ATTRIBUTE_OCTETS: + case IETF_ATTRIBUTE_STRING: + written = snprintf(pos, len, "%.*s", (int)attr->value.len, + attr->value.ptr); + break; + case IETF_ATTRIBUTE_OID: + { + int oid = asn1_known_oid(attr->value); + + if (oid == OID_UNKNOWN) + { + written = snprintf(pos, len, "0x#B", &attr->value); + } + else + { + written = snprintf(pos, len, "%s", oid_names[oid]); + } + break; + } + default: + break; + } + pos += written; + len -= written; + } + enumerator->destroy(enumerator); + if (len < BUF_LEN) + { + this->string = strdup(buf); + } + } + return this->string; +} + +/** + * Implementation of ietf_attributes_t.get_encoding. + */ +static chunk_t get_encoding(private_ietf_attributes_t *this) +{ + chunk_t values; + size_t size = 0; + u_char *pos; + ietf_attr_t *attr; + enumerator_t *enumerator; + + /* precalculate the total size of all values */ + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &attr)) + { + size_t len = attr->value.len; + + size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len; + } + enumerator->destroy(enumerator); + + pos = asn1_build_object(&values, ASN1_SEQUENCE, size); + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &attr)) + { + chunk_t ietfAttribute; + asn1_t type = ASN1_NULL; + + switch (attr->type) + { + case IETF_ATTRIBUTE_OCTETS: + type = ASN1_OCTET_STRING; + break; + case IETF_ATTRIBUTE_STRING: + type = ASN1_UTF8STRING; + break; + case IETF_ATTRIBUTE_OID: + type = ASN1_OID; + break; + } + ietfAttribute = asn1_simple_object(type, attr->value); + + /* copy ietfAttribute into values chunk */ + memcpy(pos, ietfAttribute.ptr, ietfAttribute.len); + pos += ietfAttribute.len; + free(ietfAttribute.ptr); + } + enumerator->destroy(enumerator); + + return asn1_wrap(ASN1_SEQUENCE, "m", values); +} + +static bool equals(private_ietf_attributes_t *this, private_ietf_attributes_t *other) +{ + bool result = TRUE; + + /* lists must have the same number of attributes */ + if (other == NULL || + this->list->get_count(this->list) != other->list->get_count(other->list)) + { + return FALSE; + } + + /* compare two alphabetically-sorted lists */ + { + ietf_attr_t *attr_a, *attr_b; + enumerator_t *enum_a, *enum_b; + + enum_a = this->list->create_enumerator(this->list); + enum_b = other->list->create_enumerator(other->list); + while (enum_a->enumerate(enum_a, &attr_a) && + enum_b->enumerate(enum_b, &attr_b)) + { + if (attr_a->compare(attr_a, attr_b) != 0) + { + /* we have a mismatch */ + result = FALSE; + break; + } + } + enum_a->destroy(enum_a); + enum_b->destroy(enum_b); + } + return result; +} + +static bool matches(private_ietf_attributes_t *this, private_ietf_attributes_t *other) +{ + bool result = FALSE; + ietf_attr_t *attr_a, *attr_b; + enumerator_t *enum_a, *enum_b; + + /* always match if this->list does not contain any attributes */ + if (this->list->get_count(this->list) == 0) + { + return TRUE; + } + + /* never match if other->list does not contain any attributes */ + if (other == NULL || other->list->get_count(other->list) == 0) + { + return FALSE; + } + + /* get first attribute from both lists */ + enum_a = this->list->create_enumerator(this->list); + enum_a->enumerate(enum_a, &attr_a); + enum_b = other->list->create_enumerator(other->list); + enum_b->enumerate(enum_b, &attr_b); + + /* look for at least one common attribute */ + while (TRUE) + { + bool cmp = attr_a->compare(attr_a, attr_b); + + if (cmp == 0) + { + /* we have a match */ + result = TRUE; + break; + } + if (cmp == -1) + { + /* attr_a is earlier in the alphabet, get next attr_a */ + if (!enum_a->enumerate(enum_a, &attr_a)) + { + /* we have reached the end of enum_a */ + break; + } + } + else + { + /* attr_a is later in the alphabet, get next attr_b */ + if (!enum_b->enumerate(enum_b, &attr_b)) + { + /* we have reached the end of enum_b */ + break; + } + } + } + enum_a->destroy(enum_a); + enum_b->destroy(enum_b); + + return result; +} + +/** + * Implementation of ietf_attributes_t.get_ref + */ +static private_ietf_attributes_t* get_ref(private_ietf_attributes_t *this) +{ + ref_get(&this->ref); + return this; +} + +/** + * Implementation of ietf_attributes_t.destroy. + */ +static void destroy(private_ietf_attributes_t *this) +{ + if (ref_put(&this->ref)) + { + this->list->destroy_offset(this->list, offsetof(ietf_attr_t, destroy)); + free(this->string); + free(this); + } +} + +static private_ietf_attributes_t* create_empty(void) +{ + private_ietf_attributes_t *this = malloc_thing(private_ietf_attributes_t); + + this->public.get_string = (char* (*)(ietf_attributes_t*))get_string; + this->public.get_encoding = (chunk_t (*)(ietf_attributes_t*))get_encoding; + this->public.equals = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))equals; + this->public.matches = (bool (*)(ietf_attributes_t*,ietf_attributes_t*))matches; + this->public.get_ref = (ietf_attributes_t* (*)(ietf_attributes_t*))get_ref; + this->public.destroy = (void (*)(ietf_attributes_t*))destroy; + + this->list = linked_list_create(); + this->string = NULL; + this->ref = 1; + return this; +} + +/** + * Adds an ietf_attr_t object to a sorted linked list + */ +static void ietf_attributes_add(private_ietf_attributes_t *this, + ietf_attr_t *attr) +{ + ietf_attr_t *current_attr; + bool found = FALSE; + iterator_t *iterator; + + iterator = this->list->create_iterator(this->list, TRUE); + while (iterator->iterate(iterator, (void **)¤t_attr)) + { + int cmp = attr->compare(attr, current_attr); + + if (cmp > 0) + { + continue; + } + if (cmp == 0) + { + attr->destroy(attr); + } + else + { + iterator->insert_before(iterator, attr); + } + found = TRUE; + break; + } + iterator->destroy(iterator); + if (!found) + { + this->list->insert_last(this->list, attr); + } +} + +/* + * Described in header. + */ +ietf_attributes_t *ietf_attributes_create_from_string(char *string) +{ + private_ietf_attributes_t *this = create_empty(); + + chunk_t line = { string, strlen(string) }; + + while (eat_whitespace(&line)) + { + chunk_t group; + + /* extract the next comma-separated group attribute */ + if (!extract_token(&group, ',', &line)) + { + group = line; + line.len = 0; + } + + /* remove any trailing spaces */ + while (group.len > 0 && *(group.ptr + group.len - 1) == ' ') + { + group.len--; + } + + /* add the group attribute to the list */ + if (group.len > 0) + { + ietf_attr_t *attr = ietf_attr_create(IETF_ATTRIBUTE_STRING, group); + + ietf_attributes_add(this, attr); + } + } + + return &(this->public); +} + +/** + * ASN.1 definition of ietfAttrSyntax + */ +static const asn1Object_t ietfAttrSyntaxObjects[] = +{ + { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 1 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ + { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */ + { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT | + ASN1_BODY }, /* 4 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ + { 2, "oid", ASN1_OID, ASN1_OPT | + ASN1_BODY }, /* 6 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ + { 2, "string", ASN1_UTF8STRING, ASN1_OPT | + ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define IETF_ATTR_OCTETS 4 +#define IETF_ATTR_OID 6 +#define IETF_ATTR_STRING 8 + +/* + * Described in header. + */ +ietf_attributes_t *ietf_attributes_create_from_encoding(chunk_t encoded) +{ + private_ietf_attributes_t *this = create_empty(); + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(ietfAttrSyntaxObjects, encoded); + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case IETF_ATTR_OCTETS: + case IETF_ATTR_OID: + case IETF_ATTR_STRING: + { + ietf_attribute_type_t type; + ietf_attr_t *attr; + + type = (objectID - IETF_ATTR_OCTETS) / 2; + attr = ietf_attr_create(type, object); + ietf_attributes_add(this, attr); + } + break; + default: + break; + } + } + parser->destroy(parser); + + return &(this->public); +} + diff --git a/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/utils/identification.c b/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/utils/identification.c new file mode 100644 index 000000000..6a3c3936c --- /dev/null +++ b/.pc/snprintf-fix-4.4.0.patch/src/libstrongswan/utils/identification.c @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2005-2009 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#define _GNU_SOURCE +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdio.h> + +#include "identification.h" + +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <crypto/hashers/hasher.h> + +ENUM_BEGIN(id_match_names, ID_MATCH_NONE, ID_MATCH_MAX_WILDCARDS, + "MATCH_NONE", + "MATCH_ANY", + "MATCH_MAX_WILDCARDS"); +ENUM_NEXT(id_match_names, ID_MATCH_PERFECT, ID_MATCH_PERFECT, ID_MATCH_MAX_WILDCARDS, + "MATCH_PERFECT"); +ENUM_END(id_match_names, ID_MATCH_PERFECT); + +ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID, + "ID_ANY", + "ID_IPV4_ADDR", + "ID_FQDN", + "ID_RFC822_ADDR", + "ID_IPV4_ADDR_SUBNET", + "ID_IPV6_ADDR", + "ID_IPV6_ADDR_SUBNET", + "ID_IPV4_ADDR_RANGE", + "ID_IPV6_ADDR_RANGE", + "ID_DER_ASN1_DN", + "ID_DER_ASN1_GN", + "ID_KEY_ID"); +ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_MYID, ID_KEY_ID, + "ID_DER_ASN1_GN_URI" + "ID_IETF_ATTR_STRING" + "ID_MYID"); +ENUM_END(id_type_names, ID_MYID); + +/** + * coding of X.501 distinguished name + */ +typedef struct { + const u_char *name; + int oid; + u_char type; +} x501rdn_t; + +static const x501rdn_t x501rdns[] = { + {"ND", OID_NAME_DISTINGUISHER, ASN1_PRINTABLESTRING}, + {"UID", OID_PILOT_USERID, ASN1_PRINTABLESTRING}, + {"DC", OID_PILOT_DOMAIN_COMPONENT, ASN1_PRINTABLESTRING}, + {"CN", OID_COMMON_NAME, ASN1_PRINTABLESTRING}, + {"S", OID_SURNAME, ASN1_PRINTABLESTRING}, + {"SN", OID_SERIAL_NUMBER, ASN1_PRINTABLESTRING}, + {"serialNumber", OID_SERIAL_NUMBER, ASN1_PRINTABLESTRING}, + {"C", OID_COUNTRY, ASN1_PRINTABLESTRING}, + {"L", OID_LOCALITY, ASN1_PRINTABLESTRING}, + {"ST", OID_STATE_OR_PROVINCE, ASN1_PRINTABLESTRING}, + {"O", OID_ORGANIZATION, ASN1_PRINTABLESTRING}, + {"OU", OID_ORGANIZATION_UNIT, ASN1_PRINTABLESTRING}, + {"T", OID_TITLE, ASN1_PRINTABLESTRING}, + {"D", OID_DESCRIPTION, ASN1_PRINTABLESTRING}, + {"N", OID_NAME, ASN1_PRINTABLESTRING}, + {"G", OID_GIVEN_NAME, ASN1_PRINTABLESTRING}, + {"I", OID_INITIALS, ASN1_PRINTABLESTRING}, + {"ID", OID_UNIQUE_IDENTIFIER, ASN1_PRINTABLESTRING}, + {"EN", OID_EMPLOYEE_NUMBER, ASN1_PRINTABLESTRING}, + {"employeeNumber", OID_EMPLOYEE_NUMBER, ASN1_PRINTABLESTRING}, + {"E", OID_EMAIL_ADDRESS, ASN1_IA5STRING}, + {"Email", OID_EMAIL_ADDRESS, ASN1_IA5STRING}, + {"emailAddress", OID_EMAIL_ADDRESS, ASN1_IA5STRING}, + {"UN", OID_UNSTRUCTURED_NAME, ASN1_IA5STRING}, + {"unstructuredName",OID_UNSTRUCTURED_NAME, ASN1_IA5STRING}, + {"TCGID", OID_TCGID, ASN1_PRINTABLESTRING} +}; + +/** + * maximum number of RDNs in atodn() + */ +#define RDN_MAX 20 + + +typedef struct private_identification_t private_identification_t; + +/** + * Private data of an identification_t object. + */ +struct private_identification_t { + /** + * Public interface. + */ + identification_t public; + + /** + * Encoded representation of this ID. + */ + chunk_t encoded; + + /** + * Type of this ID. + */ + id_type_t type; +}; + +/** + * Enumerator over RDNs + */ +typedef struct { + /* implements enumerator interface */ + enumerator_t public; + /* next set to parse, if any */ + chunk_t sets; + /* next sequence in set, if any */ + chunk_t seqs; +} rdn_enumerator_t; + +METHOD(enumerator_t, rdn_enumerate, bool, + rdn_enumerator_t *this, chunk_t *oid, u_char *type, chunk_t *data) +{ + chunk_t rdn; + + /* a DN contains one or more SET, each containing one or more SEQUENCES, + * each containing a OID/value RDN */ + if (!this->seqs.len) + { + /* no SEQUENCEs in current SET, parse next SET */ + if (asn1_unwrap(&this->sets, &this->seqs) != ASN1_SET) + { + return FALSE; + } + } + if (asn1_unwrap(&this->seqs, &rdn) == ASN1_SEQUENCE && + asn1_unwrap(&rdn, oid) == ASN1_OID) + { + int t = asn1_unwrap(&rdn, data); + + if (t != ASN1_INVALID) + { + *type = t; + return TRUE; + } + } + return FALSE; +} + +/** + * Create an enumerator over all RDNs (oid, string type, data) of a DN + */ +static enumerator_t* create_rdn_enumerator(chunk_t dn) +{ + rdn_enumerator_t *e; + + INIT(e, + .public = { + .enumerate = (void*)_rdn_enumerate, + .destroy = (void*)free, + }, + ); + + /* a DN is a SEQUENCE, get the first SET of it */ + if (asn1_unwrap(&dn, &e->sets) == ASN1_SEQUENCE) + { + e->seqs = chunk_empty; + return &e->public; + } + free(e); + return enumerator_create_empty(); +} + +/** + * Part enumerator over RDNs + */ +typedef struct { + /* implements enumerator interface */ + enumerator_t public; + /* inner RDN enumerator */ + enumerator_t *inner; +} rdn_part_enumerator_t; + +METHOD(enumerator_t, rdn_part_enumerate, bool, + rdn_part_enumerator_t *this, id_part_t *type, chunk_t *data) +{ + int i, known_oid, strtype; + chunk_t oid, inner_data; + static const struct { + int oid; + id_part_t type; + } oid2part[] = { + {OID_COMMON_NAME, ID_PART_RDN_CN}, + {OID_SURNAME, ID_PART_RDN_S}, + {OID_SERIAL_NUMBER, ID_PART_RDN_SN}, + {OID_COUNTRY, ID_PART_RDN_C}, + {OID_LOCALITY, ID_PART_RDN_L}, + {OID_STATE_OR_PROVINCE, ID_PART_RDN_ST}, + {OID_ORGANIZATION, ID_PART_RDN_O}, + {OID_ORGANIZATION_UNIT, ID_PART_RDN_OU}, + {OID_TITLE, ID_PART_RDN_T}, + {OID_DESCRIPTION, ID_PART_RDN_D}, + {OID_NAME, ID_PART_RDN_N}, + {OID_GIVEN_NAME, ID_PART_RDN_G}, + {OID_INITIALS, ID_PART_RDN_I}, + {OID_UNIQUE_IDENTIFIER, ID_PART_RDN_ID}, + {OID_EMAIL_ADDRESS, ID_PART_RDN_E}, + {OID_EMPLOYEE_NUMBER, ID_PART_RDN_EN}, + }; + + while (this->inner->enumerate(this->inner, &oid, &strtype, &inner_data)) + { + known_oid = asn1_known_oid(oid); + for (i = 0; i < countof(oid2part); i++) + { + if (oid2part[i].oid == known_oid) + { + *type = oid2part[i].type; + *data = inner_data; + return TRUE; + } + } + } + return FALSE; +} + +METHOD(enumerator_t, rdn_part_enumerator_destroy, void, + rdn_part_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +METHOD(identification_t, create_part_enumerator, enumerator_t*, + private_identification_t *this) +{ + switch (this->type) + { + case ID_DER_ASN1_DN: + { + rdn_part_enumerator_t *e; + + INIT(e, + .inner = create_rdn_enumerator(this->encoded), + .public = { + .enumerate = (void*)_rdn_part_enumerate, + .destroy = _rdn_part_enumerator_destroy, + }, + ); + return &e->public; + } + case ID_RFC822_ADDR: + /* TODO */ + case ID_FQDN: + /* TODO */ + default: + return enumerator_create_empty(); + } +} + +/** + * Print a DN with all its RDN in a buffer to present it to the user + */ +static void dntoa(chunk_t dn, char *buf, size_t len) +{ + enumerator_t *e; + chunk_t oid_data, data, printable; + u_char type; + int oid, written; + bool finished = FALSE; + + e = create_rdn_enumerator(dn); + while (e->enumerate(e, &oid_data, &type, &data)) + { + oid = asn1_known_oid(oid_data); + + if (oid == OID_UNKNOWN) + { + written = snprintf(buf, len, "%#B=", &oid_data); + } + else + { + written = snprintf(buf, len,"%s=", oid_names[oid].name); + } + buf += written; + len -= written; + + chunk_printable(data, &printable, '?'); + written = snprintf(buf, len, "%.*s", printable.len, printable.ptr); + chunk_free(&printable); + buf += written; + len -= written; + + if (data.ptr + data.len != dn.ptr + dn.len) + { + written = snprintf(buf, len, ", "); + buf += written; + len -= written; + } + else + { + finished = TRUE; + break; + } + } + if (!finished) + { + snprintf(buf, len, "(invalid ID_DER_ASN1_DN)"); + } + e->destroy(e); +} + +/** + * Converts an LDAP-style human-readable ASCII-encoded + * ASN.1 distinguished name into binary DER-encoded format + */ +static status_t atodn(char *src, chunk_t *dn) +{ + /* finite state machine for atodn */ + typedef enum { + SEARCH_OID = 0, + READ_OID = 1, + SEARCH_NAME = 2, + READ_NAME = 3, + UNKNOWN_OID = 4 + } state_t; + + chunk_t oid = chunk_empty; + chunk_t name = chunk_empty; + chunk_t rdns[RDN_MAX]; + int rdn_count = 0; + int dn_len = 0; + int whitespace = 0; + int i = 0; + asn1_t rdn_type; + state_t state = SEARCH_OID; + status_t status = SUCCESS; + + do + { + switch (state) + { + case SEARCH_OID: + if (*src != ' ' && *src != '/' && *src != ',') + { + oid.ptr = src; + oid.len = 1; + state = READ_OID; + } + break; + case READ_OID: + if (*src != ' ' && *src != '=') + { + oid.len++; + } + else + { + bool found = FALSE; + + for (i = 0; i < countof(x501rdns); i++) + { + if (strlen(x501rdns[i].name) == oid.len && + strncasecmp(x501rdns[i].name, oid.ptr, oid.len) == 0) + { + found = TRUE; + break; + } + } + if (!found) + { + status = NOT_SUPPORTED; + state = UNKNOWN_OID; + break; + } + /* reset oid and change state */ + oid = chunk_empty; + state = SEARCH_NAME; + } + break; + case SEARCH_NAME: + if (*src != ' ' && *src != '=') + { + name.ptr = src; + name.len = 1; + whitespace = 0; + state = READ_NAME; + } + break; + case READ_NAME: + if (*src != ',' && *src != '/' && *src != '\0') + { + name.len++; + if (*src == ' ') + whitespace++; + else + whitespace = 0; + } + else + { + name.len -= whitespace; + rdn_type = (x501rdns[i].type == ASN1_PRINTABLESTRING + && !asn1_is_printablestring(name)) + ? ASN1_T61STRING : x501rdns[i].type; + + if (rdn_count < RDN_MAX) + { + chunk_t rdn_oid; + + rdn_oid = asn1_build_known_oid(x501rdns[i].oid); + if (rdn_oid.len) + { + rdns[rdn_count] = + asn1_wrap(ASN1_SET, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + rdn_oid, + asn1_wrap(rdn_type, "c", name) + ) + ); + dn_len += rdns[rdn_count++].len; + } + else + { + status = INVALID_ARG; + } + } + else + { + status = OUT_OF_RES; + } + /* reset name and change state */ + name = chunk_empty; + state = SEARCH_OID; + } + break; + case UNKNOWN_OID: + break; + } + } while (*src++ != '\0'); + + /* build the distinguished name sequence */ + { + int i; + u_char *pos = asn1_build_object(dn, ASN1_SEQUENCE, dn_len); + + for (i = 0; i < rdn_count; i++) + { + memcpy(pos, rdns[i].ptr, rdns[i].len); + pos += rdns[i].len; + free(rdns[i].ptr); + } + } + + if (status != SUCCESS) + { + free(dn->ptr); + *dn = chunk_empty; + } + return status; +} + +METHOD(identification_t, get_encoding, chunk_t, + private_identification_t *this) +{ + return this->encoded; +} + +METHOD(identification_t, get_type, id_type_t, + private_identification_t *this) +{ + return this->type; +} + +METHOD(identification_t, contains_wildcards_dn, bool, + private_identification_t *this) +{ + enumerator_t *enumerator; + bool contains = FALSE; + id_part_t type; + chunk_t data; + + enumerator = create_part_enumerator(this); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (data.len == 1 && data.ptr[0] == '*') + { + contains = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return contains; +} + +METHOD(identification_t, contains_wildcards_memchr, bool, + private_identification_t *this) +{ + return memchr(this->encoded.ptr, '*', this->encoded.len) != NULL; +} + +METHOD(identification_t, equals_binary, bool, + private_identification_t *this, identification_t *other) +{ + if (this->type == other->get_type(other)) + { + if (this->type == ID_ANY) + { + return TRUE; + } + return chunk_equals(this->encoded, other->get_encoding(other)); + } + return FALSE; +} + +/** + * Compare to DNs, for equality if wc == NULL, for match otherwise + */ +static bool compare_dn(chunk_t t_dn, chunk_t o_dn, int *wc) +{ + enumerator_t *t, *o; + chunk_t t_oid, o_oid, t_data, o_data; + u_char t_type, o_type; + bool t_next, o_next, finished = FALSE; + + if (wc) + { + *wc = 0; + } + else + { + if (t_dn.len != o_dn.len) + { + return FALSE; + } + } + /* try a binary compare */ + if (memeq(t_dn.ptr, o_dn.ptr, t_dn.len)) + { + return TRUE; + } + + t = create_rdn_enumerator(t_dn); + o = create_rdn_enumerator(o_dn); + while (TRUE) + { + t_next = t->enumerate(t, &t_oid, &t_type, &t_data); + o_next = o->enumerate(o, &o_oid, &o_type, &o_data); + + if (!o_next && !t_next) + { + break; + } + finished = FALSE; + if (o_next != t_next) + { + break; + } + if (!chunk_equals(t_oid, o_oid)) + { + break; + } + if (wc && o_data.len == 1 && o_data.ptr[0] == '*') + { + (*wc)++; + } + else + { + if (t_data.len != o_data.len) + { + break; + } + if (t_type == o_type && + (t_type == ASN1_PRINTABLESTRING || + (t_type == ASN1_IA5STRING && + asn1_known_oid(t_oid) == OID_EMAIL_ADDRESS))) + { /* ignore case for printableStrings and email RDNs */ + if (strncasecmp(t_data.ptr, o_data.ptr, t_data.len) != 0) + { + break; + } + } + else + { /* respect case and length for everything else */ + if (!memeq(t_data.ptr, o_data.ptr, t_data.len)) + { + break; + } + } + } + /* the enumerator returns FALSE on parse error, we are finished + * if we have reached the end of the DN only */ + if ((t_data.ptr + t_data.len == t_dn.ptr + t_dn.len) && + (o_data.ptr + o_data.len == o_dn.ptr + o_dn.len)) + { + finished = TRUE; + } + } + t->destroy(t); + o->destroy(o); + return finished; +} + +METHOD(identification_t, equals_dn, bool, + private_identification_t *this, identification_t *other) +{ + return compare_dn(this->encoded, other->get_encoding(other), NULL); +} + +METHOD(identification_t, equals_strcasecmp, bool, + private_identification_t *this, identification_t *other) +{ + chunk_t encoded = other->get_encoding(other); + + /* we do some extra sanity checks to check for invalid IDs with a + * terminating null in it. */ + if (this->encoded.len == encoded.len && + memchr(this->encoded.ptr, 0, this->encoded.len) == NULL && + memchr(encoded.ptr, 0, encoded.len) == NULL && + strncasecmp(this->encoded.ptr, encoded.ptr, this->encoded.len) == 0) + { + return TRUE; + } + return FALSE; +} + +METHOD(identification_t, matches_binary, id_match_t, + private_identification_t *this, identification_t *other) +{ + if (other->get_type(other) == ID_ANY) + { + return ID_MATCH_ANY; + } + if (this->type == other->get_type(other) && + chunk_equals(this->encoded, other->get_encoding(other))) + { + return ID_MATCH_PERFECT; + } + return ID_MATCH_NONE; +} + +METHOD(identification_t, matches_string, id_match_t, + private_identification_t *this, identification_t *other) +{ + chunk_t encoded = other->get_encoding(other); + u_int len = encoded.len; + + if (other->get_type(other) == ID_ANY) + { + return ID_MATCH_ANY; + } + if (this->type != other->get_type(other)) + { + return ID_MATCH_NONE; + } + /* try a equals check first */ + if (equals_strcasecmp(this, other)) + { + return ID_MATCH_PERFECT; + } + if (len == 0 || this->encoded.len < len) + { + return ID_MATCH_NONE; + } + + /* check for single wildcard at the head of the string */ + if (*encoded.ptr == '*') + { + /* single asterisk matches any string */ + if (len-- == 1) + { /* not better than ID_ANY */ + return ID_MATCH_ANY; + } + if (strncasecmp(this->encoded.ptr + this->encoded.len - len, + encoded.ptr + 1, len) == 0) + { + return ID_MATCH_ONE_WILDCARD; + } + } + return ID_MATCH_NONE; +} + +METHOD(identification_t, matches_any, id_match_t, + private_identification_t *this, identification_t *other) +{ + if (other->get_type(other) == ID_ANY) + { + return ID_MATCH_ANY; + } + return ID_MATCH_NONE; +} + +METHOD(identification_t, matches_dn, id_match_t, + private_identification_t *this, identification_t *other) +{ + int wc; + + if (other->get_type(other) == ID_ANY) + { + return ID_MATCH_ANY; + } + + if (this->type == other->get_type(other)) + { + if (compare_dn(this->encoded, other->get_encoding(other), &wc)) + { + wc = min(wc, ID_MATCH_ONE_WILDCARD - ID_MATCH_MAX_WILDCARDS); + return ID_MATCH_PERFECT - wc; + } + } + return ID_MATCH_NONE; +} + +/** + * Described in header. + */ +int identification_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, + const void *const *args) +{ + private_identification_t *this = *((private_identification_t**)(args[0])); + chunk_t proper; + char buf[512]; + + if (this == NULL) + { + return print_in_hook(dst, len, "%*s", spec->width, "(null)"); + } + + switch (this->type) + { + case ID_ANY: + snprintf(buf, sizeof(buf), "%%any"); + break; + case ID_IPV4_ADDR: + if (this->encoded.len < sizeof(struct in_addr) || + inet_ntop(AF_INET, this->encoded.ptr, buf, sizeof(buf)) == NULL) + { + snprintf(buf, sizeof(buf), "(invalid ID_IPV4_ADDR)"); + } + break; + case ID_IPV6_ADDR: + if (this->encoded.len < sizeof(struct in6_addr) || + inet_ntop(AF_INET6, this->encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) + { + snprintf(buf, sizeof(buf), "(invalid ID_IPV6_ADDR)"); + } + break; + case ID_FQDN: + case ID_RFC822_ADDR: + case ID_DER_ASN1_GN_URI: + case ID_IETF_ATTR_STRING: + chunk_printable(this->encoded, &proper, '?'); + snprintf(buf, sizeof(buf), "%.*s", proper.len, proper.ptr); + chunk_free(&proper); + break; + case ID_DER_ASN1_DN: + dntoa(this->encoded, buf, sizeof(buf)); + break; + case ID_DER_ASN1_GN: + snprintf(buf, sizeof(buf), "(ASN.1 general Name"); + break; + case ID_KEY_ID: + if (chunk_printable(this->encoded, NULL, '?') && + this->encoded.len != HASH_SIZE_SHA1) + { /* fully printable, use ascii version */ + snprintf(buf, sizeof(buf), "%.*s", + this->encoded.len, this->encoded.ptr); + } + else + { /* not printable, hex dump */ + snprintf(buf, sizeof(buf), "%#B", &this->encoded); + } + break; + case ID_MYID: + snprintf(buf, sizeof(buf), "%%myid"); + break; + default: + snprintf(buf, sizeof(buf), "(unknown ID type: %d)", this->type); + break; + } + if (spec->minus) + { + return print_in_hook(dst, len, "%-*s", spec->width, buf); + } + return print_in_hook(dst, len, "%*s", spec->width, buf); +} + +METHOD(identification_t, clone_, identification_t*, + private_identification_t *this) +{ + private_identification_t *clone = malloc_thing(private_identification_t); + + memcpy(clone, this, sizeof(private_identification_t)); + if (this->encoded.len) + { + clone->encoded = chunk_clone(this->encoded); + } + return &clone->public; +} + +METHOD(identification_t, destroy, void, + private_identification_t *this) +{ + chunk_free(&this->encoded); + free(this); +} + +/** + * Generic constructor used for the other constructors. + */ +static private_identification_t *identification_create(id_type_t type) +{ + private_identification_t *this; + + INIT(this, + .public = { + .get_encoding = _get_encoding, + .get_type = _get_type, + .create_part_enumerator = _create_part_enumerator, + .clone = _clone_, + .destroy = _destroy, + }, + .type = type, + ); + + switch (type) + { + case ID_ANY: + this->public.matches = _matches_any; + this->public.equals = _equals_binary; + this->public.contains_wildcards = return_true; + break; + case ID_FQDN: + case ID_RFC822_ADDR: + this->public.matches = _matches_string; + this->public.equals = _equals_strcasecmp; + this->public.contains_wildcards = _contains_wildcards_memchr; + break; + case ID_DER_ASN1_DN: + this->public.equals = _equals_dn; + this->public.matches = _matches_dn; + this->public.contains_wildcards = _contains_wildcards_dn; + break; + default: + this->public.equals = _equals_binary; + this->public.matches = _matches_binary; + this->public.contains_wildcards = return_false; + break; + } + return this; +} + +/* + * Described in header. + */ +identification_t *identification_create_from_string(char *string) +{ + private_identification_t *this; + chunk_t encoded; + + if (string == NULL) + { + string = "%any"; + } + if (strchr(string, '=') != NULL) + { + /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN. + * convert from LDAP style or openssl x509 -subject style to ASN.1 DN + */ + if (atodn(string, &encoded) == SUCCESS) + { + this = identification_create(ID_DER_ASN1_DN); + this->encoded = encoded; + } + else + { + this = identification_create(ID_KEY_ID); + this->encoded = chunk_clone(chunk_create(string, strlen(string))); + } + return &this->public; + } + else if (strchr(string, '@') == NULL) + { + if (streq(string, "%any") + || streq(string, "%any6") + || streq(string, "0.0.0.0") + || streq(string, "*") + || streq(string, "::") + || streq(string, "0::0")) + { + /* any ID will be accepted */ + this = identification_create(ID_ANY); + return &this->public; + } + else + { + if (strchr(string, ':') == NULL) + { + struct in_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET, string, &address) > 0) + { /* is IPv4 */ + this = identification_create(ID_IPV4_ADDR); + this->encoded = chunk_clone(chunk); + } + else + { /* not IPv4, mostly FQDN */ + this = identification_create(ID_FQDN); + this->encoded = chunk_create(strdup(string), strlen(string)); + } + return &this->public; + } + else + { + struct in6_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET6, string, &address) > 0) + { /* is IPv6 */ + this = identification_create(ID_IPV6_ADDR); + this->encoded = chunk_clone(chunk); + } + else + { /* not IPv4/6 fallback to KEY_ID */ + this = identification_create(ID_KEY_ID); + this->encoded = chunk_create(strdup(string), strlen(string)); + } + return &this->public; + } + } + } + else + { + if (*string == '@') + { + if (*(string + 1) == '#') + { + this = identification_create(ID_KEY_ID); + string += 2; + this->encoded = chunk_from_hex( + chunk_create(string, strlen(string)), NULL); + return &this->public; + } + else + { + this = identification_create(ID_FQDN); + string += 1; + this->encoded = chunk_create(strdup(string), strlen(string)); + return &this->public; + } + } + else + { + this = identification_create(ID_RFC822_ADDR); + this->encoded = chunk_create(strdup(string), strlen(string)); + return &this->public; + } + } +} + +/* + * Described in header. + */ +identification_t * identification_create_from_data(chunk_t data) +{ + char buf[data.len + 1]; + + /* use string constructor */ + snprintf(buf, sizeof(buf), "%.*s", data.len, data.ptr); + return identification_create_from_string(buf); +} + +/* + * Described in header. + */ +identification_t *identification_create_from_encoding(id_type_t type, + chunk_t encoded) +{ + private_identification_t *this = identification_create(type); + + /* apply encoded chunk */ + if (type != ID_ANY) + { + this->encoded = chunk_clone(encoded); + } + return &(this->public); +} + +/* + * Described in header. + */ +identification_t *identification_create_from_sockaddr(sockaddr_t *sockaddr) +{ + switch (sockaddr->sa_family) + { + case AF_INET: + { + struct in_addr *addr = &(((struct sockaddr_in*)sockaddr)->sin_addr); + + return identification_create_from_encoding(ID_IPV4_ADDR, + chunk_create((u_char*)addr, sizeof(struct in_addr))); + } + case AF_INET6: + { + struct in6_addr *addr = &(((struct sockaddr_in6*)sockaddr)->sin6_addr); + + return identification_create_from_encoding(ID_IPV6_ADDR, + chunk_create((u_char*)addr, sizeof(struct in6_addr))); + } + default: + { + private_identification_t *this = identification_create(ID_ANY); + + return &(this->public); + } + } +} + diff --git a/.pc/snprintf-fix-4.4.0.patch/src/pluto/x509.c b/.pc/snprintf-fix-4.4.0.patch/src/pluto/x509.c new file mode 100644 index 000000000..0a29830ea --- /dev/null +++ b/.pc/snprintf-fix-4.4.0.patch/src/pluto/x509.c @@ -0,0 +1,459 @@ +/* Support of X.509 certificates + * Copyright (C) 2000 Andreas Hess, Patric Lichtsteiner, Roger Wegmann + * Copyright (C) 2001 Marco Bertossa, Andreas Schleiss + * Copyright (C) 2002 Mario Strasser + * Copyright (C) 2000-2009 Andreas Steffen - 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <time.h> +#include <sys/types.h> + +#include <freeswan.h> + +#include <asn1/asn1.h> +#include <crypto/hashers/hasher.h> +#include <utils/enumerator.h> +#include <utils/identification.h> + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "certs.h" +#include "keys.h" +#include "whack.h" +#include "fetch.h" +#include "ocsp.h" + +/** + * Check for equality between two key identifiers + */ +bool same_keyid(chunk_t a, chunk_t b) +{ + if (a.ptr == NULL || b.ptr == NULL) + { + return FALSE; + } + return chunk_equals(a, b); +} + +/** + * Stores a chained list of end certs and CA certs + */ +void store_x509certs(linked_list_t *certs, bool strict) +{ + cert_t *x509cert, *cacerts = NULL; + certificate_t *cert; + enumerator_t *enumerator; + + /* first extract CA certs, ignoring self-signed root CA certs */ + + enumerator = certs->create_enumerator(certs); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + x509_flag_t flags; + + flags = x509->get_flags(x509); + if (flags & X509_CA) + { + /* we don't accept self-signed CA certs */ + if (flags & X509_SELF_SIGNED) + { + plog("self-signed cacert rejected"); + } + else + { + /* insertion into temporary chain of candidate CA certs */ + x509cert = malloc_thing(cert_t); + *x509cert = cert_empty; + x509cert->cert = cert->get_ref(cert); + x509cert->next = cacerts; + cacerts = x509cert; + } + } + } + enumerator->destroy(enumerator); + + /* now verify the candidate CA certs */ + + while (cacerts) + { + cert_t *cert = cacerts; + + cacerts = cacerts->next; + + if (trust_authcert_candidate(cert, cacerts)) + { + add_authcert(cert, X509_CA); + } + else + { + plog("intermediate cacert rejected"); + cert_free(cert); + } + } + + /* now verify the end certificates */ + + enumerator = certs->create_enumerator(certs); + while (enumerator->enumerate(enumerator, &cert)) + { + time_t valid_until; + x509_t *x509 = (x509_t*)cert; + + if (!(x509->get_flags(x509) & X509_CA)) + { + x509cert = malloc_thing(cert_t); + *x509cert = cert_empty; + x509cert->cert = cert->get_ref(cert); + + if (verify_x509cert(x509cert, strict, &valid_until)) + { + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log("public key validated") + ) + add_public_key_from_cert(x509cert, valid_until, DAL_SIGNED); + } + else + { + plog("X.509 certificate rejected"); + cert_free(x509cert); + } + } + } + enumerator->destroy(enumerator); +} + +/** + * Check if a signature over binary blob is genuine + */ +bool x509_check_signature(chunk_t tbs, chunk_t sig, int algorithm, + certificate_t *issuer_cert) +{ + bool success; + public_key_t *key; + signature_scheme_t scheme; + + scheme = signature_scheme_from_oid(algorithm); + if (scheme == SIGN_UNKNOWN) + { + return FALSE; + } + + key = issuer_cert->get_public_key(issuer_cert); + if (key == NULL) + { + return FALSE; + } + success = key->verify(key, scheme, tbs, sig); + key->destroy(key); + + return success; +} + +/** + * Build an ASN.1 encoded PKCS#1 signature over a binary blob + */ +chunk_t x509_build_signature(chunk_t tbs, int algorithm, private_key_t *key, + bool bit_string) +{ + chunk_t signature; + signature_scheme_t scheme = signature_scheme_from_oid(algorithm); + + if (scheme == SIGN_UNKNOWN || !key->sign(key, scheme, tbs, &signature)) + { + return chunk_empty; + } + return (bit_string) ? asn1_bitstring("m", signature) + : asn1_wrap(ASN1_OCTET_STRING, "m", signature); +} + +/** + * Verifies a X.509 certificate + */ +bool verify_x509cert(cert_t *cert, bool strict, time_t *until) +{ + int pathlen, pathlen_constraint; + + *until = 0; + + for (pathlen = -1; pathlen <= X509_MAX_PATH_LEN; pathlen++) + { + certificate_t *certificate = cert->cert; + identification_t *subject = certificate->get_subject(certificate); + identification_t *issuer = certificate->get_issuer(certificate); + x509_t *x509 = (x509_t*)certificate; + chunk_t authKeyID = x509->get_authKeyIdentifier(x509); + cert_t *issuer_cert; + time_t notBefore, notAfter; + bool valid; + + DBG(DBG_CONTROL, + DBG_log("subject: '%Y'", subject); + DBG_log("issuer: '%Y'", issuer); + if (authKeyID.ptr) + { + DBG_log("authkey: %#B", &authKeyID); + } + ) + + valid = certificate->get_validity(certificate, NULL, + ¬Before, ¬After); + if (*until == UNDEFINED_TIME || notAfter < *until) + { + *until = notAfter; + } + if (!valid) + { + plog("certificate is invalid (valid from %T to %T)", + ¬Before, FALSE, ¬After, FALSE); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate is valid") + ) + + lock_authcert_list("verify_x509cert"); + issuer_cert = get_authcert(issuer, authKeyID, X509_CA); + if (issuer_cert == NULL) + { + plog("issuer cacert not found"); + unlock_authcert_list("verify_x509cert"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("issuer cacert found") + ) + + if (!certificate->issued_by(certificate, issuer_cert->cert)) + { + plog("certificate signature is invalid"); + unlock_authcert_list("verify_x509cert"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate signature is valid") + ) + unlock_authcert_list("verify_x509cert"); + + /* check path length constraint */ + pathlen_constraint = x509->get_pathLenConstraint(x509); + if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT && + pathlen > pathlen_constraint) + { + plog("path length of %d violates constraint of %d", + pathlen, pathlen_constraint); + return FALSE; + } + + /* check if cert is a self-signed root ca */ + if (pathlen >= 0 && (x509->get_flags(x509) & X509_SELF_SIGNED)) + { + DBG(DBG_CONTROL, + DBG_log("reached self-signed root ca with a path length of %d", + pathlen) + ) + return TRUE; + } + else + { + time_t nextUpdate = *until; + time_t revocationDate = UNDEFINED_TIME; + crl_reason_t revocationReason = CRL_REASON_UNSPECIFIED; + + /* first check certificate revocation using ocsp */ + cert_status_t status = verify_by_ocsp(cert, &nextUpdate + , &revocationDate, &revocationReason); + + /* if ocsp service is not available then fall back to crl */ + if ((status == CERT_UNDEFINED) + || (status == CERT_UNKNOWN && strict)) + { + status = verify_by_crl(cert, &nextUpdate, &revocationDate + , &revocationReason); + } + + switch (status) + { + case CERT_GOOD: + /* if status information is stale */ + if (strict && nextUpdate < time(NULL)) + { + DBG(DBG_CONTROL, + DBG_log("certificate is good but status is stale") + ) + remove_x509_public_key(cert); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate is good") + ) + + /* with strict crl policy the public key must have the same + * lifetime as the validity of the ocsp status or crl lifetime + */ + if (strict && nextUpdate < *until) + { + *until = nextUpdate; + } + break; + case CERT_REVOKED: + plog("certificate was revoked on %T, reason: %N" + , &revocationDate, TRUE + , crl_reason_names, revocationReason); + remove_x509_public_key(cert); + return FALSE; + case CERT_UNKNOWN: + case CERT_UNDEFINED: + default: + plog("certificate status unknown"); + if (strict) + { + remove_x509_public_key(cert); + return FALSE; + } + break; + } + } + + /* go up one step in the trust chain */ + cert = issuer_cert; + } + plog("maximum path length of %d exceeded", X509_MAX_PATH_LEN); + return FALSE; +} + +/** + * List all X.509 certs in a chained list + */ +void list_x509cert_chain(const char *caption, cert_t* cert, + x509_flag_t flags, bool utc) +{ + bool first = TRUE; + time_t now; + + /* determine the current time */ + time(&now); + + while (cert) + { + certificate_t *certificate = cert->cert; + x509_t *x509 = (x509_t*)certificate; + + if (certificate->get_type(certificate) == CERT_X509 && + (flags == X509_NONE || (flags & x509->get_flags(x509)))) + { + enumerator_t *enumerator; + char buf[BUF_LEN]; + char *pos = buf; + int len = BUF_LEN, pathlen; + bool first_altName = TRUE; + identification_t *id; + time_t notBefore, notAfter; + public_key_t *key; + chunk_t serial, keyid, subjkey, authkey; + + if (first) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of X.509 %s Certificates:", caption); + first = FALSE; + } + whack_log(RC_COMMENT, " "); + + enumerator = x509->create_subjectAltName_enumerator(x509); + while (enumerator->enumerate(enumerator, &id)) + { + int written; + + if (first_altName) + { + written = snprintf(pos, len, "%Y", id); + first_altName = FALSE; + } + else + { + written = snprintf(pos, len, ", %Y", id); + } + pos += written; + len -= written; + } + enumerator->destroy(enumerator); + if (!first_altName) + { + whack_log(RC_COMMENT, " altNames: %s", buf); + } + + whack_log(RC_COMMENT, " subject: \"%Y\"", + certificate->get_subject(certificate)); + whack_log(RC_COMMENT, " issuer: \"%Y\"", + certificate->get_issuer(certificate)); + serial = x509->get_serial(x509); + whack_log(RC_COMMENT, " serial: %#B", &serial); + + /* list validity */ + certificate->get_validity(certificate, &now, ¬Before, ¬After); + whack_log(RC_COMMENT, " validity: not before %T %s", + ¬Before, utc, + (notBefore < now)?"ok":"fatal (not valid yet)"); + whack_log(RC_COMMENT, " not after %T %s", + ¬After, utc, + check_expiry(notAfter, CA_CERT_WARNING_INTERVAL, TRUE)); + + key = certificate->get_public_key(certificate); + if (key) + { + whack_log(RC_COMMENT, " pubkey: %N %4d bits%s", + key_type_names, key->get_type(key), + key->get_keysize(key) * BITS_PER_BYTE, + cert->smartcard ? ", on smartcard" : + (has_private_key(cert)? ", has private key" : "")); + + if (key->get_fingerprint(key, KEY_ID_PUBKEY_INFO_SHA1, &keyid)) + { + whack_log(RC_COMMENT, " keyid: %#B", &keyid); + } + if (key->get_fingerprint(key, KEY_ID_PUBKEY_SHA1, &subjkey)) + { + whack_log(RC_COMMENT, " subjkey: %#B", &subjkey); + } + key->destroy(key); + } + + /* list optional authorityKeyIdentifier */ + authkey = x509->get_authKeyIdentifier(x509); + if (authkey.ptr) + { + whack_log(RC_COMMENT, " authkey: %#B", &authkey); + } + + /* list optional pathLenConstraint */ + pathlen = x509->get_pathLenConstraint(x509); + if (pathlen != X509_NO_PATH_LEN_CONSTRAINT) + { + whack_log(RC_COMMENT, " pathlen: %d", pathlen); + } + + } + cert = cert->next; + } +} + diff --git a/debian/patches/snprintf-fix-4.4.0.patch b/debian/patches/snprintf-fix-4.4.0.patch index 467bd2023..0cf97c0b0 100644 --- a/debian/patches/snprintf-fix-4.4.0.patch +++ b/debian/patches/snprintf-fix-4.4.0.patch @@ -9,20 +9,20 @@ Subject: [PATCH] snprintf() fixes, version 4.4.0 src/pluto/x509.c | 4 ++++ 3 files changed, 27 insertions(+), 2 deletions(-) -diff --git a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c -index ff3ddeb..de5b85b 100644 ---- a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c -+++ b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c -@@ -159,7 +159,7 @@ static char* get_string(private_ietf_attributes_t *this) +Index: strongswan/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c +=================================================================== +--- strongswan.orig/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c 2010-06-24 20:43:01.000000000 +0200 ++++ strongswan/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c 2010-06-24 20:45:07.000000000 +0200 +@@ -159,7 +159,7 @@ enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &attr)) { - int written = 0; + int written; - + if (first) { -@@ -168,8 +168,12 @@ static char* get_string(private_ietf_attributes_t *this) +@@ -168,6 +168,10 @@ else { written = snprintf(pos, len, ", "); @@ -31,12 +31,9 @@ index ff3ddeb..de5b85b 100644 + break; + } pos += written; -- len -= written; -+ len -= written; + len -= written; } - - switch (attr->type) -@@ -194,8 +198,13 @@ static char* get_string(private_ietf_attributes_t *this) +@@ -194,8 +198,13 @@ break; } default: @@ -50,11 +47,11 @@ index ff3ddeb..de5b85b 100644 pos += written; len -= written; } -diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c -index b0da340..cff24d7 100644 ---- a/src/libstrongswan/utils/identification.c -+++ b/src/libstrongswan/utils/identification.c -@@ -297,18 +297,30 @@ static void dntoa(chunk_t dn, char *buf, size_t len) +Index: strongswan/src/libstrongswan/utils/identification.c +=================================================================== +--- strongswan.orig/src/libstrongswan/utils/identification.c 2010-06-24 20:43:01.000000000 +0200 ++++ strongswan/src/libstrongswan/utils/identification.c 2010-06-24 20:44:09.348295306 +0200 +@@ -297,18 +297,30 @@ { written = snprintf(buf, len,"%s=", oid_names[oid].name); } @@ -64,7 +61,7 @@ index b0da340..cff24d7 100644 + } buf += written; len -= written; - + chunk_printable(data, &printable, '?'); written = snprintf(buf, len, "%.*s", printable.len, printable.ptr); chunk_free(&printable); @@ -74,7 +71,7 @@ index b0da340..cff24d7 100644 + } buf += written; len -= written; - + if (data.ptr + data.len != dn.ptr + dn.len) { written = snprintf(buf, len, ", "); @@ -85,11 +82,11 @@ index b0da340..cff24d7 100644 buf += written; len -= written; } -diff --git a/src/pluto/x509.c b/src/pluto/x509.c -index d8e8879..0dcc4fe 100644 ---- a/src/pluto/x509.c -+++ b/src/pluto/x509.c -@@ -393,6 +393,10 @@ void list_x509cert_chain(const char *caption, cert_t* cert, +Index: strongswan/src/pluto/x509.c +=================================================================== +--- strongswan.orig/src/pluto/x509.c 2010-06-24 20:43:01.000000000 +0200 ++++ strongswan/src/pluto/x509.c 2010-06-24 20:44:09.352296635 +0200 +@@ -393,6 +393,10 @@ { written = snprintf(pos, len, ", %Y", id); } @@ -100,6 +97,3 @@ index d8e8879..0dcc4fe 100644 pos += written; len -= written; } --- -1.7.0.4 - diff --git a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c index ff3ddeb6f..71dcd28f2 100644 --- a/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c +++ b/src/libstrongswan/credentials/ietf_attributes/ietf_attributes.c @@ -159,7 +159,7 @@ static char* get_string(private_ietf_attributes_t *this) enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &attr)) { - int written = 0; + int written; if (first) { @@ -168,6 +168,10 @@ static char* get_string(private_ietf_attributes_t *this) else { written = snprintf(pos, len, ", "); + if (written < 0 || written >= len) + { + break; + } pos += written; len -= written; } @@ -194,8 +198,13 @@ static char* get_string(private_ietf_attributes_t *this) break; } default: + written = 0; break; } + if (written < 0 || written >= len) + { + break; + } pos += written; len -= written; } diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 6a3c3936c..6ccfa193d 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -297,18 +297,30 @@ static void dntoa(chunk_t dn, char *buf, size_t len) { written = snprintf(buf, len,"%s=", oid_names[oid].name); } + if (written < 0 || written >= len) + { + break; + } buf += written; len -= written; chunk_printable(data, &printable, '?'); written = snprintf(buf, len, "%.*s", printable.len, printable.ptr); chunk_free(&printable); + if (written < 0 || written >= len) + { + break; + } buf += written; len -= written; if (data.ptr + data.len != dn.ptr + dn.len) { written = snprintf(buf, len, ", "); + if (written < 0 || written >= len) + { + break; + } buf += written; len -= written; } diff --git a/src/pluto/x509.c b/src/pluto/x509.c index 0a29830ea..0abebc6f0 100644 --- a/src/pluto/x509.c +++ b/src/pluto/x509.c @@ -393,6 +393,10 @@ void list_x509cert_chain(const char *caption, cert_t* cert, { written = snprintf(pos, len, ", %Y", id); } + if (written < 0 || written >= len) + { + break; + } pos += written; len -= written; } |