updated XML interface to new schema
[strongswan.git] / src / libstrongswan / utils / identification.c
index e193a3e..b842a8c 100644 (file)
 #include <string.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <printf.h>
 
-#include "definitions.h"
 #include "identification.h"
 
 #include <asn1/asn1.h>
 
-/** 
- * String mappings for id_type_t.
- */
-
-static const char *const id_type_name[] = {
+ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID,
        "ID_ANY",
        "ID_IPV4_ADDR",
        "ID_FQDN",
@@ -50,11 +46,11 @@ static const char *const id_type_name[] = {
        "ID_IPV6_ADDR_RANGE",
        "ID_DER_ASN1_DN",
        "ID_DER_ASN1_GN",
-       "ID_KEY_ID",
-};
+       "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);
 
-enum_names id_type_names =
-    { ID_ANY, ID_KEY_ID, id_type_name, NULL };
 
 /**
  * X.501 acronyms for well known object identifiers (OIDs)
@@ -180,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;
@@ -221,12 +212,46 @@ void hex_str(chunk_t bin, chunk_t *str)
 }
 
 /**
+ * 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)
@@ -259,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)
@@ -348,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;
@@ -386,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;
 }
@@ -470,7 +497,10 @@ 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
@@ -495,7 +525,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
                /* does rdn_b contain a wildcard? */
                if (value_b.len == 1 && *value_b.ptr == '*')
                {
-                       (*wildcards)++;
+                       if (wildcards)
+                       {
+                               (*wildcards)++;
+                       }
                        continue;
                }
                /* same lengths for values */
@@ -522,7 +555,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
        }
 
        /* the two DNs match! */
-       *wildcards = min(*wildcards, MAX_WILDCARDS);
+       if (wildcards)
+       {
+               *wildcards = min(*wildcards, MAX_WILDCARDS);
+       }
        return TRUE;
 }
 
@@ -541,8 +577,8 @@ static status_t atodn(char *src, chunk_t *dn)
                UNKNOWN_OID =   4
        } state_t;
        
-       chunk_t oid  = CHUNK_INITIALIZER;
-       chunk_t name = CHUNK_INITIALIZER;
+       chunk_t oid  = chunk_empty;
+       chunk_t name = chunk_empty;
        chunk_t rdns[RDN_MAX];
        int rdn_count = 0;
        int dn_len = 0;
@@ -586,7 +622,7 @@ static status_t atodn(char *src, chunk_t *dn)
                                                break;
                                        }
                                        /* reset oid and change state */
-                                       oid = CHUNK_INITIALIZER;
+                                       oid = chunk_empty;
                                        state = SEARCH_NAME;
                                }
                                break;
@@ -630,7 +666,7 @@ static status_t atodn(char *src, chunk_t *dn)
                                                status = OUT_OF_RES;
                                        }
                                        /* reset name and change state */
-                                       name = CHUNK_INITIALIZER;
+                                       name = chunk_empty;
                                        state = SEARCH_OID;
                                }
                                break;
@@ -655,7 +691,7 @@ static status_t atodn(char *src, chunk_t *dn)
        if (status != SUCCESS)
        {
                free(dn->ptr);
-               *dn = CHUNK_INITIALIZER;
+               *dn = chunk_empty;
        }
        return status;
 }
@@ -675,21 +711,25 @@ 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)
 {
-       return this->type == ID_ANY || strchr(this->string, '*') != NULL;
+       switch (this->type)
+       {
+               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;
+               
+       }
 }
 
 /**
@@ -698,39 +738,81 @@ static bool contains_wildcards(private_identification_t *this)
  */
 static bool equals_binary(private_identification_t *this, private_identification_t *other)
 {
-       return this->type == other->type && chunk_equals(this->encoded, other->encoded);
+       if (this->type == other->type)
+       {
+               if (this->type == ID_ANY)
+               {
+                       return TRUE;
+               }
+               return chunk_equals(this->encoded, other->encoded);
+       }
+       return FALSE;                                           
 }
 
 /**
  * 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.equals for RFC822 and FQDN.
+ */
+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)
-{      
-       *wildcards = 0;
-       return this->type == other->type && chunk_equals(this->encoded, other->encoded);
+static bool matches_binary(private_identification_t *this, 
+                                                  private_identification_t *other, int *wildcards)
+{
+       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)
+static bool matches_string(private_identification_t *this,
+                                                  private_identification_t *other, int *wildcards)
 {
        u_int len = other->encoded.len;
        
        if (other->type == ID_ANY)
        {
-               *wildcards = MAX_WILDCARDS;
+               if (wildcards)
+               {
+                       *wildcards = MAX_WILDCARDS;
+               }
                return TRUE;
        }
        
@@ -740,7 +822,10 @@ static bool matches_string(private_identification_t *this, private_identificatio
        /* try a binary comparison first */
        if (equals_binary(this, other))
        {
-               *wildcards = 0;
+               if (wildcards)
+               {
+                       *wildcards = 0;
+               }
                return TRUE;
        }
        
@@ -750,7 +835,10 @@ static bool matches_string(private_identification_t *this, private_identificatio
        /* check for single wildcard at the head of the string */
        if (*other->encoded.ptr == '*')
        {
-               *wildcards = 1;
+               if (wildcards)
+               {
+                       *wildcards = 1;
+               }
 
                /* single asterisk matches any string */
                if (len-- == 1)
@@ -767,10 +855,13 @@ static bool matches_string(private_identification_t *this, private_identificatio
  * Special implementation of identification_t.matches for ID_ANY.
  * ANY matches only another ANY, but nothing other
  */
-static bool matches_any(private_identification_t *this, private_identification_t *other,
-       int *wildcards)
-{      
-       *wildcards = 0;
+static bool matches_any(private_identification_t *this,
+                                               private_identification_t *other, int *wildcards)
+{
+       if (wildcards)
+       {
+               *wildcards = 0;
+       }
        return other->type == ID_ANY;
 }
 
@@ -778,12 +869,15 @@ static bool matches_any(private_identification_t *this, private_identification_t
  * Special implementation of identification_t.matches for ID_DER_ASN1_DN.
  * ANY matches any, even ANY, thats why its there...
  */
-static bool matches_dn(private_identification_t *this, private_identification_t *other,
-       int *wildcards)
+static bool matches_dn(private_identification_t *this,
+                                          private_identification_t *other, int *wildcards)
 {
        if (other->type == ID_ANY)
        {
-               *wildcards = MAX_WILDCARDS;
+               if (wildcards)
+               {
+                       *wildcards = MAX_WILDCARDS;
+               }
                return TRUE;
        }
        
@@ -795,17 +889,102 @@ static bool matches_dn(private_identification_t *this, private_identification_t
 }
 
 /**
+ * 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;
        
@@ -817,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);
 }
 
 /**
@@ -831,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.matches = (bool (*) (identification_t*,identification_t*,int*))matches_binary;
        
-       this->string = NULL;
-       this->encoded = CHUNK_INITIALIZER;
+       this->encoded = chunk_empty;
        
        return this;
 }
@@ -853,8 +1029,9 @@ 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.
@@ -865,7 +1042,6 @@ identification_t *identification_create_from_string(char *string)
                        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.matches = (bool (*) (identification_t*,identification_t*,int*))matches_dn;
@@ -881,8 +1057,8 @@ identification_t *identification_create_from_string(char *string)
                {
                        /* any ID will be accepted */
                        this->type = ID_ANY;
-                       this->string = strdup("%any");
-                       this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_any;
+                       this->public.matches = (bool (*)
+                                       (identification_t*,identification_t*,int*))matches_any;
                        return &this->public;
                }
                else
@@ -899,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);
                        }
@@ -915,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);
                        }
@@ -934,20 +1108,24 @@ identification_t *identification_create_from_string(char *string)
                        else
                        {
                                this->type = ID_FQDN;
-                               this->string = strdup(string);
                                this->encoded.ptr = strdup(string + 1);
                                this->encoded.len = strlen(string + 1);
-                               this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_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.matches = (bool (*) (identification_t*,identification_t*,int*))matches_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);
                }
        }
@@ -958,72 +1136,33 @@ identification_t *identification_create_from_string(char *string)
  */
 identification_t *identification_create_from_encoding(id_type_t type, chunk_t encoded)
 {
-       char *pos;
-       char buf[BUF_LEN];
-       chunk_t buf_chunk = chunk_from_buf(buf);
        private_identification_t *this = identification_create();
-       
        this->type = type;
-
        switch (type)
        {
                case ID_ANY:
-                       this->string = strdup("%any");
-                       this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_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.matches = (bool (*) (identification_t*,identification_t*,int*))matches_string;
-                       break;
                case ID_RFC822_ADDR:
-                       snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr);
-                       this->string = strdup(buf);
-                       this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_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.matches = (bool (*) (identification_t*,identification_t*,int*))matches_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 = strdup("ASN.1 coded generalName");
-                       break;
                case ID_KEY_ID:
-                       this->string = strdup("(KEY_ID)");
-                       break;
                case ID_DER_ASN1_GN_URI:
-                       snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr);
-                       this->string = strdup(buf);
-                       break;
                default:
-                       snprintf(buf, sizeof(buf), "(invalid ID type: %d)", type);
-                       this->string = strdup(buf);
                        break;
        }
        
@@ -1032,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);
 }
+