AC_SUBST(ATOMICLIB)
LIBS=$saved_LIBS
+
+# Some platforms require explicit linking to use POSIX regular expressions
+AC_SEARCH_LIBS([regcomp], [regex], [AC_DEFINE([HAVE_REGEX], [], [have regcomp() etc.])])
+
# ------------------------------------------------------
AC_MSG_CHECKING(for dladdr)
}
END_TEST
+START_TEST(test_from_string_with_regex)
+{
+ identification_t *id;
+
+ id = identification_create_from_string_with_regex("fqdn:^vpn.*\\.strongswan\\.org$");
+ ck_assert(id);
+ ck_assert_int_eq(ID_FQDN, id->get_type(id));
+ ck_assert_chunk_eq(chunk_from_str("^vpn.*\\.strongswan\\.org$"), id->get_encoding(id));
+ id->destroy(id);
+
+ id = identification_create_from_string_with_regex("email:^.+@strongswan\\.org$");
+ ck_assert(id);
+ ck_assert_int_eq(ID_RFC822_ADDR, id->get_type(id));
+ ck_assert_chunk_eq(chunk_from_str("^.+@strongswan\\.org$"), id->get_encoding(id));
+ id->destroy(id);
+
+ id = identification_create_from_string_with_regex("asn1dn:^.+CN=.+@strongswan\\.org$");
+ ck_assert(id);
+ ck_assert_int_eq(ID_DER_ASN1_DN, id->get_type(id));
+ ck_assert_chunk_eq(chunk_from_str("^.+CN=.+@strongswan\\.org$"), id->get_encoding(id));
+ id->destroy(id);
+
+ /* invalid regex */
+ ck_assert(!identification_create_from_string_with_regex("fqdn:^[+\\.strongswan\\.org$"));
+
+ /* ignored if pre-/suffix is missing or the type is unsupported */
+ id = identification_create_from_string_with_regex("fqdn:[+\\.strongswan\\.org$");
+ ck_assert(id);
+ id->destroy(id);
+ id = identification_create_from_string_with_regex("fqdn:^[+\\.strongswan\\.org");
+ ck_assert(id);
+ id->destroy(id);
+ id = identification_create_from_string_with_regex("keyid:^[+\\.strongswan\\.org$");
+ ck_assert(id);
+ ck_assert_int_eq(ID_KEY_ID, id->get_type(id));
+ id->destroy(id);
+}
+END_TEST
+
/*******************************************************************************
* printf_hook
*/
}
END_TEST
+START_TEST(test_printf_hook_regex)
+{
+ identification_t *a;
+ char buf[128];
+
+ a = identification_create_from_string_with_regex("fqdn:^vpn.+\\.strongswan\\.org$");
+ snprintf(buf, sizeof(buf), "%Y", a);
+ ck_assert_str_eq("^vpn.+\\.strongswan\\.org$", buf);
+ snprintf(buf, sizeof(buf), "%30Y", a);
+ ck_assert_str_eq(" ^vpn.+\\.strongswan\\.org$", buf);
+ snprintf(buf, sizeof(buf), "%-*Y", 30, a);
+ ck_assert_str_eq("^vpn.+\\.strongswan\\.org$ ", buf);
+ a->destroy(a);
+}
+END_TEST
+
/*******************************************************************************
* equals
*/
}
END_TEST
+START_TEST(test_equals_regex)
+{
+ identification_t *a, *b;
+
+ a = identification_create_from_string_with_regex("fqdn:^vpn.*\\.strongswan\\.org$");
+ b = identification_create_from_string_with_regex("fqdn:^vpn.*\\.strongswan\\.org$");
+ ck_assert(a->equals(a, b));
+ ck_assert(b->equals(b, a));
+ b->destroy(b);
+ b = identification_create_from_string_with_regex("fqdn:^vpn.+\\.strongswan\\.org$");
+ ck_assert(!a->equals(a, b));
+ ck_assert(!b->equals(b, a));
+ b->destroy(b);
+
+ b = identification_create_from_string("fqdn:^vpn.*\\.strongswan\\.org$");
+ ck_assert_int_eq(a->get_type(a), b->get_type(b));
+ ck_assert_chunk_eq(a->get_encoding(a), b->get_encoding(b));
+ ck_assert(!a->equals(a, b));
+ ck_assert(!b->equals(b, a));
+ b->destroy(b);
+ a->destroy(a);
+}
+END_TEST
+
START_TEST(test_equals_empty)
{
identification_t *a;
}
END_TEST
+static bool regex_matches(identification_t *a, char *b_regex, id_match_t expected)
+{
+ identification_t *b;
+ id_match_t match;
+
+ b = identification_create_from_string_with_regex(b_regex);
+ ck_assert_msg(b, "'%s' is invalid", b_regex);
+ match = a->matches(a, b);
+ b->destroy(b);
+ return match == expected;
+}
+
+START_TEST(test_matches_regex)
+{
+ identification_t *a;
+
+ a = identification_create_from_string("moon@strongswan.org");
+
+ ck_assert(regex_matches(a, "email:^moon@strongswan.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^moon@strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^MOON@strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^(moon|sun)@strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^moon@strongswan\\.or$", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "email:^.+@strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^moon@.*.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^.+$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "email:^$", ID_MATCH_NONE));
+ a->destroy(a);
+
+ a = identification_create_from_string("vpn1.strongswan.org");
+
+ ck_assert(regex_matches(a, "fqdn:^vpn1\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "fqdn:^VPN1\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "fqdn:^strongswan\\.org$", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "fqdn:^.*\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "fqdn:^vpn[0-9]\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "fqdn:^vpn[[:digit:]]\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "fqdn:^[^0-9]+[0-9]+\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ a->destroy(a);
+
+ a = identification_create_from_string("C=CH, O=strongSwan, CN=vpn1.strongswan.org");
+
+ ck_assert(regex_matches(a, "asn1dn:^.*CN=.*\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ ck_assert(regex_matches(a, "asn1dn:^.*CN=vpn[0-9]\\.strongswan\\.org$", ID_MATCH_MAX_WILDCARDS));
+ a->destroy(a);
+
+ a = identification_create_from_string("fqdn:^vpn1\\.strongswan\\.org$");
+
+ ck_assert(regex_matches(a, "fqdn:^vpn1\\.strongswan\\.org$", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "vpn1.strongswan.org", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "%any", ID_MATCH_ANY));
+ a->destroy(a);
+
+ a = identification_create_from_string_with_regex("fqdn:^vpn1\\.strongswan\\.org$");
+
+ ck_assert(regex_matches(a, "fqdn:^vpn1\\.strongswan\\.org$", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "vpn1.strongswan.org", ID_MATCH_NONE));
+ ck_assert(regex_matches(a, "%any", ID_MATCH_ANY));
+ a->destroy(a);
+
+ /* internal buffer for matched identities is BUF_LEN-1, do a suffix match
+ * as it would fail if the buffer was too small anyway otherwise */
+ a = identification_create_from_string("moon@strongswan.org ");
+ ck_assert(regex_matches(a, "email:^moon@strongswan.org.*$", ID_MATCH_MAX_WILDCARDS));
+ a->destroy(a);
+
+ a = identification_create_from_string("moon@strongswan.org ");
+ ck_assert(regex_matches(a, "email:^moon@strongswan.org.*$", ID_MATCH_NONE));
+ a->destroy(a);
+}
+END_TEST
+
START_TEST(test_matches_empty)
{
identification_t *a;
}
END_TEST
+START_TEST(test_hash_regex)
+{
+ identification_t *a, *b;
+
+ a = identification_create_from_string_with_regex("fqdn:^vpn1\\.strongswan\\.org$");
+ b = identification_create_from_string_with_regex("fqdn:^vpn1\\.strongswan\\.org$");
+ ck_assert_int_eq(a->hash(a, 0), b->hash(b, 0));
+ b->destroy(b);
+ b = identification_create_from_string("fqdn:^vpn1\\.strongswan\\.org$");
+ ck_assert_int_eq(a->get_type(a), b->get_type(b));
+ ck_assert_chunk_eq(a->get_encoding(a), b->get_encoding(b));
+ ck_assert(a->hash(a, 0) != b->hash(b, 0));
+ b->destroy(b);
+ a->destroy(a);
+}
+END_TEST
+
START_TEST(test_hash_inc)
{
identification_t *a;
* wildcards
*/
-static bool id_contains_wildcards(char *string)
+static bool check_id_contains_wildcards(identification_t *id)
{
- identification_t *id;
bool contains;
- id = identification_create_from_string(string);
contains = id->contains_wildcards(id);
id->destroy(id);
return contains;
}
+static bool id_contains_wildcards(char *str)
+{
+ return check_id_contains_wildcards(identification_create_from_string(str));
+}
+
START_TEST(test_contains_wildcards)
{
+ identification_t *id;
+
ck_assert(id_contains_wildcards("%any"));
ck_assert(id_contains_wildcards("C=*, O=strongSwan, CN=gw"));
ck_assert(id_contains_wildcards("C=CH, O=strongSwan, CN=*"));
ck_assert(id_contains_wildcards("*@strongswan.org"));
ck_assert(id_contains_wildcards("*.strongswan.org"));
ck_assert(!id_contains_wildcards("C=**, O=a*, CN=*a"));
+ /* not actual regexes as the wrong constructor is used */
+ ck_assert(!id_contains_wildcards("fqdn:^vpn1\\.strongswan\\.org$"));
+ ck_assert(id_contains_wildcards("fqdn:^vpn.*\\.strongswan\\.org$"));
+ /* no regex due to missing type prefix */
+ id = identification_create_from_string_with_regex("^vpn1\\.strongswan\\.org$");
+ ck_assert(!check_id_contains_wildcards(id));
+ id = identification_create_from_string_with_regex("fqdn:^vpn1\\.strongswan\\.org$");
+ ck_assert(check_id_contains_wildcards(id));
}
END_TEST
START_TEST(test_clone)
{
- identification_t *a, *b;
+ identification_t *a, *b, *c;
chunk_t a_enc, b_enc;
a = identification_create_from_string("moon@strongswan.org");
b = a->clone(a);
ck_assert(b != NULL);
ck_assert(a != b);
+ ck_assert_int_eq(a->get_type(a), b->get_type(b));
+ b_enc = b->get_encoding(b);
+ ck_assert(a_enc.ptr != b_enc.ptr);
+ ck_assert(chunk_equals(a_enc, b_enc));
+ a->destroy(a);
+ b->destroy(b);
+
+ a = identification_create_from_string_with_regex("fqdn:^vpn.+\\.strongswan\\.org$");
+ a_enc = a->get_encoding(a);
+ b = a->clone(a);
+ ck_assert(b != NULL);
+ ck_assert(a != b);
+ ck_assert_int_eq(a->get_type(a), b->get_type(b));
b_enc = b->get_encoding(b);
ck_assert(a_enc.ptr != b_enc.ptr);
ck_assert(chunk_equals(a_enc, b_enc));
+ ck_assert_int_eq(0, a_enc.ptr[a_enc.len]);
+ ck_assert_int_eq(0, b_enc.ptr[b_enc.len]);
+ c = identification_create_from_string("vpn1.strongswan.org");
+ ck_assert_int_eq(ID_MATCH_MAX_WILDCARDS, c->matches(c, a));
+ ck_assert_int_eq(ID_MATCH_MAX_WILDCARDS, c->matches(c, b));
a->destroy(a);
b->destroy(b);
+ c->destroy(c);
}
END_TEST
tcase_add_test(tc, test_from_data);
tcase_add_test(tc, test_from_sockaddr);
tcase_add_loop_test(tc, test_from_string, 0, countof(string_data));
+ tcase_add_test(tc, test_from_string_with_regex);
suite_add_tcase(s, tc);
tc = tcase_create("printf_hook");
tcase_add_test(tc, test_printf_hook);
tcase_add_test(tc, test_printf_hook_width);
+ tcase_add_test(tc, test_printf_hook_regex);
suite_add_tcase(s, tc);
tc = tcase_create("equals");
tcase_add_test(tc, test_equals_any);
tcase_add_test(tc, test_equals_binary);
tcase_add_test(tc, test_equals_fqdn);
+ tcase_add_test(tc, test_equals_regex);
tcase_add_loop_test(tc, test_equals_empty, ID_ANY, ID_KEY_ID + 1);
suite_add_tcase(s, tc);
tcase_add_test(tc, test_matches_range_subnet);
tcase_add_test(tc, test_matches_range_range);
tcase_add_test(tc, test_matches_string);
+ tcase_add_test(tc, test_matches_regex);
tcase_add_loop_test(tc, test_matches_empty, ID_ANY, ID_KEY_ID + 1);
tcase_add_loop_test(tc, test_matches_empty_reverse, ID_ANY, ID_KEY_ID + 1);
suite_add_tcase(s, tc);
tcase_add_test(tc, test_hash);
tcase_add_test(tc, test_hash_any);
tcase_add_test(tc, test_hash_dn);
+ tcase_add_test(tc, test_hash_regex);
tcase_add_test(tc, test_hash_inc);
suite_add_tcase(s, tc);
/*
* Copyright (C) 2016 Andreas Steffen
- * Copyright (C) 2009-2019 Tobias Brunner
+ * Copyright (C) 2009-2025 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
*
#include <stdio.h>
#include <errno.h>
+#ifdef HAVE_REGEX
+#include <regex.h>
+#else
+/* usually, POSIX regular expressions are supported. for the rare case they are
+ * not, e.g. when cross-compiling for Windows, we define some stubs to simplify
+ * the implementation below */
+typedef void regex_t;
+static int regexec(const regex_t *restrict preg, const char *restrict string,
+ size_t nmatch, void *pmatch, int eflags)
+{
+ return -1;
+}
+
+static void regfree(regex_t *preg)
+{
+}
+#endif /* HAVE_REGEX */
+
#include "identification.h"
#include <utils/utils.h>
*/
#define RDN_MAX 20
-
typedef struct private_identification_t private_identification_t;
/**
* Type of this ID.
*/
id_type_t type;
+
+ /**
+ * Pointer if we use regex comparison.
+ */
+ regex_t *regex;
};
+/**
+ * Check that neither object uses a regex.
+ */
+static inline bool neither_is_regex(private_identification_t *this,
+ identification_t *other)
+{
+ private_identification_t *other_priv = (private_identification_t*)other;
+ return !this->regex && !other_priv->regex;
+}
+
/**
* Enumerator over RDNs
*/
/**
* Create an enumerator over all RDNs (oid, string type, data) of a DN
*/
-static enumerator_t* create_rdn_enumerator(chunk_t dn)
+static enumerator_t *create_rdn_enumerator(chunk_t dn)
{
rdn_enumerator_t *e;
{
hash = chunk_hash_inc(this->encoded, hash);
}
+ if (this->regex)
+ { /* ensure regexes have different hashes even if type/encoding is equal */
+ hash = chunk_hash_inc(chunk_from_chars(0x01), hash);
+ }
return hash;
}
METHOD(identification_t, equals_binary, bool,
private_identification_t *this, identification_t *other)
{
- if (this->type == other->get_type(other))
+ private_identification_t *other_priv = (private_identification_t*)other;
+
+ if (this->type == other_priv->type &&
+ (neither_is_regex(this, other) || (this->regex && other_priv->regex)))
{
if (this->type == ID_ANY)
{
return TRUE;
}
- return chunk_equals(this->encoded, other->get_encoding(other));
+ return chunk_equals(this->encoded, other_priv->encoded);
}
return FALSE;
}
METHOD(identification_t, equals_dn, bool,
private_identification_t *this, identification_t *other)
{
- return compare_dn(this->encoded, other->get_encoding(other), NULL);
+ return neither_is_regex(this, other) &&
+ compare_dn(this->encoded, other->get_encoding(other), NULL);
}
METHOD(identification_t, hash_dn, u_int,
/* we do some extra sanity checks to check for invalid IDs with a
* terminating null in it. */
if (this->type == other->get_type(other) &&
+ neither_is_regex(this, other) &&
this->encoded.len == encoded.len &&
memchr(this->encoded.ptr, 0, this->encoded.len) == NULL &&
memchr(encoded.ptr, 0, encoded.len) == NULL &&
return ID_MATCH_NONE;
}
+/**
+ * Matches the regex in other against our own encoding.
+ */
+static id_match_t matches_regex(private_identification_t *this,
+ private_identification_t *other)
+{
+ char buf[BUF_LEN-1];
+ int rc;
+
+ if (this->regex)
+ { /* don't match two regex values */
+ return ID_MATCH_NONE;
+ }
+ if (!this->encoded.len)
+ {
+ return ID_MATCH_NONE;
+ }
+ /* match against the string representation of the identity */
+ if (snprintf(buf, sizeof(buf), "%Y", this) >= sizeof(buf))
+ {
+ /* fail if the buffer is too small. note that we use BUF_LEN-1 because
+ * the printf hook uses BUF_LEN to print the identity internally */
+ return ID_MATCH_NONE;
+ }
+ rc = regexec(other->regex, buf, 0, NULL, 0);
+ return rc == 0 ? ID_MATCH_MAX_WILDCARDS : ID_MATCH_NONE;
+}
+
METHOD(identification_t, matches_string, id_match_t,
private_identification_t *this, identification_t *other)
{
+ private_identification_t *other_priv = (private_identification_t*)other;
chunk_t encoded = other->get_encoding(other);
u_int len = encoded.len;
{
return ID_MATCH_NONE;
}
+ if (other_priv->regex)
+ {
+ return matches_regex(this, other_priv);
+ }
/* try a equals check first */
if (equals_strcasecmp(this, other))
{
identification_t *other,
bool (*match)(chunk_t,chunk_t,int*))
{
+ private_identification_t *other_priv = (private_identification_t*)other;
int wc;
if (other->get_type(other) == ID_ANY)
if (this->type == other->get_type(other))
{
- if (match(this->encoded, other->get_encoding(other), &wc))
+ if (other_priv->regex)
+ {
+ return matches_regex(this, other_priv);
+ }
+ else if (match(this->encoded, other->get_encoding(other), &wc))
{
wc = min(wc, ID_MATCH_ONE_WILDCARD - ID_MATCH_MAX_WILDCARDS);
return ID_MATCH_PERFECT - wc;
}
/**
- * Described in header.
+ * Convert the given identity to a string depending on its type.
*/
-int identification_printf_hook(printf_hook_data_t *data,
- printf_hook_spec_t *spec, const void *const *args)
+static void identity_to_string(private_identification_t *this, char buf[BUF_LEN])
{
- private_identification_t *this = *((private_identification_t**)(args[0]));
chunk_t proper;
- char buf[BUF_LEN], *pos;
+ char *pos;
uint8_t address_size;
size_t len;
int written;
- if (this == NULL)
+ if (this->regex)
{
- return print_in_hook(data, "%*s", spec->width, "(null)");
+ snprintf(buf, BUF_LEN, "%s", this->encoded.ptr);
+ return;
}
switch (this->type)
snprintf(buf, BUF_LEN, "(unknown ID type: %d)", this->type);
break;
}
+}
+
+/**
+ * Described in header.
+ */
+int identification_printf_hook(printf_hook_data_t *data,
+ printf_hook_spec_t *spec, const void *const *args)
+{
+ private_identification_t *this = *((private_identification_t**)(args[0]));
+ char buf[BUF_LEN];
+
+ if (!this)
+ {
+ return print_in_hook(data, "%*s", spec->width, "(null)");
+ }
+
+ identity_to_string(this, buf);
+
if (spec->minus)
{
return print_in_hook(data, "%-*s", spec->width, buf);
return print_in_hook(data, "%*s", spec->width, buf);
}
+#ifdef HAVE_REGEX
+
+/**
+ * Compile the encoded regular expression.
+ */
+static bool compile_regex(private_identification_t *this)
+{
+ char buf[BUF_LEN];
+ int err = 0;
+
+ this->regex = malloc(sizeof(*this->regex));
+ err = regcomp(this->regex, this->encoded.ptr,
+ REG_EXTENDED | REG_ICASE | REG_NOSUB);
+ if (err != 0)
+ {
+ regerror(err, NULL, buf, sizeof(buf));
+ DBG1(DBG_LIB, "invalid regular expression '%s': %s",
+ this->encoded.ptr, buf);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#else /* HAVE_REGEX */
+
+static bool compile_regex(private_identification_t *this)
+{
+ DBG1(DBG_LIB, "regular expressions are not supported");
+ return FALSE;
+}
+
+#endif /* HAVE_REGEX */
+
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)
+ if (this->regex)
+ {
+ /* make sure we have the full encoding cloned */
+ clone->encoded = chunk_from_str(strdup(this->encoded.ptr));
+ compile_regex(clone);
+ }
+ else if (this->encoded.len)
{
clone->encoded = chunk_clone(this->encoded);
}
private_identification_t *this)
{
chunk_free(&this->encoded);
+ if (this->regex)
+ {
+ regfree(this->regex);
+ free(this->regex);
+ }
free(this);
}
return this;
}
+/**
+ * Prefixes used when parsing identities.
+ */
+static const struct {
+ const char *str;
+ id_type_t type;
+ bool regex;
+} prefixes[] = {
+ { "ipv4:", ID_IPV4_ADDR, FALSE},
+ { "ipv6:", ID_IPV6_ADDR, FALSE},
+ { "ipv4net:", ID_IPV4_ADDR_SUBNET, FALSE},
+ { "ipv6net:", ID_IPV6_ADDR_SUBNET, FALSE},
+ { "ipv4range:", ID_IPV4_ADDR_RANGE, FALSE},
+ { "ipv6range:", ID_IPV6_ADDR_RANGE, FALSE},
+ { "rfc822:", ID_RFC822_ADDR, TRUE},
+ { "email:", ID_RFC822_ADDR, TRUE},
+ { "userfqdn:", ID_USER_FQDN, FALSE},
+ { "fqdn:", ID_FQDN, TRUE},
+ { "dns:", ID_FQDN, TRUE},
+ { "asn1dn:", ID_DER_ASN1_DN, TRUE},
+ { "asn1gn:", ID_DER_ASN1_GN, FALSE},
+ { "xmppaddr:", ID_DER_ASN1_GN, FALSE},
+ { "keyid:", ID_KEY_ID, FALSE},
+ { "uri:", ID_DER_ASN1_GN_URI, FALSE},
+};
+
/**
* Create an identity for a specific type, determined by prefix
*/
-static private_identification_t* create_from_string_with_prefix_type(char *str)
-{
- struct {
- const char *str;
- id_type_t type;
- } prefixes[] = {
- { "ipv4:", ID_IPV4_ADDR },
- { "ipv6:", ID_IPV6_ADDR },
- { "ipv4net:", ID_IPV4_ADDR_SUBNET },
- { "ipv6net:", ID_IPV6_ADDR_SUBNET },
- { "ipv4range:", ID_IPV4_ADDR_RANGE },
- { "ipv6range:", ID_IPV6_ADDR_RANGE },
- { "rfc822:", ID_RFC822_ADDR },
- { "email:", ID_RFC822_ADDR },
- { "userfqdn:", ID_USER_FQDN },
- { "fqdn:", ID_FQDN },
- { "dns:", ID_FQDN },
- { "asn1dn:", ID_DER_ASN1_DN },
- { "asn1gn:", ID_DER_ASN1_GN },
- { "xmppaddr:", ID_DER_ASN1_GN },
- { "keyid:", ID_KEY_ID },
- { "uri:", ID_DER_ASN1_GN_URI },
- };
+static private_identification_t *create_from_string_with_prefix_type(char *str)
+{
private_identification_t *this;
int i;
asn1_wrap(ASN1_UTF8STRING, "m",
this->encoded)));
}
-
return this;
}
}
* The prefix is of the form "{x}:", where x denotes the numerical identity
* type.
*/
-static private_identification_t* create_from_string_with_num_type(char *str)
+static private_identification_t *create_from_string_with_num_type(char *str)
{
private_identification_t *this;
u_long type;
/**
* Convert to an IPv4/IPv6 host address, subnet or address range
*/
-static private_identification_t* create_ip_address_from_string(char *string,
+static private_identification_t *create_ip_address_from_string(char *string,
bool is_ipv4)
{
private_identification_t *this;
else
{
this = identification_create(ID_KEY_ID);
- this->encoded = chunk_from_str(strdup(string));
+ this->encoded = chunk_clone(chunk_from_str(string));
}
return &this->public;
}
if (!this)
{ /* not IPv4, mostly FQDN */
this = identification_create(ID_FQDN);
- this->encoded = chunk_from_str(strdup(string));
+ this->encoded = chunk_clone(chunk_from_str(string));
}
return &this->public;
}
if (!this)
{ /* not IPv4/6 fallback to KEY_ID */
this = identification_create(ID_KEY_ID);
- this->encoded = chunk_from_str(strdup(string));
+ this->encoded = chunk_clone(chunk_from_str(string));
}
return &this->public;
}
else
{
this = identification_create(ID_RFC822_ADDR);
- this->encoded = chunk_from_str(strdup(string));
+ this->encoded = chunk_clone(chunk_from_str(string));
return &this->public;
}
}
}
+/**
+ * Check if the given string should be parsed as regular expression identity.
+ * If so, it modifies the string and returns the identity type, otherwise,
+ * ID_ANY is returned.
+ */
+static id_type_t is_regex_identity(char **string)
+{
+ char *regex;
+ int i;
+
+ for (i = 0; i < countof(prefixes); i++)
+ {
+ if (strcasepfx(*string, prefixes[i].str))
+ {
+ regex = *string + strlen(prefixes[i].str);
+
+ if (prefixes[i].regex &&
+ *regex == '^' && *(regex + strlen(regex) - 1) == '$')
+ {
+ *string = regex;
+ return prefixes[i].type;
+ }
+ break;
+ }
+ }
+ return ID_ANY;
+}
+
+/*
+ * Described in header.
+ */
+identification_t *identification_create_from_string_with_regex(char *string)
+{
+ private_identification_t *this;
+ id_type_t type;
+
+ type = is_regex_identity(&string);
+ if (type != ID_ANY)
+ {
+ this = identification_create(type);
+
+ this->public.hash = _hash_binary;
+ this->public.equals = _equals_binary;
+ this->public.matches = _matches_any;
+ this->public.contains_wildcards = (void*)return_true;
+
+ /* this encoding explicitly includes the null-terminator so we can
+ * directly use it to compile the regex and printing */
+ this->encoded = chunk_from_str(strdup(string));
+ if (!compile_regex(this))
+ {
+ destroy(this);
+ return NULL;
+ }
+ return &this->public;
+ }
+ return identification_create_from_string(string);
+}
+
/*
* Described in header.
*/
-identification_t * identification_create_from_data(chunk_t data)
+identification_t *identification_create_from_data(chunk_t data)
{
char buf[data.len + 1];
/*
- * Copyright (C) 2009-2015 Tobias Brunner
+ * Copyright (C) 2009-2025 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
*
* @param string input string, which will be converted
* @return identification_t
*/
-identification_t * identification_create_from_string(char *string);
+identification_t *identification_create_from_string(char *string);
+
+/**
+ * Creates an identification_t object from a string that may contain a regular
+ * expression.
+ *
+ * This function behaves like identification_create_from_string() but also
+ * accepts regular expressions. So it must only be used to parse
+ * trusted/configured values, never untrusted values received over the network.
+ *
+ * A regular expression must be prefixed by an identity type (supported are
+ * rfc822:, email:, fqdn:, dns:, and asn1dn:), and it must start with a caret
+ * ('^') and end with a dollar sign ('$') to indicate an anchored pattern.
+ * If the regular expression is invalid, the function returns NULL.
+ *
+ * The regular expression is always matched against the string representation
+ * of other identities and matching is performed case-insensitive.
+ *
+ * @param string string to parse, regex must begin with '^' and end with '$'
+ * and must be prefixed with a valid identification type
+ * @return pointer to newly allocated identification_t object, or NULL
+ * if regular expression is invalid
+ */
+identification_t *identification_create_from_string_with_regex(char *string);
/**
* Creates an identification from a chunk of data, guessing its type.
* @param data identification data
* @return identification_t
*/
-identification_t * identification_create_from_data(chunk_t data);
+identification_t *identification_create_from_data(chunk_t data);
/**
* Creates an identification_t object from an encoded chunk.
* @param encoded encoded bytes, such as from identification_t.get_encoding
* @return identification_t
*/
-identification_t * identification_create_from_encoding(id_type_t type, chunk_t encoded);
+identification_t *identification_create_from_encoding(id_type_t type,
+ chunk_t encoded);
/**
* Creates an identification_t object from a sockaddr struct
* @param sockaddr sockaddr struct which contains family and address
* @return identification_t
*/
-identification_t * identification_create_from_sockaddr(sockaddr_t *sockaddr);
+identification_t *identification_create_from_sockaddr(sockaddr_t *sockaddr);
/**
* printf hook function for identification_t.
* identification_t *identification
*/
int identification_printf_hook(printf_hook_data_t *data,
- printf_hook_spec_t *spec, const void *const *args);
+ printf_hook_spec_t *spec,
+ const void *const *args);
#endif /** IDENTIFICATION_H_ @}*/