Merge branch 'id-type-prefix'
authorMartin Willi <martin@revosec.ch>
Thu, 30 Oct 2014 10:21:22 +0000 (11:21 +0100)
committerMartin Willi <martin@revosec.ch>
Thu, 30 Oct 2014 10:21:22 +0000 (11:21 +0100)
Introduce generic identity prefixes to enforce a specific type.

NEWS
man/ipsec.conf.5.in
src/libstrongswan/tests/suites/test_identification.c
src/libstrongswan/utils/identification.c
src/libstrongswan/utils/identification.h
src/swanctl/swanctl.opt

diff --git a/NEWS b/NEWS
index f1a4b21..a967e3a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,12 @@
+strongswan-5.2.2
+----------------
+
+- The left/rightid options in ipsec.conf, or any other identity in strongSwan,
+  now accept prefixes to enforce an explicit type, such as email: or fqdn:.
+  Note that no conversion is done for the remaining string, refer to
+  ipsec.conf(5) for details.
+
+
 strongswan-5.2.1
 ----------------
 
index fe37dff..32d0b9a 100644 (file)
@@ -750,11 +750,36 @@ defaults to
 .B left
 or the subject of the certificate configured with
 .BR leftcert .
-Can be an IP address, a fully-qualified domain name, an email address, or
-a keyid. If
+If
 .B leftcert
 is configured the identity has to be confirmed by the certificate.
 
+Can be an IP address, a fully-qualified domain name, an email address or a
+Distinguished Name for which the ID type is determined automatically and the
+string is converted to the appropriate encoding. To enforce a specific identity
+type, a prefix may be used, followed by a colon (:). If the number sign (#)
+follows the colon, the remaining data is interpreted as hex encoding, otherwise
+the string is used as-is as the identification data. Note that this implies
+that no conversion is performed for non-string identities. For example,
+\fIipv4:10.0.0.1\fP does not create a valid ID_IPV4_ADDR IKE identity, as it
+does not get converted to binary 0x0a000001. Instead, one could use
+\fIipv4:#0a000001\fP to get a valid identity, but just using the implicit type
+with automatic conversion is usually simpler. The same applies to the ASN1
+encoded types. The following prefixes are known:
+.BR ipv4 ,
+.BR ipv6 ,
+.BR rfc822 ,
+.BR email ,
+.BR userfqdn ,
+.BR fqdn ,
+.BR dns ,
+.BR asn1dn ,
+.B asn1gn
+and
+.BR keyid .
+Custom type prefixes may be specified by surrounding the numerical type value by
+curly brackets.
+
 For IKEv2 and
 .B rightid
 the prefix
index 5de7857..de00e4a 100644 (file)
@@ -122,58 +122,68 @@ static struct {
                } data;
        } result;
 } string_data[] = {
-       {NULL,      ID_ANY,  { .type = ENC_CHUNK }},
-       {"",        ID_ANY,  { .type = ENC_CHUNK }},
-       {"%any",    ID_ANY,  { .type = ENC_CHUNK }},
-       {"%any6",   ID_ANY,  { .type = ENC_CHUNK }},
-       {"0.0.0.0", ID_ANY,  { .type = ENC_CHUNK }},
-       {"0::0",    ID_ANY,  { .type = ENC_CHUNK }},
-       {"::",      ID_ANY,  { .type = ENC_CHUNK }},
-       {"*",       ID_ANY,  { .type = ENC_CHUNK }},
-       {"any",     ID_FQDN, { .type = ENC_SIMPLE }},
-       {"any6",    ID_FQDN, { .type = ENC_SIMPLE }},
-       {"0",       ID_FQDN, { .type = ENC_SIMPLE }},
-       {"**",      ID_FQDN, { .type = ENC_SIMPLE }},
-       {"192.168.1.1", ID_IPV4_ADDR, { .type = ENC_CHUNK,
-                                                                       .data.c = chunk_from_chars(0xc0, 0xa8, 0x01, 0x01) }},
-       {"192.168.",ID_FQDN, { .type = ENC_SIMPLE }},
-       {".",       ID_FQDN, { .type = ENC_SIMPLE }},
-       {"fec0::1", ID_IPV6_ADDR, { .type = ENC_CHUNK,
-                                                               .data.c = chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                                                                                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01) }},
-       {"fec0::",  ID_IPV6_ADDR, { .type = ENC_CHUNK,
-                                                               .data.c = chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                                                                                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) }},
-       {"fec0:",   ID_KEY_ID,    { .type = ENC_SIMPLE }},
-       {":",       ID_KEY_ID,    { .type = ENC_SIMPLE }},
-       {"alice@strongswan.org", ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
-       {"alice@strongswan", ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
-       {"alice@",  ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
-       {"alice",   ID_FQDN, { .type = ENC_SIMPLE }},
-       {"@",       ID_FQDN, { .type = ENC_CHUNK }},
-       {" @",      ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
-       {"@strongswan.org",  ID_FQDN, { .type = ENC_STRING,
-                                                                       .data.s = "strongswan.org" }},
-       {"@#deadbeef", ID_KEY_ID, { .type = ENC_CHUNK,
-                                                               .data.c = chunk_from_chars(0xde, 0xad, 0xbe, 0xef) }},
-       {"@#deadbee",  ID_KEY_ID, { .type = ENC_CHUNK,
-                                                               .data.c = chunk_from_chars(0x0d, 0xea, 0xdb, 0xee) }},
-       {"foo=bar",    ID_KEY_ID, { .type = ENC_SIMPLE }},
-       {"foo=",           ID_KEY_ID, { .type = ENC_SIMPLE }},
-       {"=bar",           ID_KEY_ID, { .type = ENC_SIMPLE }},
-       {"C=",             ID_DER_ASN1_DN, { .type = ENC_CHUNK,
-                                                                        .data.c = chunk_from_chars(0x30, 0x0b, 0x31, 0x09, 0x30, 0x07, 0x06,
-                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x00)}},
-       {"C=CH",           ID_DER_ASN1_DN, { .type = ENC_CHUNK,
-                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
-                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
-       {"C=CH,",          ID_DER_ASN1_DN, { .type = ENC_CHUNK,
-                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
-                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
-       {"C=CH, ",         ID_DER_ASN1_DN, { .type = ENC_CHUNK,
-                                                                        .data.c = chunk_from_chars(0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09, 0x06,
-                                                                                                                               0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48)}},
-       {"C=CH, O",        ID_KEY_ID, { .type = ENC_SIMPLE }},
+       {NULL,                                          ID_ANY,                 { .type = ENC_CHUNK }},
+       {"",                                            ID_ANY,                 { .type = ENC_CHUNK }},
+       {"%any",                                        ID_ANY,                 { .type = ENC_CHUNK }},
+       {"%any6",                                       ID_ANY,                 { .type = ENC_CHUNK }},
+       {"0.0.0.0",                                     ID_ANY,                 { .type = ENC_CHUNK }},
+       {"0::0",                                        ID_ANY,                 { .type = ENC_CHUNK }},
+       {"::",                                          ID_ANY,                 { .type = ENC_CHUNK }},
+       {"*",                                           ID_ANY,                 { .type = ENC_CHUNK }},
+       {"any",                                         ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"any6",                                        ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"0",                                           ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"**",                                          ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"192.168.1.1",                         ID_IPV4_ADDR,   { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }},
+       {"192.168.",                            ID_FQDN,                { .type = ENC_SIMPLE }},
+       {".",                                           ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"fec0::1",                                     ID_IPV6_ADDR,   { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
+                                                                  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01) }},
+       {"fec0::",                                      ID_IPV6_ADDR,   { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
+                                                                  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) }},
+       {"fec0:",                                       ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {":",                                           ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {"alice@strongswan.org",        ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice@strongswan",            ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice@",                                      ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"alice",                                       ID_FQDN,                { .type = ENC_SIMPLE }},
+       {"@",                                           ID_FQDN,                { .type = ENC_CHUNK }},
+       {" @",                                          ID_RFC822_ADDR, { .type = ENC_SIMPLE }},
+       {"@strongswan.org",                     ID_FQDN,                { .type = ENC_STRING,
+               .data.s = "strongswan.org" }},
+       {"@#deadbeef",                          ID_KEY_ID,              { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xde,0xad,0xbe,0xef) }},
+       {"@#deadbee",                           ID_KEY_ID,              { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0x0d,0xea,0xdb,0xee) }},
+       {"foo=bar",                                     ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {"foo=",                                        ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {"=bar",                                        ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {"C=",                                          ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0x30,0x0b,0x31,0x09,0x30,0x07,0x06,
+                                                                  0x03,0x55,0x04,0x06,0x13,0x00) }},
+       {"C=CH",                                        ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06,
+                                                                  0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }},
+       {"C=CH,",                                       ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06,
+                                                                  0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }},
+       {"C=CH, ",                                      ID_DER_ASN1_DN, { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06,
+                                                                  0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }},
+       {"C=CH, O",                                     ID_KEY_ID,              { .type = ENC_SIMPLE }},
+       {"IPv4:#c0a80101",                      ID_IPV4_ADDR,   { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }},
+       { "email:tester",                       ID_RFC822_ADDR, { .type = ENC_STRING,
+               .data.s = "tester" }},
+       { "{1}:#c0a80101",                      ID_IPV4_ADDR,   { .type = ENC_CHUNK,
+               .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }},
+       { "{0x02}:tester",                      ID_FQDN,                { .type = ENC_STRING,
+               .data.s = "tester" }},
+       { "{99}:somedata",                      99,                             { .type = ENC_STRING,
+               .data.s = "somedata" }},
 };
 
 START_TEST(test_from_string)
index 46ac7e8..b69adf3 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <string.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include "identification.h"
 
@@ -927,6 +928,82 @@ static private_identification_t *identification_create(id_type_t type)
        return this;
 }
 
+/**
+ * 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                    },
+               { "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                  },
+               { "keyid:",                     ID_KEY_ID                               },
+       };
+       private_identification_t *this;
+       int i;
+
+       for (i = 0; i < countof(prefixes); i++)
+       {
+               if (strcasepfx(str, prefixes[i].str))
+               {
+                       this = identification_create(prefixes[i].type);
+                       str += strlen(prefixes[i].str);
+                       if (*str == '#')
+                       {
+                               this->encoded = chunk_from_hex(chunk_from_str(str + 1), NULL);
+                       }
+                       else
+                       {
+                               this->encoded = chunk_clone(chunk_from_str(str));
+                       }
+                       return this;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * Create an identity for a specific type, determined by a numerical prefix
+ *
+ * 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)
+{
+       private_identification_t *this;
+       u_long type;
+
+       if (*str++ != '{')
+       {
+               return NULL;
+       }
+       errno = 0;
+       type = strtoul(str, &str, 0);
+       if (errno || *str++ != '}' || *str++ != ':')
+       {
+               return NULL;
+       }
+       this = identification_create(type);
+       if (*str == '#')
+       {
+               this->encoded = chunk_from_hex(chunk_from_str(str + 1), NULL);
+       }
+       else
+       {
+               this->encoded = chunk_clone(chunk_from_str(str));
+       }
+       return this;
+}
+
 /*
  * Described in header.
  */
@@ -939,6 +1016,16 @@ identification_t *identification_create_from_string(char *string)
        {
                string = "%any";
        }
+       this = create_from_string_with_prefix_type(string);
+       if (this)
+       {
+               return &this->public;
+       }
+       this = create_from_string_with_num_type(string);
+       if (this)
+       {
+               return &this->public;
+       }
        if (strchr(string, '=') != NULL)
        {
                /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN.
index e624468..e6a9fe1 100644 (file)
@@ -302,6 +302,15 @@ struct identification_t {
  * N, G, I, dnQualifier, ID, EN, EmployeeNumber, E, Email, emailAddress, UN,
  * unstructuredName, TCGID.
  *
+ * To skip automatic type detection the following prefixes may be used to
+ * enforce a specific type: ipv4:, ipv6:, rfc822:, email:, userfqdn:, fqdn:,
+ * dns:, asn1dn:, asn1gn: and keyid:. If a # follows the :, the remaining data
+ * is interpreted as hex encoded binary data for that ID, otherwise the raw
+ * string following the prefix is used as identity data, without conversion.
+ * To specify a non-standard ID type, the numerical type may be prefixed
+ * between curly backets, building a prefix. For instance the "{1}:" prefix
+ * defines an ID_IPV4_ADDR type.
+ *
  * This constructor never returns NULL. If it does not find a suitable
  * conversion function, it will copy the string to an ID_KEY_ID.
  *
index f1e47a9..5783555 100644 (file)
@@ -303,6 +303,22 @@ connections.<conn>.local<suffix>.id =
        authentication, the IKE identity must be contained in the certificate,
        either as subject or as subjectAltName.
 
+       The identity can be an IP address, a fully-qualified domain name, an email
+       address or a Distinguished Name for which the ID type is determined
+       automatically and the string is converted to the appropriate encoding. To
+       enforce a specific identity type, a prefix may be used, followed by a colon
+       (:). If the number sign (#) follows the colon, the remaining data is
+       interpreted as hex encoding, otherwise the string is used as-is as the
+       identification data. Note that this implies that no conversion is performed
+       for non-string identities. For example, _ipv4:10.0.0.1_ does not create a
+       valid ID_IPV4_ADDR IKE identity, as it does not get converted to binary
+       0x0a000001. Instead, one could use _ipv4:#0a000001_ to get a valid identity,
+       but just using the implicit type with automatic conversion is usually
+       simpler. The same applies to the ASN1 encoded types. The following prefixes
+       are known: _ipv4_, _ipv6_, _rfc822_, _email_, _userfqdn_, _fqdn_, _dns_,
+       _asn1dn_, _asn1gn_ and _keyid_. Custom type prefixes may be specified by
+       surrounding the numerical type value by curly brackets.
+
 connections.<conn>.local<suffix>.eap_id = id
        Client EAP-Identity to use in EAP-Identity exchange and the EAP method.
 
@@ -335,9 +351,8 @@ connections.<conn>.remote<suffix> {}
 connections.<conn>.remote<suffix>.id = %any
        IKE identity to expect for authentication round.
 
-       IKE identity to expect for authentication round. When using certificate
-       authentication, the IKE identity must be contained in the certificate,
-       either as subject or as subjectAltName.
+       IKE identity to expect for authentication round. Refer to the _local_ _id_
+       section for details.
 
 connections.<conn>.remote<suffix>.groups =
        Authorization group memberships to require.