IPv6 display and input support for extensions usingh GeneralName.
authorDr. Stephen Henson <steve@openssl.org>
Wed, 5 Feb 2003 00:34:31 +0000 (00:34 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Wed, 5 Feb 2003 00:34:31 +0000 (00:34 +0000)
CHANGES
crypto/x509v3/v3_alt.c
crypto/x509v3/v3_utl.c
crypto/x509v3/x509v3.h

diff --git a/CHANGES b/CHANGES
index 8196fd23f127be57b8bc68beb3817a1964f56896..7dd56c3cd7ebebe2354d77fdc0a77dae60a7641d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,12 @@
 
  Changes between 0.9.7 and 0.9.8  [xx XXX xxxx]
 
+  *) IPv6 support for certificate extensions. The various extensions
+     which use the IP:a.b.c.d can now take IPv6 addresses using the
+     formats of RFC1884 2.2 . IPv6 addresses are now also displayed
+     correctly.
+     [Steve Henson]
+
   *) Added an ENGINE that implements RSA by performing private key
      exponentiations with the GMP library. The conversions to and from
      GMP's mpz_t format aren't optimised nor are any montgomery forms
index baa9ca103d11d24f396b39fad52312574aa13bbd..64e51d6129ca294c7a8ac289a921602c3186c740 100644 (file)
@@ -1,9 +1,9 @@
 /* v3_alt.c */
 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
- * project 1999.
+ * project.
  */
 /* ====================================================================
- * Copyright (c) 1999-2002 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -100,7 +100,8 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method,
                                GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret)
 {
        unsigned char *p;
-       char oline[256];
+       char oline[256], htmp[5];
+       int i;
        switch (gen->type)
        {
                case GEN_OTHERNAME:
@@ -134,12 +135,25 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method,
 
                case GEN_IPADD:
                p = gen->d.ip->data;
-               /* BUG: doesn't support IPV6 */
-               if(gen->d.ip->length != 4) {
+               if(gen->d.ip->length == 4)
+                       sprintf(oline, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+               else if(gen->d.ip->length == 16)
+                       {
+                       oline[0] = 0;
+                       for (i = 0; i < 8; i++)
+                               {
+                               sprintf(htmp, "%X", p[0] << 8 | p[1]);
+                               p += 2;
+                               strcat(oline, htmp);
+                               if (i != 7)
+                                       strcat(oline, ":");
+                               }
+                       }
+               else
+                       {
                        X509V3_add_value("IP Address","<invalid>", &ret);
                        break;
-               }
-               sprintf(oline, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+                       }
                X509V3_add_value("IP Address",oline, &ret);
                break;
 
@@ -154,6 +168,7 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method,
 int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen)
 {
        unsigned char *p;
+       int i;
        switch (gen->type)
        {
                case GEN_OTHERNAME:
@@ -188,12 +203,24 @@ int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen)
 
                case GEN_IPADD:
                p = gen->d.ip->data;
-               /* BUG: doesn't support IPV6 */
-               if(gen->d.ip->length != 4) {
+               if(gen->d.ip->length == 4)
+                       BIO_printf(out, "IP Address:%d.%d.%d.%d",
+                                               p[0], p[1], p[2], p[3]);
+               else if(gen->d.ip->length == 16)
+                       {
+                       BIO_printf(out, "IP Address");
+                       for (i = 0; i < 8; i++)
+                               {
+                               BIO_printf(out, ":%X", p[0] << 8 | p[1]);
+                               p += 2;
+                               }
+                       BIO_puts(out, "\n");
+                       }
+               else
+                       {
                        BIO_printf(out,"IP Address:<invalid>");
                        break;
-               }
-               BIO_printf(out, "IP Address:%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+                       }
                break;
 
                case GEN_RID:
@@ -418,21 +445,12 @@ if(!name_cmp(name, "email")) {
        gen->d.rid = obj;
        type = GEN_RID;
 } else if(!name_cmp(name, "IP")) {
-       int i1,i2,i3,i4;
-       unsigned char ip[4];
-       if((sscanf(value, "%d.%d.%d.%d",&i1,&i2,&i3,&i4) != 4) ||
-           (i1 < 0) || (i1 > 255) || (i2 < 0) || (i2 > 255) ||
-           (i3 < 0) || (i3 > 255) || (i4 < 0) || (i4 > 255) ) {
+       if(!(gen->d.ip = a2i_IPADDRESS(value)))
+               {
                X509V3err(X509V3_F_V2I_GENERAL_NAME,X509V3_R_BAD_IP_ADDRESS);
                ERR_add_error_data(2, "value=", value);
                goto err;
-       }
-       ip[0] = i1; ip[1] = i2 ; ip[2] = i3 ; ip[3] = i4;
-       if(!(gen->d.ip = M_ASN1_OCTET_STRING_new()) ||
-               !ASN1_STRING_set(gen->d.ip, ip, 4)) {
-                       X509V3err(X509V3_F_V2I_GENERAL_NAME,ERR_R_MALLOC_FAILURE);
-                       goto err;
-       }
+               }
        type = GEN_IPADD;
 } else if(!name_cmp(name, "otherName")) {
        if (!do_othername(gen, value, ctx))
index a11243db8f663f4cbe3a892992f82a52cdf63f3b..4b85378e945fdcc6e4adeb9fd07325736fb45e6d 100644 (file)
@@ -1,9 +1,9 @@
 /* v3_utl.c */
 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
- * project 1999.
+ * project.
  */
 /* ====================================================================
- * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -70,6 +70,11 @@ static STACK *get_email(X509_NAME *name, GENERAL_NAMES *gens);
 static void str_free(void *str);
 static int append_ia5(STACK **sk, ASN1_IA5STRING *email);
 
+static int ipv4_from_asc(unsigned char *v4, const char *in);
+static int ipv6_from_asc(unsigned char *v6, const char *in);
+static int ipv6_cb(const char *elem, int len, void *usr);
+static int ipv6_hex(unsigned char *out, const char *in, int inlen);
+
 /* Add a CONF_VALUE name value pair to stack */
 
 int X509V3_add_value(const char *name, const char *value,
@@ -534,3 +539,204 @@ void X509_email_free(STACK *sk)
 {
        sk_pop_free(sk, str_free);
 }
+
+/* Convert IP addresses both IPv4 and IPv6 into an 
+ * OCTET STRING compatible with RFC3280.
+ */
+
+ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc)
+       {
+       unsigned char ipout[16];
+       ASN1_OCTET_STRING *ret;
+       int iplen;
+
+       /* If string contains a ':' assume IPv6 */
+
+       if (strchr(ipasc, ':'))
+               {
+               if (!ipv6_from_asc(ipout, ipasc))
+                       return NULL;
+               iplen = 16;
+               }
+       else
+               {
+               if (!ipv4_from_asc(ipout, ipasc))
+                       return NULL;
+               iplen = 4;
+               }
+
+       ret = ASN1_OCTET_STRING_new();
+       if (!ret)
+               return NULL;
+       if (!ASN1_OCTET_STRING_set(ret, ipout, iplen))
+               {
+               ASN1_OCTET_STRING_free(ret);
+               return NULL;
+               }
+       return ret;
+       }
+
+static int ipv4_from_asc(unsigned char *v4, const char *in)
+       {
+       int a0, a1, a2, a3;
+       if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4)
+               return 0;
+       if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255)
+               || (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255))
+               return 0;
+       v4[0] = a0;
+       v4[1] = a1;
+       v4[2] = a2;
+       v4[3] = a3;
+       return 1;
+       }
+
+typedef struct {
+               /* Temporary store for IPV6 output */
+               unsigned char tmp[16];
+               /* Total number of bytes in tmp */
+               int total;
+               /* The position of a zero (corresponding to '::') */
+               int zero_pos;
+               /* Number of zeroes */
+               int zero_cnt;
+       } IPV6_STAT;
+
+
+static int ipv6_from_asc(unsigned char *v6, const char *in)
+       {
+       IPV6_STAT v6stat;
+       v6stat.total = 0;
+       v6stat.zero_pos = -1;
+       v6stat.zero_cnt = 0;
+       /* Treat the IPv6 representation as a list of values
+        * separated by ':'. The presence of a '::' will parse
+        * as one, two or three zero length elements.
+        */
+       if (!CONF_parse_list(in, ':', 0, ipv6_cb, &v6stat))
+               return 0;
+
+       /* Now for some sanity checks */
+
+       if (v6stat.zero_pos == -1)
+               {
+               /* If no '::' must have exactly 16 bytes */
+               if (v6stat.total != 16)
+                       return 0;
+               }
+       else 
+               {
+               /* If '::' must have less than 16 bytes */
+               if (v6stat.total == 16)
+                       return 0;
+               /* More than three zeroes is an error */
+               if (v6stat.zero_cnt > 3)
+                       return 0;
+               /* Can only have three zeroes if nothing else present */
+               else if (v6stat.zero_cnt == 3)
+                       {
+                       if (v6stat.total > 0)
+                               return 0;
+                       }
+               /* Can only have two zeroes if at start or end */
+               else if (v6stat.zero_cnt == 2)
+                       {
+                       if ((v6stat.zero_pos != 0)
+                               && (v6stat.zero_pos != v6stat.total))
+                               return 0;
+                       }
+               else 
+               /* Can only have one zero if *not* start or end */
+                       {
+                       if ((v6stat.zero_pos == 0)
+                               || (v6stat.zero_pos == v6stat.total))
+                               return 0;
+                       }
+               }
+
+       /* Format result */
+
+       /* Copy initial part */
+       if (v6stat.zero_pos > 0)
+               memcpy(v6, v6stat.tmp, v6stat.zero_pos);
+       /* Zero middle */
+       if (v6stat.total != 16)
+               memset(v6 + v6stat.zero_pos, 0, 16 - v6stat.total);
+       /* Copy final part */
+       if (v6stat.total != v6stat.zero_pos)
+               memcpy(v6 + v6stat.zero_pos + 16 - v6stat.total,
+                       v6stat.tmp + v6stat.zero_pos,
+                       v6stat.total - v6stat.zero_pos);
+
+       return 1;
+       }
+
+static int ipv6_cb(const char *elem, int len, void *usr)
+       {
+       IPV6_STAT *s = usr;
+       /* Error if 16 bytes written */
+       if (s->total == 16)
+               return 0;
+       if (len == 0)
+               {
+               /* Zero length element, corresponds to '::' */
+               if (s->zero_pos == -1)
+                       s->zero_pos = s->total;
+               /* If we've already got a :: its an error */
+               else if (s->zero_pos != s->total)
+                       return 0;
+               s->zero_cnt++;
+               }
+       else 
+               {
+               /* If more than 4 characters could be final a.b.c.d form */
+               if (len > 4)
+                       {
+                       /* Need at least 4 bytes left */
+                       if (s->total > 12)
+                               return 0;
+                       /* Must be end of string */
+                       if (elem[len])
+                               return 0;
+                       if (!ipv4_from_asc(s->tmp + s->total, elem))
+                               return 0;
+                       s->total += 4;
+                       }
+               else
+                       {
+                       if (!ipv6_hex(s->tmp + s->total, elem, len))
+                               return 0;
+                       s->total += 2;
+                       }
+               }
+       return 1;
+       }
+
+/* Convert a string of up to 4 hex digits into the corresponding
+ * IPv6 form.
+ */
+
+static int ipv6_hex(unsigned char *out, const char *in, int inlen)
+       {
+       unsigned char c;
+       unsigned int num = 0;
+       if (inlen > 4)
+               return 0;
+       while(inlen--)
+               {
+               c = *in++;
+               num <<= 4;
+               if ((c >= '0') && (c <= '9'))
+                       num |= c - '0';
+               else if ((c >= 'A') && (c <= 'F'))
+                       num |= c - 'A' + 10;
+               else if ((c >= 'a') && (c <= 'f'))
+                       num |=  c - 'a' + 10;
+               else
+                       return 0;
+               }
+       out[0] = num >> 8;
+       out[1] = num & 0xff;
+       return 1;
+       }
+
index b4dd52a95121f10afe12498eca89b3fa43a1182a..a720ff2b9e52a61707b3a7d79f4c7075e667cd71 100644 (file)
@@ -547,6 +547,7 @@ STACK *X509_get1_email(X509 *x);
 STACK *X509_REQ_get1_email(X509_REQ *x);
 void X509_email_free(STACK *sk);
 
+ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
 
 /* BEGIN ERROR CODES */
 /* The following lines are auto generated by the script mkerr.pl. Any changes