diff --git a/CHANGES b/CHANGES index 8196fd23f127be57b8bc68beb3817a1964f56896..7dd56c3cd7ebebe2354d77fdc0a77dae60a7641d 100644 --- 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 diff --git a/crypto/x509v3/v3_alt.c b/crypto/x509v3/v3_alt.c index baa9ca103d11d24f396b39fad52312574aa13bbd..64e51d6129ca294c7a8ac289a921602c3186c740 100644 --- a/crypto/x509v3/v3_alt.c +++ b/crypto/x509v3/v3_alt.c @@ -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","", &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:"); 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)) diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index a11243db8f663f4cbe3a892992f82a52cdf63f3b..4b85378e945fdcc6e4adeb9fd07325736fb45e6d 100644 --- a/crypto/x509v3/v3_utl.c +++ b/crypto/x509v3/v3_utl.c @@ -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; + } + diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index b4dd52a95121f10afe12498eca89b3fa43a1182a..a720ff2b9e52a61707b3a7d79f4c7075e667cd71 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -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