1/*
2 * Copyright (c) 2004 - 2009 Kungliga Tekniska H��gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "hx_locl.h"
35#include <wind.h>
36#include "char_map.h"
37
38/**
39 * @page page_name PKIX/X.509 Names
40 *
41 * There are several names in PKIX/X.509, GeneralName and Name.
42 *
43 * A Name consists of an ordered list of Relative Distinguished Names
44 * (RDN). Each RDN consists of an unordered list of typed strings. The
45 * types are defined by OID and have long and short description. For
46 * example id-at-commonName (2.5.4.3) have the long name CommonName
47 * and short name CN. The string itself can be of several encoding,
48 * UTF8, UTF16, Teltex string, etc. The type limit what encoding
49 * should be used.
50 *
51 * GeneralName is a broader nametype that can contains al kind of
52 * stuff like Name, IP addresses, partial Name, etc.
53 *
54 * Name is mapped into a hx509_name object.
55 *
56 * Parse and string name into a hx509_name object with hx509_parse_name(),
57 * make it back into string representation with hx509_name_to_string().
58 *
59 * Name string are defined rfc2253, rfc1779 and X.501.
60 *
61 * See the library functions here: @ref hx509_name
62 */
63
64static const struct {
65    const char *n;
66    const heim_oid *o;
67    wind_profile_flags flags;
68} no[] = {
69    { "C", &asn1_oid_id_at_countryName, 0 },
70    { "CN", &asn1_oid_id_at_commonName, 0 },
71    { "DC", &asn1_oid_id_domainComponent, 0 },
72    { "L", &asn1_oid_id_at_localityName, 0 },
73    { "O", &asn1_oid_id_at_organizationName, 0 },
74    { "OU", &asn1_oid_id_at_organizationalUnitName, 0 },
75    { "S", &asn1_oid_id_at_stateOrProvinceName, 0 },
76    { "STREET", &asn1_oid_id_at_streetAddress, 0 },
77    { "UID", &asn1_oid_id_Userid, 0 },
78    { "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0 },
79    { "serialNumber", &asn1_oid_id_at_serialNumber, 0 }
80};
81
82static char *
83quote_string(const char *f, size_t len, int flags, size_t *rlen)
84{
85    size_t i, j, tolen;
86    const unsigned char *from = (const unsigned char *)f;
87    unsigned char *to;
88
89    tolen = len * 3 + 1;
90    to = malloc(tolen);
91    if (to == NULL)
92	return NULL;
93
94    for (i = 0, j = 0; i < len; i++) {
95	unsigned char map = char_map[from[i]] & flags;
96	if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) {
97	    to[j++] = '\\';
98	    to[j++] = from[i];
99	} else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) {
100
101	    to[j++] = '\\';
102	    to[j++] = from[i];
103	} else if (map & Q_RFC2253_QUOTE) {
104	    to[j++] = '\\';
105	    to[j++] = from[i];
106	} else if (map & Q_RFC2253_HEX) {
107	    int l = snprintf((char *)&to[j], tolen - j - 1,
108			     "#%02x", (unsigned char)from[i]);
109	    j += l;
110	} else {
111	    to[j++] = from[i];
112	}
113    }
114    to[j] = '\0';
115    assert(j < tolen);
116    *rlen = j;
117    return (char *)to;
118}
119
120
121static int
122append_string(char **str, size_t *total_len, const char *ss,
123	      size_t len, int quote)
124{
125    char *s, *qs;
126
127    if (quote)
128	qs = quote_string(ss, len, Q_RFC2253, &len);
129    else
130	qs = rk_UNCONST(ss);
131
132    s = realloc(*str, len + *total_len + 1);
133    if (s == NULL)
134	_hx509_abort("allocation failure"); /* XXX */
135    memcpy(s + *total_len, qs, len);
136    if (qs != ss)
137	free(qs);
138    s[*total_len + len] = '\0';
139    *str = s;
140    *total_len += len;
141    return 0;
142}
143
144static char *
145oidtostring(const heim_oid *type)
146{
147    char *s;
148    size_t i;
149
150    for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
151	if (der_heim_oid_cmp(no[i].o, type) == 0)
152	    return strdup(no[i].n);
153    }
154    if (der_print_heim_oid(type, '.', &s) != 0)
155	return NULL;
156    return s;
157}
158
159static int
160stringtooid(const char *name, size_t len, heim_oid *oid)
161{
162    int ret;
163    size_t i;
164    char *s;
165
166    memset(oid, 0, sizeof(*oid));
167
168    for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
169	if (strncasecmp(no[i].n, name, len) == 0)
170	    return der_copy_oid(no[i].o, oid);
171    }
172    s = malloc(len + 1);
173    if (s == NULL)
174	return ENOMEM;
175    memcpy(s, name, len);
176    s[len] = '\0';
177    ret = der_parse_heim_oid(s, ".", oid);
178    free(s);
179    return ret;
180}
181
182/**
183 * Convert the hx509 name object into a printable string.
184 * The resulting string should be freed with free().
185 *
186 * @param name name to print
187 * @param str the string to return
188 *
189 * @return An hx509 error code, see hx509_get_error_string().
190 *
191 * @ingroup hx509_name
192 */
193
194int
195hx509_name_to_string(const hx509_name name, char **str)
196{
197    return _hx509_Name_to_string(&name->der_name, str);
198}
199
200int
201_hx509_Name_to_string(const Name *n, char **str)
202{
203    size_t total_len = 0;
204    size_t i, j, m;
205    int ret;
206
207    *str = strdup("");
208    if (*str == NULL)
209	return ENOMEM;
210
211    for (m = n->u.rdnSequence.len; m > 0; m--) {
212	size_t len;
213	i = m - 1;
214
215	for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
216	    DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
217	    char *oidname;
218	    char *ss;
219
220	    oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
221
222	    switch(ds->element) {
223	    case choice_DirectoryString_ia5String:
224		ss = ds->u.ia5String.data;
225		len = ds->u.ia5String.length;
226		break;
227	    case choice_DirectoryString_printableString:
228		ss = ds->u.printableString.data;
229		len = ds->u.printableString.length;
230		break;
231	    case choice_DirectoryString_utf8String:
232		ss = ds->u.utf8String;
233		len = strlen(ss);
234		break;
235	    case choice_DirectoryString_bmpString: {
236	        const uint16_t *bmp = ds->u.bmpString.data;
237		size_t bmplen = ds->u.bmpString.length;
238		size_t k;
239
240		ret = wind_ucs2utf8_length(bmp, bmplen, &k);
241		if (ret)
242		    return ret;
243
244		ss = malloc(k + 1);
245		if (ss == NULL)
246		    _hx509_abort("allocation failure"); /* XXX */
247		ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
248		if (ret) {
249		    free(ss);
250		    return ret;
251		}
252		ss[k] = '\0';
253		len = k;
254		break;
255	    }
256	    case choice_DirectoryString_teletexString:
257		ss = ds->u.teletexString;
258		len = strlen(ss);
259		break;
260	    case choice_DirectoryString_universalString: {
261	        const uint32_t *uni = ds->u.universalString.data;
262		size_t unilen = ds->u.universalString.length;
263		size_t k;
264
265		ret = wind_ucs4utf8_length(uni, unilen, &k);
266		if (ret)
267		    return ret;
268
269		ss = malloc(k + 1);
270		if (ss == NULL)
271		    _hx509_abort("allocation failure"); /* XXX */
272		ret = wind_ucs4utf8(uni, unilen, ss, NULL);
273		if (ret) {
274		    free(ss);
275		    return ret;
276		}
277		ss[k] = '\0';
278		len = k;
279		break;
280	    }
281	    default:
282		_hx509_abort("unknown directory type: %d", ds->element);
283		exit(1);
284	    }
285	    append_string(str, &total_len, oidname, strlen(oidname), 0);
286	    free(oidname);
287	    append_string(str, &total_len, "=", 1, 0);
288	    append_string(str, &total_len, ss, len, 1);
289	    if (ds->element == choice_DirectoryString_bmpString ||
290		ds->element == choice_DirectoryString_universalString)
291	    {
292		free(ss);
293	    }
294	    if (j + 1 < n->u.rdnSequence.val[i].len)
295		append_string(str, &total_len, "+", 1, 0);
296	}
297
298	if (i > 0)
299	    append_string(str, &total_len, ",", 1, 0);
300    }
301    return 0;
302}
303
304#define COPYCHARARRAY(_ds,_el,_l,_n)		\
305        (_l) = strlen(_ds->u._el);		\
306	(_n) = malloc((_l) * sizeof((_n)[0]));	\
307	if ((_n) == NULL)			\
308	    return ENOMEM;			\
309	for (i = 0; i < (_l); i++)		\
310	    (_n)[i] = _ds->u._el[i]
311
312
313#define COPYVALARRAY(_ds,_el,_l,_n)		\
314        (_l) = _ds->u._el.length;		\
315	(_n) = malloc((_l) * sizeof((_n)[0]));	\
316	if ((_n) == NULL)			\
317	    return ENOMEM;			\
318	for (i = 0; i < (_l); i++)		\
319	    (_n)[i] = _ds->u._el.data[i]
320
321#define COPYVOIDARRAY(_ds,_el,_l,_n)		\
322        (_l) = _ds->u._el.length;		\
323	(_n) = malloc((_l) * sizeof((_n)[0]));	\
324	if ((_n) == NULL)			\
325	    return ENOMEM;			\
326	for (i = 0; i < (_l); i++)		\
327	    (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
328
329
330
331static int
332dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
333{
334    wind_profile_flags flags;
335    size_t i, len;
336    int ret;
337    uint32_t *name;
338
339    *rname = NULL;
340    *rlen = 0;
341
342    switch(ds->element) {
343    case choice_DirectoryString_ia5String:
344	flags = WIND_PROFILE_LDAP;
345	COPYVOIDARRAY(ds, ia5String, len, name);
346	break;
347    case choice_DirectoryString_printableString:
348	flags = WIND_PROFILE_LDAP;
349	flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
350	COPYVOIDARRAY(ds, printableString, len, name);
351	break;
352    case choice_DirectoryString_teletexString:
353	flags = WIND_PROFILE_LDAP_CASE;
354	COPYCHARARRAY(ds, teletexString, len, name);
355	break;
356    case choice_DirectoryString_bmpString:
357	flags = WIND_PROFILE_LDAP;
358	COPYVALARRAY(ds, bmpString, len, name);
359	break;
360    case choice_DirectoryString_universalString:
361	flags = WIND_PROFILE_LDAP;
362	COPYVALARRAY(ds, universalString, len, name);
363	break;
364    case choice_DirectoryString_utf8String:
365	flags = WIND_PROFILE_LDAP;
366	ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
367	if (ret)
368	    return ret;
369	name = malloc(len * sizeof(name[0]));
370	if (name == NULL)
371	    return ENOMEM;
372	ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
373	if (ret) {
374	    free(name);
375	    return ret;
376	}
377	break;
378    default:
379	_hx509_abort("unknown directory type: %d", ds->element);
380    }
381
382    *rlen = len;
383    /* try a couple of times to get the length right, XXX gross */
384    for (i = 0; i < 4; i++) {
385	*rlen = *rlen * 2;
386	*rname = malloc(*rlen * sizeof((*rname)[0]));
387
388	ret = wind_stringprep(name, len, *rname, rlen, flags);
389	if (ret == WIND_ERR_OVERRUN) {
390	    free(*rname);
391	    *rname = NULL;
392	    continue;
393	} else
394	    break;
395    }
396    free(name);
397    if (ret) {
398	if (*rname)
399	    free(*rname);
400	*rname = NULL;
401	*rlen = 0;
402	return ret;
403    }
404
405    return 0;
406}
407
408int
409_hx509_name_ds_cmp(const DirectoryString *ds1,
410		   const DirectoryString *ds2,
411		   int *diff)
412{
413    uint32_t *ds1lp, *ds2lp;
414    size_t ds1len, ds2len, i;
415    int ret;
416
417    ret = dsstringprep(ds1, &ds1lp, &ds1len);
418    if (ret)
419	return ret;
420    ret = dsstringprep(ds2, &ds2lp, &ds2len);
421    if (ret) {
422	free(ds1lp);
423	return ret;
424    }
425
426    if (ds1len != ds2len)
427	*diff = ds1len - ds2len;
428    else {
429	for (i = 0; i < ds1len; i++) {
430	    *diff = ds1lp[i] - ds2lp[i];
431	    if (*diff)
432		break;
433	}
434    }
435    free(ds1lp);
436    free(ds2lp);
437
438    return 0;
439}
440
441int
442_hx509_name_cmp(const Name *n1, const Name *n2, int *c)
443{
444    int ret;
445    size_t i, j;
446
447    *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
448    if (*c)
449	return 0;
450
451    for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
452	*c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
453	if (*c)
454	    return 0;
455
456	for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
457	    *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
458				  &n1->u.rdnSequence.val[i].val[j].type);
459	    if (*c)
460		return 0;
461
462	    ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
463				     &n2->u.rdnSequence.val[i].val[j].value,
464				     c);
465	    if (ret)
466		return ret;
467	    if (*c)
468		return 0;
469	}
470    }
471    *c = 0;
472    return 0;
473}
474
475/**
476 * Compare to hx509 name object, useful for sorting.
477 *
478 * @param n1 a hx509 name object.
479 * @param n2 a hx509 name object.
480 *
481 * @return 0 the objects are the same, returns > 0 is n2 is "larger"
482 * then n2, < 0 if n1 is "smaller" then n2.
483 *
484 * @ingroup hx509_name
485 */
486
487int
488hx509_name_cmp(hx509_name n1, hx509_name n2)
489{
490    int ret, diff;
491    ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
492    if (ret)
493	return ret;
494    return diff;
495}
496
497
498int
499_hx509_name_from_Name(const Name *n, hx509_name *name)
500{
501    int ret;
502    *name = calloc(1, sizeof(**name));
503    if (*name == NULL)
504	return ENOMEM;
505    ret = copy_Name(n, &(*name)->der_name);
506    if (ret) {
507	free(*name);
508	*name = NULL;
509    }
510    return ret;
511}
512
513int
514_hx509_name_modify(hx509_context context,
515		   Name *name,
516		   int append,
517		   const heim_oid *oid,
518		   const char *str)
519{
520    RelativeDistinguishedName *rdn;
521    int ret;
522    void *ptr;
523
524    ptr = realloc(name->u.rdnSequence.val,
525		  sizeof(name->u.rdnSequence.val[0]) *
526		  (name->u.rdnSequence.len + 1));
527    if (ptr == NULL) {
528	hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
529	return ENOMEM;
530    }
531    name->u.rdnSequence.val = ptr;
532
533    if (append) {
534	rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
535    } else {
536	memmove(&name->u.rdnSequence.val[1],
537		&name->u.rdnSequence.val[0],
538		name->u.rdnSequence.len *
539		sizeof(name->u.rdnSequence.val[0]));
540
541	rdn = &name->u.rdnSequence.val[0];
542    }
543    rdn->val = malloc(sizeof(rdn->val[0]));
544    if (rdn->val == NULL)
545	return ENOMEM;
546    rdn->len = 1;
547    ret = der_copy_oid(oid, &rdn->val[0].type);
548    if (ret)
549	return ret;
550    rdn->val[0].value.element = choice_DirectoryString_utf8String;
551    rdn->val[0].value.u.utf8String = strdup(str);
552    if (rdn->val[0].value.u.utf8String == NULL)
553	return ENOMEM;
554    name->u.rdnSequence.len += 1;
555
556    return 0;
557}
558
559/**
560 * Parse a string into a hx509 name object.
561 *
562 * @param context A hx509 context.
563 * @param str a string to parse.
564 * @param name the resulting object, NULL in case of error.
565 *
566 * @return An hx509 error code, see hx509_get_error_string().
567 *
568 * @ingroup hx509_name
569 */
570
571int
572hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
573{
574    const char *p, *q;
575    size_t len;
576    hx509_name n;
577    int ret;
578
579    *name = NULL;
580
581    n = calloc(1, sizeof(*n));
582    if (n == NULL) {
583	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
584	return ENOMEM;
585    }
586
587    n->der_name.element = choice_Name_rdnSequence;
588
589    p = str;
590
591    while (p != NULL && *p != '\0') {
592	heim_oid oid;
593	int last;
594
595	q = strchr(p, ',');
596	if (q) {
597	    len = (q - p);
598	    last = 1;
599	} else {
600	    len = strlen(p);
601	    last = 0;
602	}
603
604	q = strchr(p, '=');
605	if (q == NULL) {
606	    ret = HX509_PARSING_NAME_FAILED;
607	    hx509_set_error_string(context, 0, ret, "missing = in %s", p);
608	    goto out;
609	}
610	if (q == p) {
611	    ret = HX509_PARSING_NAME_FAILED;
612	    hx509_set_error_string(context, 0, ret,
613				   "missing name before = in %s", p);
614	    goto out;
615	}
616
617	if ((size_t)(q - p) > len) {
618	    ret = HX509_PARSING_NAME_FAILED;
619	    hx509_set_error_string(context, 0, ret, " = after , in %s", p);
620	    goto out;
621	}
622
623	ret = stringtooid(p, q - p, &oid);
624	if (ret) {
625	    ret = HX509_PARSING_NAME_FAILED;
626	    hx509_set_error_string(context, 0, ret,
627				   "unknown type: %.*s", (int)(q - p), p);
628	    goto out;
629	}
630
631	{
632	    size_t pstr_len = len - (q - p) - 1;
633	    const char *pstr = p + (q - p) + 1;
634	    char *r;
635
636	    r = malloc(pstr_len + 1);
637	    if (r == NULL) {
638		der_free_oid(&oid);
639		ret = ENOMEM;
640		hx509_set_error_string(context, 0, ret, "out of memory");
641		goto out;
642	    }
643	    memcpy(r, pstr, pstr_len);
644	    r[pstr_len] = '\0';
645
646	    ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
647	    free(r);
648	    der_free_oid(&oid);
649	    if(ret)
650		goto out;
651	}
652	p += len + last;
653    }
654
655    *name = n;
656
657    return 0;
658out:
659    hx509_name_free(&n);
660    return HX509_NAME_MALFORMED;
661}
662
663/**
664 * Copy a hx509 name object.
665 *
666 * @param context A hx509 cotext.
667 * @param from the name to copy from
668 * @param to the name to copy to
669 *
670 * @return An hx509 error code, see hx509_get_error_string().
671 *
672 * @ingroup hx509_name
673 */
674
675int
676hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
677{
678    int ret;
679
680    *to = calloc(1, sizeof(**to));
681    if (*to == NULL)
682	return ENOMEM;
683    ret = copy_Name(&from->der_name, &(*to)->der_name);
684    if (ret) {
685	free(*to);
686	*to = NULL;
687	return ENOMEM;
688    }
689    return 0;
690}
691
692/**
693 * Convert a hx509_name into a Name.
694 *
695 * @param from the name to copy from
696 * @param to the name to copy to
697 *
698 * @return An hx509 error code, see hx509_get_error_string().
699 *
700 * @ingroup hx509_name
701 */
702
703int
704hx509_name_to_Name(const hx509_name from, Name *to)
705{
706    return copy_Name(&from->der_name, to);
707}
708
709int
710hx509_name_normalize(hx509_context context, hx509_name name)
711{
712    return 0;
713}
714
715/**
716 * Expands variables in the name using env. Variables are on the form
717 * ${name}. Useful when dealing with certificate templates.
718 *
719 * @param context A hx509 cotext.
720 * @param name the name to expand.
721 * @param env environment variable to expand.
722 *
723 * @return An hx509 error code, see hx509_get_error_string().
724 *
725 * @ingroup hx509_name
726 */
727
728int
729hx509_name_expand(hx509_context context,
730		  hx509_name name,
731		  hx509_env env)
732{
733    Name *n = &name->der_name;
734    size_t i, j;
735
736    if (env == NULL)
737	return 0;
738
739    if (n->element != choice_Name_rdnSequence) {
740	hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
741	return EINVAL;
742    }
743
744    for (i = 0 ; i < n->u.rdnSequence.len; i++) {
745	for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
746	    /** Only UTF8String rdnSequence names are allowed */
747	    /*
748	      THIS SHOULD REALLY BE:
749	      COMP = n->u.rdnSequence.val[i].val[j];
750	      normalize COMP to utf8
751	      check if there are variables
752	        expand variables
753	        convert back to orignal format, store in COMP
754	      free normalized utf8 string
755	    */
756	    DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
757	    char *p, *p2;
758	    struct rk_strpool *strpool = NULL;
759
760	    if (ds->element != choice_DirectoryString_utf8String) {
761		hx509_set_error_string(context, 0, EINVAL, "unsupported type");
762		return EINVAL;
763	    }
764	    p = strstr(ds->u.utf8String, "${");
765	    if (p) {
766		strpool = rk_strpoolprintf(strpool, "%.*s",
767					   (int)(p - ds->u.utf8String),
768					   ds->u.utf8String);
769		if (strpool == NULL) {
770		    hx509_set_error_string(context, 0, ENOMEM, "out of memory");
771		    return ENOMEM;
772		}
773	    }
774	    while (p != NULL) {
775		/* expand variables */
776		const char *value;
777		p2 = strchr(p, '}');
778		if (p2 == NULL) {
779		    hx509_set_error_string(context, 0, EINVAL, "missing }");
780		    rk_strpoolfree(strpool);
781		    return EINVAL;
782		}
783		p += 2;
784		value = hx509_env_lfind(context, env, p, p2 - p);
785		if (value == NULL) {
786		    hx509_set_error_string(context, 0, EINVAL,
787					   "variable %.*s missing",
788					   (int)(p2 - p), p);
789		    rk_strpoolfree(strpool);
790		    return EINVAL;
791		}
792		strpool = rk_strpoolprintf(strpool, "%s", value);
793		if (strpool == NULL) {
794		    hx509_set_error_string(context, 0, ENOMEM, "out of memory");
795		    return ENOMEM;
796		}
797		p2++;
798
799		p = strstr(p2, "${");
800		if (p)
801		    strpool = rk_strpoolprintf(strpool, "%.*s",
802					       (int)(p - p2), p2);
803		else
804		    strpool = rk_strpoolprintf(strpool, "%s", p2);
805		if (strpool == NULL) {
806		    hx509_set_error_string(context, 0, ENOMEM, "out of memory");
807		    return ENOMEM;
808		}
809	    }
810	    if (strpool) {
811		free(ds->u.utf8String);
812		ds->u.utf8String = rk_strpoolcollect(strpool);
813		if (ds->u.utf8String == NULL) {
814		    hx509_set_error_string(context, 0, ENOMEM, "out of memory");
815		    return ENOMEM;
816		}
817	    }
818	}
819    }
820    return 0;
821}
822
823/**
824 * Free a hx509 name object, upond return *name will be NULL.
825 *
826 * @param name a hx509 name object to be freed.
827 *
828 * @ingroup hx509_name
829 */
830
831void
832hx509_name_free(hx509_name *name)
833{
834    free_Name(&(*name)->der_name);
835    memset(*name, 0, sizeof(**name));
836    free(*name);
837    *name = NULL;
838}
839
840/**
841 * Convert a DER encoded name info a string.
842 *
843 * @param data data to a DER/BER encoded name
844 * @param length length of data
845 * @param str the resulting string, is NULL on failure.
846 *
847 * @return An hx509 error code, see hx509_get_error_string().
848 *
849 * @ingroup hx509_name
850 */
851
852int
853hx509_unparse_der_name(const void *data, size_t length, char **str)
854{
855    Name name;
856    int ret;
857
858    *str = NULL;
859
860    ret = decode_Name(data, length, &name, NULL);
861    if (ret)
862	return ret;
863    ret = _hx509_Name_to_string(&name, str);
864    free_Name(&name);
865    return ret;
866}
867
868/**
869 * Convert a hx509_name object to DER encoded name.
870 *
871 * @param name name to concert
872 * @param os data to a DER encoded name, free the resulting octet
873 * string with hx509_xfree(os->data).
874 *
875 * @return An hx509 error code, see hx509_get_error_string().
876 *
877 * @ingroup hx509_name
878 */
879
880int
881hx509_name_binary(const hx509_name name, heim_octet_string *os)
882{
883    size_t size;
884    int ret;
885
886    ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
887    if (ret)
888	return ret;
889    if (os->length != size)
890	_hx509_abort("internal ASN.1 encoder error");
891
892    return 0;
893}
894
895int
896_hx509_unparse_Name(const Name *aname, char **str)
897{
898    hx509_name name;
899    int ret;
900
901    ret = _hx509_name_from_Name(aname, &name);
902    if (ret)
903	return ret;
904
905    ret = hx509_name_to_string(name, str);
906    hx509_name_free(&name);
907    return ret;
908}
909
910/**
911 * Unparse the hx509 name in name into a string.
912 *
913 * @param name the name to check if its empty/null.
914 *
915 * @return non zero if the name is empty/null.
916 *
917 * @ingroup hx509_name
918 */
919
920int
921hx509_name_is_null_p(const hx509_name name)
922{
923    return name->der_name.u.rdnSequence.len == 0;
924}
925
926/**
927 * Unparse the hx509 name in name into a string.
928 *
929 * @param name the name to print
930 * @param str an allocated string returns the name in string form
931 *
932 * @return An hx509 error code, see hx509_get_error_string().
933 *
934 * @ingroup hx509_name
935 */
936
937int
938hx509_general_name_unparse(GeneralName *name, char **str)
939{
940    struct rk_strpool *strpool = NULL;
941
942    *str = NULL;
943
944    switch (name->element) {
945    case choice_GeneralName_otherName: {
946	char *oid;
947	hx509_oid_sprint(&name->u.otherName.type_id, &oid);
948	if (oid == NULL)
949	    return ENOMEM;
950	strpool = rk_strpoolprintf(strpool, "otherName: %s", oid);
951	free(oid);
952	break;
953    }
954    case choice_GeneralName_rfc822Name:
955	strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n",
956				   (int)name->u.rfc822Name.length,
957				   (char *)name->u.rfc822Name.data);
958	break;
959    case choice_GeneralName_dNSName:
960	strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n",
961				   (int)name->u.dNSName.length,
962				   (char *)name->u.dNSName.data);
963	break;
964    case choice_GeneralName_directoryName: {
965	Name dir;
966	char *s;
967	int ret;
968	memset(&dir, 0, sizeof(dir));
969	dir.element = name->u.directoryName.element;
970	dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
971	ret = _hx509_unparse_Name(&dir, &s);
972	if (ret)
973	    return ret;
974	strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
975	free(s);
976	break;
977    }
978    case choice_GeneralName_uniformResourceIdentifier:
979	strpool = rk_strpoolprintf(strpool, "URI: %.*s",
980				   (int)name->u.uniformResourceIdentifier.length,
981				   (char *)name->u.uniformResourceIdentifier.data);
982	break;
983    case choice_GeneralName_iPAddress: {
984	unsigned char *a = name->u.iPAddress.data;
985
986	strpool = rk_strpoolprintf(strpool, "IPAddress: ");
987	if (strpool == NULL)
988	    break;
989	if (name->u.iPAddress.length == 4)
990	    strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
991				       a[0], a[1], a[2], a[3]);
992	else if (name->u.iPAddress.length == 16)
993	    strpool = rk_strpoolprintf(strpool,
994				       "%02X:%02X:%02X:%02X:"
995				       "%02X:%02X:%02X:%02X:"
996				       "%02X:%02X:%02X:%02X:"
997				       "%02X:%02X:%02X:%02X",
998				       a[0], a[1], a[2], a[3],
999				       a[4], a[5], a[6], a[7],
1000				       a[8], a[9], a[10], a[11],
1001				       a[12], a[13], a[14], a[15]);
1002	else
1003	    strpool = rk_strpoolprintf(strpool,
1004				       "unknown IP address of length %lu",
1005				       (unsigned long)name->u.iPAddress.length);
1006	break;
1007    }
1008    case choice_GeneralName_registeredID: {
1009	char *oid;
1010	hx509_oid_sprint(&name->u.registeredID, &oid);
1011	if (oid == NULL)
1012	    return ENOMEM;
1013	strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid);
1014	free(oid);
1015	break;
1016    }
1017    default:
1018	return EINVAL;
1019    }
1020    if (strpool == NULL)
1021	return ENOMEM;
1022
1023    *str = rk_strpoolcollect(strpool);
1024
1025    return 0;
1026}
1027