reworked parsing and matching of subjectAltNames
authorAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 29 May 2006 07:06:02 +0000 (07:06 -0000)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 29 May 2006 07:06:02 +0000 (07:06 -0000)
src/libstrongswan/crypto/x509.c
src/libstrongswan/crypto/x509.h
src/libstrongswan/utils/identification.c
src/libstrongswan/utils/identification.h

index 9123b63..3e0f039 100755 (executable)
@@ -27,6 +27,8 @@
 
 #include "x509.h"
 
+#include <types.h>
+#include <definitions.h>
 #include <asn1/oid.h>
 #include <asn1/asn1.h>
 #include <asn1/pem.h>
 #include <utils/linked_list.h>
 #include <utils/identification.h>
 
-#define BUF_LEN 512
 #define BITS_PER_BYTE  8
 #define RSA_MIN_OCTETS (1024 / BITS_PER_BYTE)
 #define RSA_MIN_OCTETS_UGH     "RSA modulus too small for security: less than 1024 bits"
 #define RSA_MAX_OCTETS (8192 / BITS_PER_BYTE)
 #define RSA_MAX_OCTETS_UGH     "RSA modulus too large: more than 8192 bits"
 
-logger_t *logger;
+#define CERT_WARNING_INTERVAL  30      /* days */
 
-typedef struct generalName_t generalName_t;
+logger_t *logger;
 
 /**
- * A generalName, chainable in a list
+ * Different kinds of generalNames
  */
-struct generalName_t {
-       generalName_t *next;
-       generalNames_t kind;
-       chunk_t name;
+typedef enum generalNames_t generalNames_t;
+
+enum generalNames_t {
+       GN_OTHER_NAME =         0,
+       GN_RFC822_NAME =        1,
+       GN_DNS_NAME =           2,
+       GN_X400_ADDRESS =       3,
+       GN_DIRECTORY_NAME =     4,
+       GN_EDI_PARTY_NAME = 5,
+       GN_URI =                        6,
+       GN_IP_ADDRESS =         7,
+       GN_REGISTERED_ID =      8,
 };
 
 typedef struct private_x509_t private_x509_t;
@@ -111,11 +120,6 @@ struct private_x509_t {
        linked_list_t *subjectAltNames;
        
        /**
-        * List of identification_t's representing issuerAltNames
-        */
-       linked_list_t *issuerAltNames;
-       
-       /**
         * List of identification_t's representing crlDistributionPoints
         */
        linked_list_t *crlDistributionPoints;
@@ -154,6 +158,50 @@ struct private_x509_t {
 };
 
 /**
+ * ASN.1 definition of generalName 
+ */
+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
+
+/**
+ * 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
+/**
  * ASN.1 definition of a basicConstraints extension 
  */
 static const asn1Object_t basicConstraintsObjects[] = {
@@ -331,84 +379,134 @@ static bool equals(private_x509_t *this, private_x509_t *other)
 }
 
 /**
- * encode a linked list of subjectAltNames
+ * extracts the basicConstraints extension
  */
-chunk_t build_subjectAltNames(generalName_t *subjectAltNames)
+static bool parse_basicConstraints(chunk_t blob, int level0)
 {
-       u_char *pos;
-       chunk_t names;
-       size_t len = 0;
-       generalName_t *gn = subjectAltNames;
-       
-       /* compute the total size of the ASN.1 attributes object */
-       while (gn != NULL)
-       {
-               len += gn->name.len;
-               gn = gn->next;
-       }
+       asn1_ctx_t ctx;
+       chunk_t object;
+       u_int level;
+       int objectID = 0;
+       bool isCA = FALSE;
 
-       pos = build_asn1_object(&names, ASN1_SEQUENCE, len);
+       asn1_init(&ctx, blob, level0, FALSE);
 
-       gn = subjectAltNames;
-       while (gn != NULL)
-       {
-               memcpy(pos, gn->name.ptr, gn->name.len); 
-               pos += gn->name.len;
-               gn = gn->next;
-       }
+       while (objectID < BASIC_CONSTRAINTS_ROOF) {
 
-       return asn1_wrap(ASN1_SEQUENCE, "cm",
-                                        ASN1_subjectAltName_oid,
-                                        asn1_wrap(ASN1_OCTET_STRING, "m", names)
-                                       );
+               if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
+               {
+                       break;
+               }
+               if (objectID == BASIC_CONSTRAINTS_CA)
+               {
+                       isCA = object.len && *object.ptr;
+                       logger->log(logger, CONTROL|LEVEL1, "  %s", isCA ? "TRUE" : "FALSE");
+               }
+               objectID++;
+       }
+       return isCA;
 }
 
-/**
- * free the dynamic memory used to store generalNames
+/*
+ * extracts an otherName
  */
-void free_generalNames(generalName_t* gn, bool free_name)
+static bool
+parse_otherName(chunk_t blob, int level0)
 {
-       while (gn != NULL)
+       asn1_ctx_t ctx;
+       chunk_t object;
+       int objectID = 0;
+       u_int level;
+       int oid = OID_UNKNOWN;
+
+       asn1_init(&ctx, blob, level0, FALSE);
+
+       while (objectID < ON_OBJ_ROOF)
        {
-               generalName_t *gn_top = gn;
-               if (free_name)
+               if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx))
+                       return FALSE;
+
+               switch (objectID)
                {
-                       free(gn->name.ptr);
+                       case ON_OBJ_ID_TYPE:
+                               oid = known_oid(object);
+                               break;
+                       case ON_OBJ_VALUE:
+                               if (oid == OID_XMPP_ADDR)
+                               {
+                                       if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING, level + 1, "xmppAddr"))
+                                               return FALSE;
+                               }
+                               break;
+                       default:
+                               break;
                }
-               gn = gn->next;
-               free(gn_top);
+               objectID++;
        }
+       return TRUE;
 }
 
-/**
- * extracts the basicConstraints extension
+/*
+ * extracts a generalName
  */
-static bool parse_basicConstraints(chunk_t blob, int level0)
+static identification_t *parse_generalName(chunk_t blob, int level0)
 {
+       u_char buf[BUF_LEN];
        asn1_ctx_t ctx;
        chunk_t object;
-       u_int level;
        int objectID = 0;
-       bool isCA = FALSE;
+       u_int level;
 
        asn1_init(&ctx, blob, level0, FALSE);
 
-       while (objectID < BASIC_CONSTRAINTS_ROOF) {
+       while (objectID < GN_OBJ_ROOF)
+       {
+               id_type_t id_type = ID_ANY;
+       
+               if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx))
+                       return NULL;
 
-               if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
+               switch (objectID)
                {
-                       break;
+                       case GN_OBJ_RFC822_NAME:
+                               id_type = ID_RFC822_ADDR;
+                               break;
+                       case GN_OBJ_DNS_NAME:
+                               id_type = ID_FQDN;
+                               break;
+                       case GN_OBJ_URI:
+                               id_type = ID_DER_ASN1_GN_URI;
+                               break;
+                       case GN_OBJ_DIRECTORY_NAME:
+                               id_type = ID_DER_ASN1_DN;
+                       break;
+                       case GN_OBJ_IP_ADDRESS:
+                               id_type = ID_IPV4_ADDR;
+                               break;
+                       case GN_OBJ_OTHER_NAME:
+                               if (!parse_otherName(object, level + 1))
+                                       return NULL;
+                               break;
+                       case GN_OBJ_X400_ADDRESS:
+                       case GN_OBJ_EDI_PARTY_NAME:
+                       case GN_OBJ_REGISTERED_ID:
+                               break;
+                       default:
+                               break;
                }
-               if (objectID == BASIC_CONSTRAINTS_CA)
+
+               if (id_type != ID_ANY)
                {
-                       isCA = object.len && *object.ptr;
-                       logger->log(logger, RAW|LEVEL1, "  %s", isCA ? "TRUE" : "FALSE");
-               }
+                       identification_t *gn = identification_create_from_encoding(id_type, object);
+                       logger->log(logger, CONTROL|LEVEL1, "  '%s'", gn->get_string(gn));
+                       return gn;
+        }
                objectID++;
-       }
-       return isCA;
+    }
+    return NULL;
 }
 
+
 /**
  * extracts one or several GNs and puts them into a chained list
  */
@@ -428,7 +526,10 @@ static void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_l
                
                if (objectID == GENERAL_NAMES_GN)
                {
-                       list->insert_last(list, identification_create_from_encoding(ID_DER_ASN1_GN, object));
+                       identification_t *gn = parse_generalName(object, level+1);
+
+                       if (gn != NULL)
+                               list->insert_last(list, gn);
                }
                objectID++;
        }
@@ -547,10 +648,8 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, chunk_t *accessL
                                                if (*object.ptr == ASN1_CONTEXT_S_6)
                                                {
                                                        if (asn1_length(&object) == ASN1_INVALID_LENGTH)
-                                                       {
                                                                return;
-                                                       }
-                                                       logger->log(logger, RAW|LEVEL1, "  '%.*s'",(int)object.len, object.ptr);
+                                                       logger->log(logger, CONTROL|LEVEL1, "  '%.*s'",(int)object.len, object.ptr);
                                                        /* only HTTP(S) URIs accepted */
                                                        if (strncasecmp(object.ptr, "http", 4) == 0)
                                                        {
@@ -659,7 +758,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_VERSION:
                                cert->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
-                               logger->log(logger, RAW|LEVEL1, "  v%d", cert->version);
+                               logger->log(logger, CONTROL|LEVEL1, "  v%d", cert->version);
                                break;
                        case X509_OBJ_SERIAL_NUMBER:
                                cert->serialNumber = object;
@@ -669,6 +768,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_ISSUER:
                                cert->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+                               logger->log(logger, CONTROL|LEVEL1, "  '%s'", cert->issuer->get_string(cert->issuer));
                                break;
                        case X509_OBJ_NOT_BEFORE:
                                cert->notBefore = parse_time(object, level);
@@ -678,6 +778,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_SUBJECT:
                                cert->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+                               logger->log(logger, CONTROL|LEVEL1, "  '%s'", cert->subject->get_string(cert->subject));
                                break;
                        case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM:
                                if (parse_algorithmIdentifier(object, level, NULL) != OID_RSA_ENCRYPTION)
@@ -737,9 +838,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                        case OID_NS_CA_POLICY_URL:
                                        case OID_NS_COMMENT:
                                                if (!parse_asn1_simple_object(&object, ASN1_IA5STRING , level, oid_names[extn_oid].name))
-                                               {
                                                        return FALSE;
-                                               }
                                                break;
                                        default:
                                                break;
@@ -790,11 +889,34 @@ err_t check_validity(const private_x509_t *cert, time_t *until)
 }
 
 /**
+ * Implements x509_t.equals_subjectAltName
+ */
+static bool equals_subjectAltName(private_x509_t *this, identification_t *id)
+{
+       bool found = FALSE;
+       iterator_t *iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE);
+
+       while (iterator->has_next(iterator))
+       {
+               identification_t *subjectAltName;
+
+               iterator->current(iterator, (void**)&subjectAltName);
+               if (id->equals(id, subjectAltName))
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       return found;
+}
+
+/**
  * Implements x509_t.get_public_key
  */
 static rsa_public_key_t *get_public_key(private_x509_t *this)
 {
-       return this->public_key->clone(this->public_key);;
+       return this->public_key->clone(this->public_key);
 }
 
 /**
@@ -824,32 +946,69 @@ static void destroy(private_x509_t *this)
                id->destroy(id);
        }
        this->subjectAltNames->destroy(this->subjectAltNames);
-       while (this->issuerAltNames->remove_last(this->issuerAltNames, (void**)&id) == SUCCESS)
-       {
-               id->destroy(id);
-       }
-       this->issuerAltNames->destroy(this->issuerAltNames);
+
        while (this->crlDistributionPoints->remove_last(this->crlDistributionPoints, (void**)&id) == SUCCESS)
        {
                id->destroy(id);
        }
        this->crlDistributionPoints->destroy(this->crlDistributionPoints);
+
        if (this->issuer)
-       {
                this->issuer->destroy(this->issuer);
-       }
+
        if (this->subject)
-       {
                this->subject->destroy(this->subject);
-       }
+
        if (this->public_key)
-       {
                this->public_key->destroy(this->public_key);
-       }
+
        free(this->certificate.ptr);
        free(this);
 }
 
+/** checks if the expiration date has been reached and
+ *  warns during the warning_interval of the imminent
+ *  expiry. strict=TRUE declares a fatal error,
+ *  strict=FALSE issues a warning upon expiry.
+ */
+char* check_expiry(time_t expiration_date, int warning_interval, bool strict)
+{
+       time_t now;
+       int time_left;
+
+       if (expiration_date == UNDEFINED_TIME)
+               return "ok (expires never)";
+
+       time_left = (expiration_date - time(NULL));
+       if (time_left < 0)
+               return strict? "fatal (expired)" : "warning (expired)";
+
+       {
+               static char buf[35]; /* temporary storage */
+               const char* unit = "second";
+
+               if (time_left > 86400*warning_interval)
+                       return "ok";
+
+               if (time_left > 172800)
+               {
+                       time_left /= 86400;
+                       unit = "day";
+               }
+               else if (time_left > 7200)
+               {
+                       time_left /= 3600;
+                       unit = "hour";
+               }
+               else if (time_left > 120)
+               {
+                       time_left /= 60;
+                       unit = "minute";
+               }
+               snprintf(buf, sizeof(buf), "warning (expires in %d %s%s)", time_left, unit, (time_left == 1)?"":"s");
+       }
+}
+
 /**
  * log certificate
  */
@@ -861,6 +1020,10 @@ static void log_certificate(private_x509_t *this, logger_t *logger, bool utc)
        rsa_public_key_t *rsa_key = this->public_key;
 
        char buf[BUF_LEN];
+    time_t now;
+
+    /* determine the current time */
+    time(&now);
 
        timetoa(buf, BUF_LEN, &this->installed, utc);
        logger->log(logger, CONTROL, "%s", buf);
@@ -869,9 +1032,11 @@ static void log_certificate(private_x509_t *this, logger_t *logger, bool utc)
        chunk_to_hex(buf, BUF_LEN, this->serialNumber);
        logger->log(logger, CONTROL, "       serial:   %s", buf);
        timetoa(buf, BUF_LEN, &this->notBefore, utc);
-       logger->log(logger, CONTROL, "       validity: not before %s", buf);
+       logger->log(logger, CONTROL, "       validity: not before %s %s", buf,
+                               (this->notBefore < now)? "ok":"fatal (not valid yet)");
        timetoa(buf, BUF_LEN, &this->notAfter, utc);
-       logger->log(logger, CONTROL, "                 not after  %s", buf);
+       logger->log(logger, CONTROL, "                 not after  %s %s", buf,
+                       check_expiry(this->notAfter, CERT_WARNING_INTERVAL, TRUE));
        logger->log(logger, CONTROL, "       pubkey:   RSA %d bits", BITS_PER_BYTE * rsa_key->get_keysize(rsa_key));
        if (this->subjectKeyID.ptr != NULL)
        {
@@ -899,6 +1064,7 @@ x509_t *x509_create_from_chunk(chunk_t chunk)
        
        /* public functions */
        this->public.equals = (bool (*) (x509_t*,x509_t*))equals;
+       this->public.equals_subjectAltName = (bool (*) (x509_t*,identification_t*))equals_subjectAltName;
        this->public.destroy = (void (*) (x509_t*))destroy;
        this->public.get_public_key = (rsa_public_key_t* (*) (x509_t*))get_public_key;
        this->public.get_subject = (identification_t* (*) (x509_t*))get_subject;
@@ -911,7 +1077,6 @@ x509_t *x509_create_from_chunk(chunk_t chunk)
        this->subject = NULL;
        this->issuer = NULL;
        this->subjectAltNames = linked_list_create();
-       this->issuerAltNames = linked_list_create();
        this->crlDistributionPoints = linked_list_create();
        this->subjectKeyID = CHUNK_INITIALIZER;
        this->authKeyID = CHUNK_INITIALIZER;
@@ -920,8 +1085,7 @@ x509_t *x509_create_from_chunk(chunk_t chunk)
        /* we do not use a per-instance logger right now, since its not always accessible */
        logger = logger_manager->get_logger(logger_manager, ASN1);
        
-       if (!is_asn1(chunk) ||
-               !parse_x509cert(chunk, 0, this))
+       if (!is_asn1(chunk) || !parse_x509cert(chunk, 0, this))
        {
                destroy(this);
                return NULL;
@@ -950,9 +1114,8 @@ x509_t *x509_create_from_file(const char *filename)
                return NULL;
 
        cert = x509_create_from_chunk(chunk);
+
        if (cert == NULL)
-       {
                free(chunk.ptr);
-       }
        return cert;
 }
index 9caf0ab..8e13dfc 100755 (executable)
@@ -107,6 +107,15 @@ struct x509_t {
        bool (*equals) (x509_t *this, x509_t *that);
        
        /**
+        * @brief Checks if the certificate contains a subjectAltName equal to id.
+        * 
+        * @param this                  certificate being examined
+        * @param id                    id which is being compared to the subjectAltNames
+        * @return                              TRUE if a match is found
+        */
+       bool (*equals_subjectAltName) (x509_t *this, identification_t *id);
+       
+       /**
         * @brief Destroys the certificate.
         * 
         * @param this                  certificate to destroy
index 8132d6e..369d481 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <ctype.h>
 
+#include "definitions.h"
 #include "identification.h"
 
 #include <asn1/asn1.h>
@@ -47,21 +48,17 @@ mapping_t id_type_m[] = {
        {MAPPING_END, NULL}
 };
 
-
 /**
  * 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 +103,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 +155,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;
 
@@ -235,7 +189,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,7 +206,7 @@ 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++));
        }
@@ -396,18 +349,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 */
@@ -447,18 +396,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 +413,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 */
@@ -524,25 +465,25 @@ bool match_dn(chunk_t a, chunk_t b, int *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 == '*')
                {
@@ -551,24 +492,19 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
                }
                /* 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 */
@@ -581,59 +517,6 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
 }
 
 /**
- * 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)
-       {
-               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++;
-       }
-       return NULL;
-}
-
-/**
  * Converts an LDAP-style human-readable ASCII-encoded
  * ASN.1 distinguished name into binary DER-encoded format
  */
@@ -648,13 +531,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 rdns[RDN_MAX];
+       int rdn_count = 0;
+       int dn_len = 0;
        int whitespace = 0;
-       int pos = 0;
+       int i;
        asn1_t rdn_type;
        state_t state = SEARCH_OID;
        status_t status = SUCCESS;
@@ -678,15 +561,15 @@ 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;
@@ -711,29 +594,26 @@ 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
                                        {
@@ -749,17 +629,19 @@ 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);
@@ -797,29 +679,16 @@ static char *get_string(private_identification_t *this)
  */
 static bool contains_wildcards(private_identification_t *this)
 {
-       if (this->type == ID_ANY ||
-               memchr(this->encoded.ptr, '*', this->encoded.len) != NULL)
-       {
-               return TRUE;
-       }
-       return FALSE;
+       return this->type == ID_ANY || memchr(this->encoded.ptr, '*', this->encoded.len) != NULL;
 }
 
 /**
  * Default implementation of identification_t.equals and identification_t.belongs_to.
  * 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)
-               {
-                       return TRUE;
-               }
-       }
-       return FALSE;
+       return this->type == other->type && chunk_equals(this->encoded, other->encoded);
 }
 
 /**
@@ -882,11 +751,7 @@ static bool belongs_to_wc_string(private_identification_t *this, private_identif
  */
 static bool belongs_to_any(private_identification_t *this, private_identification_t *other)
 {      
-       if (other->type == ID_ANY)
-       {
-               return TRUE;
-       }
-       return FALSE;
+       return other->type == ID_ANY;
 }
 
 /**
@@ -968,9 +833,8 @@ identification_t *identification_create_from_string(char *string)
        {
                /* 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;
@@ -983,11 +847,11 @@ identification_t *identification_create_from_string(char *string)
        }
        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;
@@ -997,8 +861,6 @@ identification_t *identification_create_from_string(char *string)
                }
                else
                {
-                       /* TODO: Pluto resolve domainnames without '@' to IPv4/6 address. Is this really needed? */
-                       
                        if (strchr(string, ':') == NULL)
                        {
                                /* try IPv4 */
@@ -1039,14 +901,15 @@ 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 ASN1/KEY ID. Do we need this, too?
+                   Yes, key IDs are needed */
                                free(this);
                                return NULL;
                        }
                        else
                        {
                                this->type = ID_FQDN;
-                               this->string = strdup(string + 1); /* discard @ */
+                               this->string = strdup(string);
                                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;
@@ -1070,10 +933,10 @@ 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;
+       char buf[BUF_LEN];
+       chunk_t buf_chunk = chunk_from_buf(buf);
+       private_identification_t *this = identification_create();
        
        this->type = type;
        switch (type)
@@ -1123,10 +986,14 @@ identification_t *identification_create_from_encoding(id_type_t type, chunk_t en
                        this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn;
                        break;
                case ID_DER_ASN1_GN:
-                       this->string = gntoa(encoded);
+                       this->string = strdup("ASN.1 coded generalName");
                        break;
                case ID_KEY_ID:
-                       this->string = strdup("(unparsed 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);
index e1e147f..ca1b534 100644 (file)
@@ -37,6 +37,11 @@ typedef enum id_type_t id_type_t;
 enum id_type_t {
        
        /**
+        * private type which matches any other id.
+        */
+       ID_ANY = 0,
+
+       /**
         * ID data is a single four (4) octet IPv4 address.
         */
        ID_IPV4_ADDR = 1,
@@ -78,11 +83,12 @@ enum id_type_t {
      * types of identification.
      */
        ID_KEY_ID = 11,
-       
+
        /**
-        * Special type of PRIVATE USE which matches to any other id.
+        * private type which represents a GeneralName of type URI
         */
-       ID_ANY = 201,
+       ID_DER_ASN1_GN_URI = 201,
+
 };
 
 /**
@@ -90,23 +96,6 @@ enum id_type_t {
  */
 extern mapping_t id_type_m[];
 
-/**
- * Different kinds of generalNames
- */
-typedef enum generalNames_t generalNames_t;
-
-enum generalNames_t {
-       GN_OTHER_NAME =         0,
-       GN_RFC822_NAME =        1,
-       GN_DNS_NAME =           2,
-       GN_X400_ADDRESS =       3,
-       GN_DIRECTORY_NAME =     4,
-       GN_EDI_PARTY_NAME = 5,
-       GN_URI =                        6,
-       GN_IP_ADDRESS =         7,
-       GN_REGISTERED_ID =      8,
-};
-
 typedef struct identification_t identification_t;
 
 /**
@@ -120,6 +109,7 @@ typedef struct identification_t identification_t;
  * - ID_DER_ASN1_DN
  * - ID_DER_ASN1_GN
  * - ID_KEY_ID
+ * - ID_DER_ASN1_GN_URI
  * 
  * @b Constructors:
  * - identification_create_from_string()