updated XML interface to new schema
[strongswan.git] / src / libstrongswan / utils / identification.c
index 8132d6e..b842a8c 100644 (file)
@@ -6,7 +6,8 @@
  */
 
 /*
- * Copyright (C) 2005 Jan Hutter, Martin Willi
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
 #include <string.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <printf.h>
 
 #include "identification.h"
 
 #include <asn1/asn1.h>
 
-/** 
- * String mappings for id_type_t.
- */
-mapping_t id_type_m[] = {
-       {ID_IPV4_ADDR,   "ID_IPV4_ADDR"},
-       {ID_FQDN,                "ID_FQDN"},
-       {ID_RFC822_ADDR, "ID_RFC822_ADDR"},
-       {ID_IPV6_ADDR,   "ID_IPV6_ADDR"},
-       {ID_DER_ASN1_DN, "ID_DER_ASN1_DN"},
-       {ID_DER_ASN1_GN, "ID_DER_ASN1_GN"},
-       {ID_KEY_ID,              "ID_KEY_ID"},
-       {ID_ANY,                 "ID_ANY"},
-       {MAPPING_END, NULL}
-};
+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_DER_ASN1_GN_URI, ID_KEY_ID,
+       "ID_DER_ASN1_GN_URI");
+ENUM_END(id_type_names, ID_DER_ASN1_GN_URI);
 
 
 /**
  * X.501 acronyms for well known object identifiers (OIDs)
  */
 static u_char oid_ND[]  = {
-       0x02, 0x82, 0x06, 0x01, 
-       0x0A, 0x07, 0x14
+       0x02, 0x82, 0x06, 0x01, 0x0A, 0x07, 0x14
 };
 static u_char oid_UID[] = {
-       0x09, 0x92, 0x26, 0x89, 0x93,
-       0xF2, 0x2C, 0x64, 0x01, 0x01
+       0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01
 };
 static u_char oid_DC[]  = {
-       0x09, 0x92, 0x26, 0x89, 0x93,
-       0xF2, 0x2C, 0x64, 0x01, 0x19
+       0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19
 };
 static u_char oid_CN[] = {
        0x55, 0x04, 0x03
@@ -106,20 +107,16 @@ static u_char oid_ID[] = {
        0x55, 0x04, 0x2D
 };
 static u_char oid_EN[]  = {
-       0x60, 0x86, 0x48, 0x01, 0x86,
-       0xF8, 0x42, 0x03, 0x01, 0x03
+       0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x03, 0x01, 0x03
 };
 static u_char oid_E[] = {
-       0x2A, 0x86, 0x48, 0x86, 0xF7,
-       0x0D, 0x01, 0x09, 0x01
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01
 };
 static u_char oid_UN[]  = {
-       0x2A, 0x86, 0x48, 0x86, 0xF7,
-       0x0D, 0x01, 0x09, 0x02
+       0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x02
 };
 static u_char oid_TCGID[] = {
-       0x2B, 0x06, 0x01, 0x04, 0x01, 0x89,
-       0x31, 0x01, 0x01, 0x02, 0x02, 0x4B
+       0x2B, 0x06, 0x01, 0x04, 0x01, 0x89, 0x31, 0x01, 0x01, 0x02, 0x02, 0x4B
 };
 
 /**
@@ -162,49 +159,10 @@ static const x501rdn_t x501rdns[] = {
 #define X501_RDN_ROOF   26
 
 /**
- * ASN.1 definition of generalName 
+ * maximum number of RDNs in atodn()
  */
-static const asn1Object_t generalNameObjects[] = {
-       { 0,   "otherName",             ASN1_CONTEXT_C_0,  ASN1_OPT|ASN1_BODY   }, /*  0 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  1 */
-       { 0,   "rfc822Name",    ASN1_CONTEXT_S_1,  ASN1_OPT|ASN1_BODY   }, /*  2 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                     }, /*  3 */
-       { 0,   "dnsName",               ASN1_CONTEXT_S_2,  ASN1_OPT|ASN1_BODY   }, /*  4 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  5 */
-       { 0,   "x400Address",   ASN1_CONTEXT_S_3,  ASN1_OPT|ASN1_BODY   }, /*  6 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  7 */
-       { 0,   "directoryName", ASN1_CONTEXT_C_4,  ASN1_OPT|ASN1_BODY   }, /*  8 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  9 */
-       { 0,   "ediPartyName",  ASN1_CONTEXT_C_5,  ASN1_OPT|ASN1_BODY   }, /* 10 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 11 */
-       { 0,   "URI",                   ASN1_CONTEXT_S_6,  ASN1_OPT|ASN1_BODY   }, /* 12 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 13 */
-       { 0,   "ipAddress",             ASN1_CONTEXT_S_7,  ASN1_OPT|ASN1_BODY   }, /* 14 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 15 */
-       { 0,   "registeredID",  ASN1_CONTEXT_S_8,  ASN1_OPT|ASN1_BODY   }, /* 16 */
-       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }  /* 17 */
-};
-#define GN_OBJ_OTHER_NAME               0
-#define GN_OBJ_RFC822_NAME              2
-#define GN_OBJ_DNS_NAME                         4
-#define GN_OBJ_X400_ADDRESS             6
-#define GN_OBJ_DIRECTORY_NAME   8
-#define GN_OBJ_EDI_PARTY_NAME  10
-#define GN_OBJ_URI                             12
-#define GN_OBJ_IP_ADDRESS              14
-#define GN_OBJ_REGISTERED_ID   16
-#define GN_OBJ_ROOF                            18
+#define RDN_MAX                        20
 
-/**
- * ASN.1 definition of otherName 
- */
-static const asn1Object_t otherNameObjects[] = {
-       {0, "type-id",  ASN1_OID,                       ASN1_BODY       }, /*  0 */
-       {0, "value",    ASN1_CONTEXT_C_0,       ASN1_BODY       }  /*  1 */
-};
-#define ON_OBJ_ID_TYPE         0
-#define ON_OBJ_VALUE           1
-#define ON_OBJ_ROOF                    2
 
 typedef struct private_identification_t private_identification_t;
 
@@ -218,11 +176,6 @@ struct private_identification_t {
        identification_t public;
        
        /**
-        * String representation of this ID.
-        */
-       char *string;
-       
-       /**
         * Encoded representation of this ID.
         */
        chunk_t encoded;
@@ -235,7 +188,6 @@ struct private_identification_t {
 
 static private_identification_t *identification_create(void);
 
-
 /**
  * updates a chunk (!????)
  * TODO: We should reconsider this stuff, its not really clear
@@ -253,19 +205,53 @@ void hex_str(chunk_t bin, chunk_t *str)
 {
        u_int i;
        update_chunk(str, snprintf(str->ptr,str->len,"0x"));
-       for (i=0; i < bin.len; i++)
+       for (i = 0; i < bin.len; i++)
        {
                update_chunk(str, snprintf(str->ptr,str->len,"%02X",*bin.ptr++));
        }
 }
 
 /**
+ * Remove any malicious characters from a chunk. We are very restrictive, but
+ * whe use these strings only to present it to the user.
+ */
+static chunk_t sanitize_chunk(chunk_t chunk)
+{
+       char *pos;
+       chunk_t clone = chunk_clone(chunk);
+       
+       for (pos = clone.ptr; pos < (char*)(clone.ptr + clone.len); pos++)
+       {
+               switch (*pos)
+               {
+                       case '\0':
+                       case ' ':
+                       case '*':
+                       case '-':
+                       case '.':
+                       case '/':
+                       case '0' ... '9':
+                       case ':':
+                       case '=':
+                       case '@':
+                       case 'A' ... 'Z':
+                       case '_':
+                       case 'a' ... 'z':
+                               break;
+                       default:
+                               *pos = '?';
+               }
+       }
+       return clone;
+}
+
+/**
  * Pointer is set to the first RDN in a DN
  */
 static status_t init_rdn(chunk_t dn, chunk_t *rdn, chunk_t *attribute, bool *next)
 {
-       *rdn = CHUNK_INITIALIZER;
-       *attribute = CHUNK_INITIALIZER;
+       *rdn = chunk_empty;
+       *attribute = chunk_empty;
        
        /* a DN is a SEQUENCE OF RDNs */
        if (*dn.ptr != ASN1_SEQUENCE)
@@ -298,8 +284,8 @@ static status_t get_next_rdn(chunk_t *rdn, chunk_t * attribute, chunk_t *oid, ch
        chunk_t body;
 
        /* initialize return values */
-       *oid   = CHUNK_INITIALIZER;
-       *value = CHUNK_INITIALIZER;
+       *oid   = chunk_empty;
+       *value = chunk_empty;
 
        /* if all attributes have been parsed, get next rdn */
        if (attribute->len <= 0)
@@ -387,7 +373,7 @@ static status_t get_next_rdn(chunk_t *rdn, chunk_t * attribute, chunk_t *oid, ch
  */
 static status_t dntoa(chunk_t dn, chunk_t *str)
 {
-       chunk_t rdn, oid, attribute, value;
+       chunk_t rdn, oid, attribute, value, proper;
        asn1_t type;
        int oid_code;
        bool next;
@@ -396,18 +382,14 @@ static status_t dntoa(chunk_t dn, chunk_t *str)
        status_t status = init_rdn(dn, &rdn, &attribute, &next);
 
        if (status != SUCCESS)
-       {/* a parsing error has occured */
                return status;
-       }
 
        while (next)
        {
                status = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next);
 
                if (status != SUCCESS)
-               {/* a parsing error has occured */
                        return status;
-               }
 
                if (first) 
                { /* first OID/value pair */
@@ -429,7 +411,9 @@ static status_t dntoa(chunk_t dn, chunk_t *str)
                        update_chunk(str, snprintf(str->ptr,str->len,"%s", oid_names[oid_code].name));
                }
                /* print value */
-               update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)value.len,value.ptr));
+               proper = sanitize_chunk(value);
+               update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)proper.len, proper.ptr));
+               chunk_free(&proper);
        }
        return SUCCESS;
 }
@@ -447,18 +431,15 @@ static bool same_dn(chunk_t a, chunk_t b)
 
        /* same lengths for the DNs */
        if (a.len != b.len)
-       {
                return FALSE;
-       }
+
        /* try a binary comparison first */
-       if (memcmp(a.ptr, b.ptr, b.len) == 0)
-       {
+       if (memeq(a.ptr, b.ptr, b.len))
                return TRUE;
-       }
  
        /* initialize DN parsing */
-       if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS ||
-               init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS)
+       if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS
+       ||      init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS)
        {
                return FALSE;
        }
@@ -467,36 +448,31 @@ static bool same_dn(chunk_t a, chunk_t b)
        while (next_a && next_b)
        {
                /* parse next RDNs and check for errors */
-               if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS ||  
-                       get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS)
+               if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS 
+               ||  get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS)
                {
                        return FALSE;
                }
+
                /* OIDs must agree */
                if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0)
-               {
                        return FALSE;
-               }
+
                /* same lengths for values */
                if (value_a.len != value_b.len)
-               {
                        return FALSE;
-               }
+
                /* printableStrings and email RDNs require uppercase comparison */
-               if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING ||
-                                 (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL)))
+               if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING
+               || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL)))
                {
                        if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0)
-                       {
                                return FALSE;
-                       }
                }
                else
                {
                        if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0)
-                       {
                                return FALSE;
-                       }
                }
        }
        /* both DNs must have same number of RDNs */
@@ -521,54 +497,55 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
        bool next_a, next_b;
 
        /* initialize wildcard counter */
-       *wildcards = 0;
+       if (wildcards)
+       {
+               *wildcards = 0;
+       }
 
        /* initialize DN parsing */
-       if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS ||
-               init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS)
+       if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS
+       ||      init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS)
        {
                return FALSE;
        }
+
        /* fetch next RDN pair */
        while (next_a && next_b)
        {
                /* parse next RDNs and check for errors */
-               if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS ||
-                       get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS)
+               if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS
+               ||      get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS)
                {
                        return FALSE;
                }
                /* OIDs must agree */
                if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0)
-               {
                        return FALSE;
-               }
+
                /* does rdn_b contain a wildcard? */
                if (value_b.len == 1 && *value_b.ptr == '*')
                {
-                       (*wildcards)++;
+                       if (wildcards)
+                       {
+                               (*wildcards)++;
+                       }
                        continue;
                }
                /* same lengths for values */
                if (value_a.len != value_b.len)
-               {
                        return FALSE;
-               }
+
                /* printableStrings and email RDNs require uppercase comparison */
-               if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING ||
-                       (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL)))
+               if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING
+               || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL)))
                {
                        if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0)
-                       {
                                return FALSE;
-                       }
                }
                else
                {
                        if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0)
-                       {
                                return FALSE;
-                       }
                }
        }
        /* both DNs must have same number of RDNs */
@@ -576,61 +553,13 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
        {
                return FALSE;
        }
-       /* the two DNs match! */
-       return TRUE;
-}
-
-/**
- * get string representation of a general name
- * TODO: Add support for gn types
- */
-static char *gntoa(chunk_t blob)
-{
-       asn1_ctx_t ctx;
-       chunk_t object;
-       int objectID = 0;
-       u_int level;
-       char buf[128];
-
-       asn1_init(&ctx, blob, 0, FALSE);
 
-       while (objectID < GN_OBJ_ROOF)
+       /* the two DNs match! */
+       if (wildcards)
        {
-               if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx))
-               {
-                       return NULL;
-               }
-               switch (objectID)
-               {
-                       case GN_OBJ_RFC822_NAME:
-                       case GN_OBJ_DNS_NAME:
-                       case GN_OBJ_URI:
-                               snprintf(buf, sizeof(buf), "%.*s", object.len, object.ptr);
-                               return strdup(buf);
-                       case GN_OBJ_IP_ADDRESS:
-                               if (object.len == 4 &&
-                                       inet_ntop(AF_INET, object.ptr, buf, sizeof(buf)))
-                               {
-                                       return strdup(buf);
-                               }
-                               return NULL;
-                               break;
-                       case GN_OBJ_OTHER_NAME:
-                               return strdup("(other name)");
-                       case GN_OBJ_X400_ADDRESS:
-                               return strdup("(X400 Address)");
-                       case GN_OBJ_EDI_PARTY_NAME:
-                               return strdup("(EDI party name)");
-                       case GN_OBJ_REGISTERED_ID:
-                               return strdup("(registered ID)");
-                       case GN_OBJ_DIRECTORY_NAME:
-                               return strdup("(directory name)");
-                       default:
-                               break;
-               }
-               objectID++;
+               *wildcards = min(*wildcards, MAX_WILDCARDS);
        }
-       return NULL;
+       return TRUE;
 }
 
 /**
@@ -648,13 +577,13 @@ static status_t atodn(char *src, chunk_t *dn)
                UNKNOWN_OID =   4
        } state_t;
        
-       char *wrap_mode;
-       chunk_t oid  = CHUNK_INITIALIZER;
-       chunk_t name = CHUNK_INITIALIZER;
-       chunk_t names[25]; /* max to 25 rdns */
-       int name_count = 0;
+       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 pos = 0;
+       int i = 0;
        asn1_t rdn_type;
        state_t state = SEARCH_OID;
        status_t status = SUCCESS;
@@ -678,22 +607,22 @@ static status_t atodn(char *src, chunk_t *dn)
                                }
                                else
                                {
-                                       for (pos = 0; pos < X501_RDN_ROOF; pos++)
+                                       for (i = 0; i < X501_RDN_ROOF; i++)
                                        {
-                                               if (strlen(x501rdns[pos].name) == oid.len &&
-                                                                                         strncasecmp(x501rdns[pos].name, oid.ptr, oid.len) == 0)
+                                               if (strlen(x501rdns[i].name) == oid.len
+                                               &&  strncasecmp(x501rdns[i].name, oid.ptr, oid.len) == 0)
                                                {
                                                        break; /* found a valid OID */
                                                }
                                        }
-                                       if (pos == X501_RDN_ROOF)
+                                       if (i == X501_RDN_ROOF)
                                        {
                                                status = NOT_SUPPORTED;
                                                state = UNKNOWN_OID;
                                                break;
                                        }
                                        /* reset oid and change state */
-                                       oid = CHUNK_INITIALIZER;
+                                       oid = chunk_empty;
                                        state = SEARCH_NAME;
                                }
                                break;
@@ -711,36 +640,33 @@ static status_t atodn(char *src, chunk_t *dn)
                                {
                                        name.len++;
                                        if (*src == ' ')
-                                       {
                                                whitespace++;
-                                       }
                                        else
-                                       {
                                                whitespace = 0;
-                                       }
                                }
                                else
                                {
                                        name.len -= whitespace;
-                                       rdn_type = (x501rdns[pos].type == ASN1_PRINTABLESTRING
-                                                       && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type;
+                                       rdn_type = (x501rdns[i].type == ASN1_PRINTABLESTRING
+                                                       && !is_printablestring(name))? ASN1_T61STRING : x501rdns[i].type;
                                        
-                                       if (name_count < 25)
+                                       if (rdn_count < RDN_MAX)
                                        {
-                                               names[name_count++] = 
+                                               rdns[rdn_count] = 
                                                                asn1_wrap(ASN1_SET, "m",
-                                                                                 asn1_wrap(ASN1_SEQUENCE, "mm",
-                                                                                                 asn1_wrap(ASN1_OID, "c", x501rdns[pos].oid),
-                                                                                                 asn1_wrap(rdn_type, "c", name)
-                                                                                                  )
-                                                                                );
+                                                                       asn1_wrap(ASN1_SEQUENCE, "mm",
+                                                                               asn1_wrap(ASN1_OID, "c", x501rdns[i].oid),
+                                                                               asn1_wrap(rdn_type, "c", name)
+                                                                       )
+                                                               );
+                                               dn_len += rdns[rdn_count++].len;
                                        }
                                        else
                                        {
                                                status = OUT_OF_RES;
                                        }
                                        /* reset name and change state */
-                                       name = CHUNK_INITIALIZER;
+                                       name = chunk_empty;
                                        state = SEARCH_OID;
                                }
                                break;
@@ -749,21 +675,23 @@ static status_t atodn(char *src, chunk_t *dn)
                }
        } while (*src++ != '\0');
 
-       
        /* build the distinguished name sequence */
-       wrap_mode = alloca(26);
-       memset(wrap_mode, 0, 26);
-       memset(wrap_mode, 'm', name_count);
-       *dn = asn1_wrap(ASN1_SEQUENCE, wrap_mode, 
-                                       names[0], names[1], names[2], names[3], names[4], 
-                                       names[5], names[6], names[7], names[8], names[9],
-                                       names[10], names[11], names[12], names[13], names[14], 
-                                       names[15], names[16], names[17], names[18], names[19], 
-                                       names[20], names[21], names[22], names[23], names[24]);
+   {
+               int i;
+               u_char *pos = build_asn1_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_INITIALIZER;
+               *dn = chunk_empty;
        }
        return status;
 }
@@ -783,143 +711,282 @@ static id_type_t get_type(private_identification_t *this)
 {
        return this->type;
 }
-       
-/**
- * Implementation of identification_t.get_string.
- */
-static char *get_string(private_identification_t *this)
-{
-       return this->string;
-}
 
 /**
  * Implementation of identification_t.contains_wildcards.
  */
 static bool contains_wildcards(private_identification_t *this)
 {
-       if (this->type == ID_ANY ||
-               memchr(this->encoded.ptr, '*', this->encoded.len) != NULL)
+       switch (this->type)
        {
-               return TRUE;
+               case ID_ANY:
+                       return TRUE;
+               case ID_FQDN:
+               case ID_RFC822_ADDR:
+                       return memchr(this->encoded.ptr, '*', this->encoded.len) != NULL;
+               case ID_DER_ASN1_DN:
+                       /* TODO */
+               default:
+                       return FALSE;
+               
        }
-       return FALSE;
 }
 
 /**
- * Default implementation of identification_t.equals and identification_t.belongs_to.
+ * Default implementation of identification_t.equals.
  * compares encoded chunk for equality.
  */
-static bool equals_binary(private_identification_t *this,private_identification_t *other)
+static bool equals_binary(private_identification_t *this, private_identification_t *other)
 {
        if (this->type == other->type)
        {
-               if (this->encoded.len == other->encoded.len &&
-                       memcmp(this->encoded.ptr, other->encoded.ptr, this->encoded.len) == 0)
+               if (this->type == ID_ANY)
                {
                        return TRUE;
                }
+               return chunk_equals(this->encoded, other->encoded);
        }
-       return FALSE;
+       return FALSE;                                           
 }
 
 /**
- * Special implementation of identification_t.equals for ID_DER_ASN1_DN
+ * Special implementation of identification_t.equals for ID_DER_ASN1_DN.
  */
-static bool equals_dn(private_identification_t *this, private_identification_t *other)
+static bool equals_dn(private_identification_t *this,
+                                         private_identification_t *other)
 {
        return same_dn(this->encoded, other->encoded);
 }
 
 /**
- * Special implementation of identification_t.belongs_to for ID_RFC822_ADDR/ID_FQDN.
- * checks for a wildcard in other-string, and compares it against this-string.
+ * Special implementation of identification_t.equals for RFC822 and FQDN.
  */
-static bool belongs_to_wc_string(private_identification_t *this, private_identification_t *other)
+static bool equals_strcasecmp(private_identification_t *this,
+                                                         private_identification_t *other)
+{
+       /* we do some extra sanity checks to check for invalid IDs with a 
+        * terminating null in it. */
+       if (this->encoded.len == other->encoded.len &&
+               memchr(this->encoded.ptr, 0, this->encoded.len) == NULL &&
+               memchr(other->encoded.ptr, 0, other->encoded.len) == NULL &&
+               strncasecmp(this->encoded.ptr, other->encoded.ptr, this->encoded.len) == 0)
+       {
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * Default implementation of identification_t.matches.
+ */
+static bool matches_binary(private_identification_t *this, 
+                                                  private_identification_t *other, int *wildcards)
 {
-       char *this_str, *other_str, *pos;
-       
        if (other->type == ID_ANY)
        {
+               if (wildcards)
+               {
+                       *wildcards = MAX_WILDCARDS;
+               }
                return TRUE;
        }
+       if (wildcards)
+       {
+               *wildcards = 0;
+       }
+       return this->type == other->type &&
+                                                       chunk_equals(this->encoded, other->encoded);
+}
+
+/**
+ * Special implementation of identification_t.matches for ID_RFC822_ADDR/ID_FQDN.
+ * Checks for a wildcard in other-string, and compares it against this-string.
+ */
+static bool matches_string(private_identification_t *this,
+                                                  private_identification_t *other, int *wildcards)
+{
+       u_int len = other->encoded.len;
        
-       if (this->type == other->type)
+       if (other->type == ID_ANY)
        {
-               /* try a binary comparison first */
-               if (equals_binary(this, other))
+               if (wildcards)
                {
-                       return TRUE;
+                       *wildcards = MAX_WILDCARDS;
                }
+               return TRUE;
        }
-       if (other->encoded.len > 0 &&
-                  *(other->encoded.ptr) == '*')
+       
+       if (this->type != other->type)
+               return FALSE;
+
+       /* try a binary comparison first */
+       if (equals_binary(this, other))
        {
-               if (other->encoded.len == 1)
+               if (wildcards)
                {
-                       /* other contains just a wildcard, and therefore matches anything */
-                       return TRUE;
+                       *wildcards = 0;
                }
-               /* We strdup chunks, since they are NOT null-terminated */
-               this_str = strndupa(this->encoded.ptr, this->encoded.len);
-               other_str = strndupa(other->encoded.ptr + 1, other->encoded.len - 1);
-               pos = strstr(this_str, other_str);
-               if (pos != NULL)
+               return TRUE;
+       }
+       
+       if (len == 0 || this->encoded.len < len)
+               return FALSE;
+
+       /* check for single wildcard at the head of the string */
+       if (*other->encoded.ptr == '*')
+       {
+               if (wildcards)
                {
-                       /* ok, other is contained in this, but there may be more characters, so check it */
-                       if (strlen(pos) == strlen(other_str))
-                       {
-                               return TRUE;
-                       }
+                       *wildcards = 1;
                }
+
+               /* single asterisk matches any string */
+               if (len-- == 1)
+                       return TRUE;
+
+               if (memeq(this->encoded.ptr + this->encoded.len - len, other->encoded.ptr + 1, len))
+                       return TRUE;
        }
        
        return FALSE;
 }
 
 /**
- * Special implementation of identification_t.belongs_to for ID_ANY.
+ * Special implementation of identification_t.matches for ID_ANY.
  * ANY matches only another ANY, but nothing other
  */
-static bool belongs_to_any(private_identification_t *this, private_identification_t *other)
-{      
-       if (other->type == ID_ANY)
+static bool matches_any(private_identification_t *this,
+                                               private_identification_t *other, int *wildcards)
+{
+       if (wildcards)
        {
-               return TRUE;
+               *wildcards = 0;
        }
-       return FALSE;
+       return other->type == ID_ANY;
 }
 
 /**
- * Special implementation of identification_t.belongs_to for ID_DER_ASN1_DN.
+ * Special implementation of identification_t.matches for ID_DER_ASN1_DN.
  * ANY matches any, even ANY, thats why its there...
  */
-static bool belongs_to_dn(private_identification_t *this, private_identification_t *other)
+static bool matches_dn(private_identification_t *this,
+                                          private_identification_t *other, int *wildcards)
 {
-       int wildcards;
-       
        if (other->type == ID_ANY)
        {
+               if (wildcards)
+               {
+                       *wildcards = MAX_WILDCARDS;
+               }
                return TRUE;
        }
        
        if (this->type == other->type)
        {
-               return match_dn(this->encoded, other->encoded, &wildcards);
+               return match_dn(this->encoded, other->encoded, wildcards);
        }
        return FALSE;
 }
 
 /**
+ * output handler in printf()
+ */
+static int print(FILE *stream, const struct printf_info *info,
+                                const void *const *args)
+{
+       private_identification_t *this = *((private_identification_t**)(args[0]));
+       char buf[BUF_LEN];
+       chunk_t proper, buf_chunk = chunk_from_buf(buf);
+       int written;
+       
+       if (this == NULL)
+       {
+               return fprintf(stream, "(null)");
+       }
+       
+       switch (this->type)
+       {
+               case ID_ANY:
+                       return fprintf(stream, "%%any");
+               case ID_IPV4_ADDR:
+                       if (this->encoded.len < sizeof(struct in_addr) ||
+                               inet_ntop(AF_INET, this->encoded.ptr, buf, sizeof(buf)) == NULL)
+                       {
+                               return fprintf(stream, "(invalid ID_IPV4_ADDR)");
+                       }
+                       else
+                       {
+                               return fprintf(stream, "%s", buf);
+                       }
+               case ID_IPV6_ADDR:
+                       if (this->encoded.len < sizeof(struct in6_addr) ||
+                               inet_ntop(AF_INET6, this->encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL)
+                       {
+                               return fprintf(stream, "(invalid ID_IPV6_ADDR)");
+                       }
+                       else
+                       {
+                               return fprintf(stream, "%s", buf);
+                       }
+               case ID_FQDN:
+               {
+                       proper = sanitize_chunk(this->encoded);
+                       written = fprintf(stream, "%.*s", proper.len, proper.ptr);
+                       chunk_free(&proper);
+                       return written;
+               }
+               case ID_RFC822_ADDR:
+               {
+                       proper = sanitize_chunk(this->encoded);
+                       written = fprintf(stream, "%.*s", proper.len, proper.ptr);
+                       chunk_free(&proper);
+                       return written;
+               }
+               case ID_DER_ASN1_DN:
+               {
+                       snprintf(buf, sizeof(buf), "%.*s", this->encoded.len, this->encoded.ptr);
+                       /* TODO: whats returned on failure?*/
+                       dntoa(this->encoded, &buf_chunk);
+                       return fprintf(stream, "%s", buf);
+               }
+               case ID_DER_ASN1_GN:
+                       return fprintf(stream, "(ASN.1 general Name");
+               case ID_KEY_ID:
+                       return fprintf(stream, "(KEY_ID)");
+               case ID_DER_ASN1_GN_URI:
+               {
+                       proper = sanitize_chunk(this->encoded);
+                       written = fprintf(stream, "%.*s", proper.len, proper.ptr);
+                       chunk_free(&proper);
+                       return written;
+               }
+               default:
+                       return fprintf(stream, "(unknown ID type: %d)", this->type);
+       }
+}
+
+/**
+ * register printf() handlers
+ */
+static void __attribute__ ((constructor))print_register()
+{
+       register_printf_function(PRINTF_IDENTIFICATION, print, arginfo_ptr);
+}
+
+/**
  * Implementation of identification_t.clone.
  */
-static identification_t *clone(private_identification_t *this)
+static identification_t *clone_(private_identification_t *this)
 {
        private_identification_t *clone = identification_create();
        
        clone->type = this->type;
-       clone->encoded = chunk_clone(this->encoded);
-       clone->string = malloc(strlen(this->string) + 1);
-       strcpy(clone->string, this->string);
+       if (this->encoded.len)
+       {
+               clone->encoded = chunk_clone(this->encoded);
+       }
+       clone->public.equals = this->public.equals;
+       clone->public.matches = this->public.matches;
        
        return &clone->public;
 }
@@ -929,9 +996,8 @@ static identification_t *clone(private_identification_t *this)
  */
 static void destroy(private_identification_t *this)
 {
-       free(this->string);
-       free(this->encoded.ptr);
-       free(this);     
+       chunk_free(&this->encoded);
+       free(this);
 }
 
 /**
@@ -943,16 +1009,14 @@ static private_identification_t *identification_create(void)
        
        this->public.get_encoding = (chunk_t (*) (identification_t*))get_encoding;
        this->public.get_type = (id_type_t (*) (identification_t*))get_type;
-       this->public.get_string = (char* (*) (identification_t*))get_string;
        this->public.contains_wildcards = (bool (*) (identification_t *this))contains_wildcards;
-       this->public.clone = (identification_t* (*) (identification_t*))clone;
+       this->public.clone = (identification_t* (*) (identification_t*))clone_;
        this->public.destroy = (void (*) (identification_t*))destroy;
        /* we use these as defaults, the may be overloaded for special ID types */
        this->public.equals = (bool (*) (identification_t*,identification_t*))equals_binary;
-       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))equals_binary;
+       this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_binary;
        
-       this->string = NULL;
-       this->encoded = CHUNK_INITIALIZER;
+       this->encoded = chunk_empty;
        
        return this;
 }
@@ -963,42 +1027,42 @@ static private_identification_t *identification_create(void)
 identification_t *identification_create_from_string(char *string)
 {
        private_identification_t *this = identification_create();
-       
+
+       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
-                * discard optional @ character in front of DN
                 */
-               if (atodn((*string == '@') ? string + 1 : string, &this->encoded) != SUCCESS)
+               if (atodn(string, &this->encoded) != SUCCESS)
                {
                        free(this);
                        return NULL;
                }
-               this->string = strdup(string);
                this->type = ID_DER_ASN1_DN;
                this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn;
-               this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn;
+               this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_dn;
                return &this->public;
        }
        else if (strchr(string, '@') == NULL)
        {
-               if (strcmp(string, "%any") == 0 ||
-                       strcmp(string, "0.0.0.0") == 0 ||
-                       strcmp(string, "*") == 0 ||
-                       strcmp(string, "::") == 0||
-                       strcmp(string, "0::0") == 0)
+               if (streq(string, "%any")
+               ||      streq(string, "0.0.0.0")
+               ||      streq(string, "*")
+               ||      streq(string, "::")
+               ||      streq(string, "0::0"))
                {
                        /* any ID will be accepted */
                        this->type = ID_ANY;
-                       this->string = strdup("%any");
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any;
+                       this->public.matches = (bool (*)
+                                       (identification_t*,identification_t*,int*))matches_any;
                        return &this->public;
                }
                else
                {
-                       /* TODO: Pluto resolve domainnames without '@' to IPv4/6 address. Is this really needed? */
-                       
                        if (strchr(string, ':') == NULL)
                        {
                                /* try IPv4 */
@@ -1011,7 +1075,6 @@ identification_t *identification_create_from_string(char *string)
                                        return NULL;
                                }
                                this->encoded = chunk_clone(chunk);
-                               this->string = strdup(string);
                                this->type = ID_IPV4_ADDR;
                                return &(this->public);
                        }
@@ -1027,7 +1090,6 @@ identification_t *identification_create_from_string(char *string)
                                        return NULL;
                                }
                                this->encoded = chunk_clone(chunk);
-                               this->string = strdup(string);
                                this->type = ID_IPV6_ADDR;
                                return &(this->public);
                        }
@@ -1039,27 +1101,31 @@ identification_t *identification_create_from_string(char *string)
                {
                        if (*(string + 1) == '#')
                        {
-                               /* TODO: Pluto handles '#' as hex encoded ASN1/KEY ID. Do we need this, too? */
+                               /* TODO: Pluto handles '#' as hex encoded ID_KEY_ID. */
                                free(this);
                                return NULL;
                        }
                        else
                        {
                                this->type = ID_FQDN;
-                               this->string = strdup(string + 1); /* discard @ */
                                this->encoded.ptr = strdup(string + 1);
                                this->encoded.len = strlen(string + 1);
-                               this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string;
+                               this->public.matches = (bool (*) 
+                                               (identification_t*,identification_t*,int*))matches_string;
+                               this->public.equals = (bool (*)
+                                                       (identification_t*,identification_t*))equals_strcasecmp;
                                return &(this->public);
                        }
                }
                else
                {
                        this->type = ID_RFC822_ADDR;
-                       this->string = strdup(string);
                        this->encoded.ptr = strdup(string);
                        this->encoded.len = strlen(string);
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string;
+                       this->public.matches = (bool (*) 
+                                       (identification_t*,identification_t*,int*))matches_string;
+                       this->public.equals = (bool (*)
+                                               (identification_t*,identification_t*))equals_strcasecmp;
                        return &(this->public);
                }
        }
@@ -1071,66 +1137,32 @@ identification_t *identification_create_from_string(char *string)
 identification_t *identification_create_from_encoding(id_type_t type, chunk_t encoded)
 {
        private_identification_t *this = identification_create();
-       char buf[256];
-       chunk_t buf_chunk = chunk_from_buf(buf);
-       char *pos;
-       
        this->type = type;
        switch (type)
        {
                case ID_ANY:
-                       this->string = strdup("%any");
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any;
-                       break;
-               case ID_IPV4_ADDR:
-                       if (encoded.len < sizeof(struct in_addr) ||
-                               inet_ntop(AF_INET, encoded.ptr, buf, sizeof(buf)) == NULL)
-                       {
-                               this->string = strdup("(invalid ID_IPV4_ADDR)");
-                       }
-                       else
-                       {
-                               this->string = strdup(buf);
-                       }
-                       break;
-               case ID_IPV6_ADDR:
-                       if (encoded.len < sizeof(struct in6_addr) ||
-                               inet_ntop(AF_INET6, encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL)
-                       {
-                               this->string = strdup("(invalid ID_IPV6_ADDR)");
-                       }
-                       else
-                       {
-                               this->string = strdup(buf);
-                       }
+                       this->public.matches = (bool (*)
+                                       (identification_t*,identification_t*,int*))matches_any;
                        break;
                case ID_FQDN:
-                       snprintf(buf, sizeof(buf), "@%.*s", encoded.len, encoded.ptr);
-                       this->string = strdup(buf);
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string;
-                       break;
                case ID_RFC822_ADDR:
-                       snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr);
-                       this->string = strdup(buf);
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string;
+                       this->public.matches = (bool (*)
+                                       (identification_t*,identification_t*,int*))matches_string;
+                       this->public.equals = (bool (*)
+                                               (identification_t*,identification_t*))equals_strcasecmp;
                        break;
                case ID_DER_ASN1_DN:
-                       snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr);
-                       /* TODO: whats returned on failure */
-                       dntoa(encoded, &buf_chunk);
-                       this->string = strdup(buf);
-                       this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn;
-                       this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn;
+                       this->public.equals = (bool (*)
+                                       (identification_t*,identification_t*))equals_dn;
+                       this->public.matches = (bool (*)
+                                       (identification_t*,identification_t*,int*))matches_dn;
                        break;
+               case ID_IPV4_ADDR:
+               case ID_IPV6_ADDR:
                case ID_DER_ASN1_GN:
-                       this->string = gntoa(encoded);
-                       break;
                case ID_KEY_ID:
-                       this->string = strdup("(unparsed KEY_ID)");
-                       break;
+               case ID_DER_ASN1_GN_URI:
                default:
-                       snprintf(buf, sizeof(buf), "(invalid ID type: %d)", type);
-                       this->string = strdup(buf);
                        break;
        }
        
@@ -1139,14 +1171,6 @@ identification_t *identification_create_from_encoding(id_type_t type, chunk_t en
        {
                this->encoded = chunk_clone(encoded);
        }
-       
-       /* remove unprintable chars in string */
-       for (pos = this->string; *pos != '\0'; pos++)
-       {
-               if (!isprint(*pos))
-               {
-                       *pos = '?';
-               }
-       }
        return &(this->public);
 }
+