+
+static int sk_strcmp(const char * const *a, const char * const *b)
+{
+ return strcmp(*a, *b);
+}
+
+STACK *X509_get1_email(X509 *x)
+{
+ GENERAL_NAMES *gens;
+ STACK *ret;
+ gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+ ret = get_email(X509_get_subject_name(x), gens);
+ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+ return ret;
+}
+
+STACK *X509_REQ_get1_email(X509_REQ *x)
+{
+ GENERAL_NAMES *gens;
+ STACK_OF(X509_EXTENSION) *exts;
+ STACK *ret;
+ exts = X509_REQ_get_extensions(x);
+ gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL);
+ ret = get_email(X509_REQ_get_subject_name(x), gens);
+ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ return ret;
+}
+
+
+static STACK *get_email(X509_NAME *name, GENERAL_NAMES *gens)
+{
+ STACK *ret = NULL;
+ X509_NAME_ENTRY *ne;
+ ASN1_IA5STRING *email;
+ GENERAL_NAME *gen;
+ int i;
+ /* Now add any email address(es) to STACK */
+ i = -1;
+ /* First supplied X509_NAME */
+ while((i = X509_NAME_get_index_by_NID(name,
+ NID_pkcs9_emailAddress, i)) >= 0) {
+ ne = X509_NAME_get_entry(name, i);
+ email = X509_NAME_ENTRY_get_data(ne);
+ if(!append_ia5(&ret, email)) return NULL;
+ }
+ for(i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+ {
+ gen = sk_GENERAL_NAME_value(gens, i);
+ if(gen->type != GEN_EMAIL) continue;
+ if(!append_ia5(&ret, gen->d.ia5)) return NULL;
+ }
+ return ret;
+}
+
+static void str_free(void *str)
+{
+ OPENSSL_free(str);
+}
+
+static int append_ia5(STACK **sk, ASN1_IA5STRING *email)
+{
+ char *emtmp;
+ /* First some sanity checks */
+ if(email->type != V_ASN1_IA5STRING) return 1;
+ if(!email->data || !email->length) return 1;
+ if(!*sk) *sk = sk_new(sk_strcmp);
+ if(!*sk) return 0;
+ /* Don't add duplicates */
+ if(sk_find(*sk, (char *)email->data) != -1) return 1;
+ emtmp = BUF_strdup((char *)email->data);
+ if(!emtmp || !sk_push(*sk, emtmp)) {
+ X509_email_free(*sk);
+ *sk = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+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;
+ }
+
+
+int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE)*dn_sk,
+ unsigned long chtype)
+ {
+ CONF_VALUE *v;
+ int i;
+ char *p, *type;
+ if (!nm)
+ return 0;
+
+ for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++)
+ {
+ v=sk_CONF_VALUE_value(dn_sk,i);
+ type=v->name;
+ /* Skip past any leading X. X: X, etc to allow for
+ * multiple instances
+ */
+ for(p = type; *p ; p++)
+#ifndef CHARSET_EBCDIC
+ if ((*p == ':') || (*p == ',') || (*p == '.'))
+#else
+ if ((*p == os_toascii[':']) || (*p == os_toascii[',']) || (*p == os_toascii['.']))
+#endif
+ {
+ p++;
+ if(*p) type = p;
+ break;
+ }
+ if (!X509_NAME_add_entry_by_txt(nm,type, chtype,
+ (unsigned char *) v->value,-1,-1,0))
+ return 0;
+
+ }
+ return 1;
+ }