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