s4:heimdal: import lorikeet-heimdal-201009250123 (commit 42cabfb5b683dbcb97d583c397b8...
[samba.git] / source4 / heimdal / lib / hx509 / name.c
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
64 static 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 },
70     { "CN", &asn1_oid_id_at_commonName },
71     { "DC", &asn1_oid_id_domainComponent },
72     { "L", &asn1_oid_id_at_localityName },
73     { "O", &asn1_oid_id_at_organizationName },
74     { "OU", &asn1_oid_id_at_organizationalUnitName },
75     { "S", &asn1_oid_id_at_stateOrProvinceName },
76     { "STREET", &asn1_oid_id_at_streetAddress },
77     { "UID", &asn1_oid_id_Userid },
78     { "emailAddress", &asn1_oid_id_pkcs9_emailAddress },
79     { "serialNumber", &asn1_oid_id_at_serialNumber }
80 };
81
82 static char *
83 quote_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
121 static int
122 append_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
144 static char *
145 oidtostring(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
159 static int
160 stringtooid(const char *name, size_t len, heim_oid *oid)
161 {
162     int i, ret;
163     char *s;
164
165     memset(oid, 0, sizeof(*oid));
166
167     for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
168         if (strncasecmp(no[i].n, name, len) == 0)
169             return der_copy_oid(no[i].o, oid);
170     }
171     s = malloc(len + 1);
172     if (s == NULL)
173         return ENOMEM;
174     memcpy(s, name, len);
175     s[len] = '\0';
176     ret = der_parse_heim_oid(s, ".", oid);
177     free(s);
178     return ret;
179 }
180
181 /**
182  * Convert the hx509 name object into a printable string.
183  * The resulting string should be freed with free().
184  *
185  * @param name name to print
186  * @param str the string to return
187  *
188  * @return An hx509 error code, see hx509_get_error_string().
189  *
190  * @ingroup hx509_name
191  */
192
193 int
194 hx509_name_to_string(const hx509_name name, char **str)
195 {
196     return _hx509_Name_to_string(&name->der_name, str);
197 }
198
199 int
200 _hx509_Name_to_string(const Name *n, char **str)
201 {
202     size_t total_len = 0;
203     int i, j, ret;
204
205     *str = strdup("");
206     if (*str == NULL)
207         return ENOMEM;
208
209     for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
210         size_t len;
211
212         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
213             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
214             char *oidname;
215             char *ss;
216         
217             oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
218
219             switch(ds->element) {
220             case choice_DirectoryString_ia5String:
221                 ss = ds->u.ia5String.data;
222                 len = ds->u.ia5String.length;
223                 break;
224             case choice_DirectoryString_printableString:
225                 ss = ds->u.printableString.data;
226                 len = ds->u.printableString.length;
227                 break;
228             case choice_DirectoryString_utf8String:
229                 ss = ds->u.utf8String;
230                 len = strlen(ss);
231                 break;
232             case choice_DirectoryString_bmpString: {
233                 const uint16_t *bmp = ds->u.bmpString.data;
234                 size_t bmplen = ds->u.bmpString.length;
235                 size_t k;
236
237                 ret = wind_ucs2utf8_length(bmp, bmplen, &k);
238                 if (ret)
239                     return ret;
240                 
241                 ss = malloc(k + 1);
242                 if (ss == NULL)
243                     _hx509_abort("allocation failure"); /* XXX */
244                 ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
245                 if (ret) {
246                     free(ss);
247                     return ret;
248                 }
249                 ss[k] = '\0';
250                 len = k;
251                 break;
252             }
253             case choice_DirectoryString_teletexString:
254                 ss = ds->u.teletexString;
255                 len = strlen(ss);
256                 break;
257             case choice_DirectoryString_universalString: {
258                 const uint32_t *uni = ds->u.universalString.data;
259                 size_t unilen = ds->u.universalString.length;
260                 size_t k;
261
262                 ret = wind_ucs4utf8_length(uni, unilen, &k);
263                 if (ret)
264                     return ret;
265
266                 ss = malloc(k + 1);
267                 if (ss == NULL)
268                     _hx509_abort("allocation failure"); /* XXX */
269                 ret = wind_ucs4utf8(uni, unilen, ss, NULL);
270                 if (ret) {
271                     free(ss);
272                     return ret;
273                 }
274                 ss[k] = '\0';
275                 len = k;
276                 break;
277             }
278             default:
279                 _hx509_abort("unknown directory type: %d", ds->element);
280                 exit(1);
281             }
282             append_string(str, &total_len, oidname, strlen(oidname), 0);
283             free(oidname);
284             append_string(str, &total_len, "=", 1, 0);
285             append_string(str, &total_len, ss, len, 1);
286             if (ds->element == choice_DirectoryString_bmpString ||
287                 ds->element == choice_DirectoryString_universalString)
288             {
289                 free(ss);
290             }
291             if (j + 1 < n->u.rdnSequence.val[i].len)
292                 append_string(str, &total_len, "+", 1, 0);
293         }
294
295         if (i > 0)
296             append_string(str, &total_len, ",", 1, 0);
297     }
298     return 0;
299 }
300
301 #define COPYCHARARRAY(_ds,_el,_l,_n)            \
302         (_l) = strlen(_ds->u._el);              \
303         (_n) = malloc((_l) * sizeof((_n)[0]));  \
304         if ((_n) == NULL)                       \
305             return ENOMEM;                      \
306         for (i = 0; i < (_l); i++)              \
307             (_n)[i] = _ds->u._el[i]
308
309
310 #define COPYVALARRAY(_ds,_el,_l,_n)             \
311         (_l) = _ds->u._el.length;               \
312         (_n) = malloc((_l) * sizeof((_n)[0]));  \
313         if ((_n) == NULL)                       \
314             return ENOMEM;                      \
315         for (i = 0; i < (_l); i++)              \
316             (_n)[i] = _ds->u._el.data[i]
317
318 #define COPYVOIDARRAY(_ds,_el,_l,_n)            \
319         (_l) = _ds->u._el.length;               \
320         (_n) = malloc((_l) * sizeof((_n)[0]));  \
321         if ((_n) == NULL)                       \
322             return ENOMEM;                      \
323         for (i = 0; i < (_l); i++)              \
324             (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
325
326
327
328 static int
329 dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
330 {
331     wind_profile_flags flags;
332     size_t i, len;
333     int ret;
334     uint32_t *name;
335
336     *rname = NULL;
337     *rlen = 0;
338
339     switch(ds->element) {
340     case choice_DirectoryString_ia5String:
341         flags = WIND_PROFILE_LDAP;
342         COPYVOIDARRAY(ds, ia5String, len, name);
343         break;
344     case choice_DirectoryString_printableString:
345         flags = WIND_PROFILE_LDAP;
346         flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
347         COPYVOIDARRAY(ds, printableString, len, name);
348         break;
349     case choice_DirectoryString_teletexString:
350         flags = WIND_PROFILE_LDAP_CASE;
351         COPYCHARARRAY(ds, teletexString, len, name);
352         break;
353     case choice_DirectoryString_bmpString:
354         flags = WIND_PROFILE_LDAP;
355         COPYVALARRAY(ds, bmpString, len, name);
356         break;
357     case choice_DirectoryString_universalString:
358         flags = WIND_PROFILE_LDAP;
359         COPYVALARRAY(ds, universalString, len, name);
360         break;
361     case choice_DirectoryString_utf8String:
362         flags = WIND_PROFILE_LDAP;
363         ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
364         if (ret)
365             return ret;
366         name = malloc(len * sizeof(name[0]));
367         if (name == NULL)
368             return ENOMEM;
369         ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
370         if (ret) {
371             free(name);
372             return ret;
373         }
374         break;
375     default:
376         _hx509_abort("unknown directory type: %d", ds->element);
377     }
378
379     *rlen = len;
380     /* try a couple of times to get the length right, XXX gross */
381     for (i = 0; i < 4; i++) {
382         *rlen = *rlen * 2;
383         *rname = malloc(*rlen * sizeof((*rname)[0]));
384
385         ret = wind_stringprep(name, len, *rname, rlen, flags);
386         if (ret == WIND_ERR_OVERRUN) {
387             free(*rname);
388             *rname = NULL;
389             continue;
390         } else
391             break;
392     }
393     free(name);
394     if (ret) {
395         if (*rname)
396             free(*rname);
397         *rname = NULL;
398         *rlen = 0;
399         return ret;
400     }
401
402     return 0;
403 }
404
405 int
406 _hx509_name_ds_cmp(const DirectoryString *ds1,
407                    const DirectoryString *ds2,
408                    int *diff)
409 {
410     uint32_t *ds1lp, *ds2lp;
411     size_t ds1len, ds2len, i;
412     int ret;
413
414     ret = dsstringprep(ds1, &ds1lp, &ds1len);
415     if (ret)
416         return ret;
417     ret = dsstringprep(ds2, &ds2lp, &ds2len);
418     if (ret) {
419         free(ds1lp);
420         return ret;
421     }
422
423     if (ds1len != ds2len)
424         *diff = ds1len - ds2len;
425     else {
426         for (i = 0; i < ds1len; i++) {
427             *diff = ds1lp[i] - ds2lp[i];
428             if (*diff)
429                 break;
430         }
431     }
432     free(ds1lp);
433     free(ds2lp);
434
435     return 0;
436 }
437
438 int
439 _hx509_name_cmp(const Name *n1, const Name *n2, int *c)
440 {
441     int ret, i, j;
442
443     *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
444     if (*c)
445         return 0;
446
447     for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
448         *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
449         if (*c)
450             return 0;
451
452         for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
453             *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
454                                   &n1->u.rdnSequence.val[i].val[j].type);
455             if (*c)
456                 return 0;
457                         
458             ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
459                                      &n2->u.rdnSequence.val[i].val[j].value,
460                                      c);
461             if (ret)
462                 return ret;
463             if (*c)
464                 return 0;
465         }
466     }
467     *c = 0;
468     return 0;
469 }
470
471 /**
472  * Compare to hx509 name object, useful for sorting.
473  *
474  * @param n1 a hx509 name object.
475  * @param n2 a hx509 name object.
476  *
477  * @return 0 the objects are the same, returns > 0 is n2 is "larger"
478  * then n2, < 0 if n1 is "smaller" then n2.
479  *
480  * @ingroup hx509_name
481  */
482
483 int
484 hx509_name_cmp(hx509_name n1, hx509_name n2)
485 {
486     int ret, diff;
487     ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
488     if (ret)
489         return ret;
490     return diff;
491 }
492
493
494 int
495 _hx509_name_from_Name(const Name *n, hx509_name *name)
496 {
497     int ret;
498     *name = calloc(1, sizeof(**name));
499     if (*name == NULL)
500         return ENOMEM;
501     ret = copy_Name(n, &(*name)->der_name);
502     if (ret) {
503         free(*name);
504         *name = NULL;
505     }
506     return ret;
507 }
508
509 int
510 _hx509_name_modify(hx509_context context,
511                    Name *name,
512                    int append,
513                    const heim_oid *oid,
514                    const char *str)
515 {
516     RelativeDistinguishedName *rdn;
517     int ret;
518     void *ptr;
519
520     ptr = realloc(name->u.rdnSequence.val,
521                   sizeof(name->u.rdnSequence.val[0]) *
522                   (name->u.rdnSequence.len + 1));
523     if (ptr == NULL) {
524         hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
525         return ENOMEM;
526     }
527     name->u.rdnSequence.val = ptr;
528
529     if (append) {
530         rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
531     } else {
532         memmove(&name->u.rdnSequence.val[1],
533                 &name->u.rdnSequence.val[0],
534                 name->u.rdnSequence.len *
535                 sizeof(name->u.rdnSequence.val[0]));
536         
537         rdn = &name->u.rdnSequence.val[0];
538     }
539     rdn->val = malloc(sizeof(rdn->val[0]));
540     if (rdn->val == NULL)
541         return ENOMEM;
542     rdn->len = 1;
543     ret = der_copy_oid(oid, &rdn->val[0].type);
544     if (ret)
545         return ret;
546     rdn->val[0].value.element = choice_DirectoryString_utf8String;
547     rdn->val[0].value.u.utf8String = strdup(str);
548     if (rdn->val[0].value.u.utf8String == NULL)
549         return ENOMEM;
550     name->u.rdnSequence.len += 1;
551
552     return 0;
553 }
554
555 /**
556  * Parse a string into a hx509 name object.
557  *
558  * @param context A hx509 context.
559  * @param str a string to parse.
560  * @param name the resulting object, NULL in case of error.
561  *
562  * @return An hx509 error code, see hx509_get_error_string().
563  *
564  * @ingroup hx509_name
565  */
566
567 int
568 hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
569 {
570     const char *p, *q;
571     size_t len;
572     hx509_name n;
573     int ret;
574
575     *name = NULL;
576
577     n = calloc(1, sizeof(*n));
578     if (n == NULL) {
579         hx509_set_error_string(context, 0, ENOMEM, "out of memory");
580         return ENOMEM;
581     }
582
583     n->der_name.element = choice_Name_rdnSequence;
584
585     p = str;
586
587     while (p != NULL && *p != '\0') {
588         heim_oid oid;
589         int last;
590
591         q = strchr(p, ',');
592         if (q) {
593             len = (q - p);
594             last = 1;
595         } else {
596             len = strlen(p);
597             last = 0;
598         }
599
600         q = strchr(p, '=');
601         if (q == NULL) {
602             ret = HX509_PARSING_NAME_FAILED;
603             hx509_set_error_string(context, 0, ret, "missing = in %s", p);
604             goto out;
605         }
606         if (q == p) {
607             ret = HX509_PARSING_NAME_FAILED;
608             hx509_set_error_string(context, 0, ret,
609                                    "missing name before = in %s", p);
610             goto out;
611         }
612         
613         if ((q - p) > len) {
614             ret = HX509_PARSING_NAME_FAILED;
615             hx509_set_error_string(context, 0, ret, " = after , in %s", p);
616             goto out;
617         }
618
619         ret = stringtooid(p, q - p, &oid);
620         if (ret) {
621             ret = HX509_PARSING_NAME_FAILED;
622             hx509_set_error_string(context, 0, ret,
623                                    "unknown type: %.*s", (int)(q - p), p);
624             goto out;
625         }
626         
627         {
628             size_t pstr_len = len - (q - p) - 1;
629             const char *pstr = p + (q - p) + 1;
630             char *r;
631         
632             r = malloc(pstr_len + 1);
633             if (r == NULL) {
634                 der_free_oid(&oid);
635                 ret = ENOMEM;
636                 hx509_set_error_string(context, 0, ret, "out of memory");
637                 goto out;
638             }
639             memcpy(r, pstr, pstr_len);
640             r[pstr_len] = '\0';
641
642             ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
643             free(r);
644             der_free_oid(&oid);
645             if(ret)
646                 goto out;
647         }
648         p += len + last;
649     }
650
651     *name = n;
652
653     return 0;
654 out:
655     hx509_name_free(&n);
656     return HX509_NAME_MALFORMED;
657 }
658
659 /**
660  * Copy a hx509 name object.
661  *
662  * @param context A hx509 cotext.
663  * @param from the name to copy from
664  * @param to the name to copy to
665  *
666  * @return An hx509 error code, see hx509_get_error_string().
667  *
668  * @ingroup hx509_name
669  */
670
671 int
672 hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
673 {
674     int ret;
675
676     *to = calloc(1, sizeof(**to));
677     if (*to == NULL)
678         return ENOMEM;
679     ret = copy_Name(&from->der_name, &(*to)->der_name);
680     if (ret) {
681         free(*to);
682         *to = NULL;
683         return ENOMEM;
684     }
685     return 0;
686 }
687
688 /**
689  * Convert a hx509_name into a Name.
690  *
691  * @param from the name to copy from
692  * @param to the name to copy to
693  *
694  * @return An hx509 error code, see hx509_get_error_string().
695  *
696  * @ingroup hx509_name
697  */
698
699 int
700 hx509_name_to_Name(const hx509_name from, Name *to)
701 {
702     return copy_Name(&from->der_name, to);
703 }
704
705 int
706 hx509_name_normalize(hx509_context context, hx509_name name)
707 {
708     return 0;
709 }
710
711 /**
712  * Expands variables in the name using env. Variables are on the form
713  * ${name}. Useful when dealing with certificate templates.
714  *
715  * @param context A hx509 cotext.
716  * @param name the name to expand.
717  * @param env environment variable to expand.
718  *
719  * @return An hx509 error code, see hx509_get_error_string().
720  *
721  * @ingroup hx509_name
722  */
723
724 int
725 hx509_name_expand(hx509_context context,
726                   hx509_name name,
727                   hx509_env env)
728 {
729     Name *n = &name->der_name;
730     int i, j;
731
732     if (env == NULL)
733         return 0;
734
735     if (n->element != choice_Name_rdnSequence) {
736         hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
737         return EINVAL;
738     }
739
740     for (i = 0 ; i < n->u.rdnSequence.len; i++) {
741         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
742             /** Only UTF8String rdnSequence names are allowed */
743             /*
744               THIS SHOULD REALLY BE:
745               COMP = n->u.rdnSequence.val[i].val[j];
746               normalize COMP to utf8
747               check if there are variables
748                 expand variables
749                 convert back to orignal format, store in COMP
750               free normalized utf8 string
751             */
752             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
753             char *p, *p2;
754             struct rk_strpool *strpool = NULL;
755
756             if (ds->element != choice_DirectoryString_utf8String) {
757                 hx509_set_error_string(context, 0, EINVAL, "unsupported type");
758                 return EINVAL;
759             }
760             p = strstr(ds->u.utf8String, "${");
761             if (p) {
762                 strpool = rk_strpoolprintf(strpool, "%.*s",
763                                            (int)(p - ds->u.utf8String),
764                                            ds->u.utf8String);
765                 if (strpool == NULL) {
766                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
767                     return ENOMEM;
768                 }
769             }
770             while (p != NULL) {
771                 /* expand variables */
772                 const char *value;
773                 p2 = strchr(p, '}');
774                 if (p2 == NULL) {
775                     hx509_set_error_string(context, 0, EINVAL, "missing }");
776                     rk_strpoolfree(strpool);
777                     return EINVAL;
778                 }
779                 p += 2;
780                 value = hx509_env_lfind(context, env, p, p2 - p);
781                 if (value == NULL) {
782                     hx509_set_error_string(context, 0, EINVAL,
783                                            "variable %.*s missing",
784                                            (int)(p2 - p), p);
785                     rk_strpoolfree(strpool);
786                     return EINVAL;
787                 }
788                 strpool = rk_strpoolprintf(strpool, "%s", value);
789                 if (strpool == NULL) {
790                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
791                     return ENOMEM;
792                 }
793                 p2++;
794
795                 p = strstr(p2, "${");
796                 if (p)
797                     strpool = rk_strpoolprintf(strpool, "%.*s",
798                                                (int)(p - p2), p2);
799                 else
800                     strpool = rk_strpoolprintf(strpool, "%s", p2);
801                 if (strpool == NULL) {
802                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
803                     return ENOMEM;
804                 }
805             }
806             if (strpool) {
807                 free(ds->u.utf8String);
808                 ds->u.utf8String = rk_strpoolcollect(strpool);
809                 if (ds->u.utf8String == NULL) {
810                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
811                     return ENOMEM;
812                 }
813             }
814         }
815     }
816     return 0;
817 }
818
819 /**
820  * Free a hx509 name object, upond return *name will be NULL.
821  *
822  * @param name a hx509 name object to be freed.
823  *
824  * @ingroup hx509_name
825  */
826
827 void
828 hx509_name_free(hx509_name *name)
829 {
830     free_Name(&(*name)->der_name);
831     memset(*name, 0, sizeof(**name));
832     free(*name);
833     *name = NULL;
834 }
835
836 /**
837  * Convert a DER encoded name info a string.
838  *
839  * @param data data to a DER/BER encoded name
840  * @param length length of data
841  * @param str the resulting string, is NULL on failure.
842  *
843  * @return An hx509 error code, see hx509_get_error_string().
844  *
845  * @ingroup hx509_name
846  */
847
848 int
849 hx509_unparse_der_name(const void *data, size_t length, char **str)
850 {
851     Name name;
852     int ret;
853
854     *str = NULL;
855
856     ret = decode_Name(data, length, &name, NULL);
857     if (ret)
858         return ret;
859     ret = _hx509_Name_to_string(&name, str);
860     free_Name(&name);
861     return ret;
862 }
863
864 /**
865  * Convert a hx509_name object to DER encoded name.
866  *
867  * @param name name to concert
868  * @param os data to a DER encoded name, free the resulting octet
869  * string with hx509_xfree(os->data).
870  *
871  * @return An hx509 error code, see hx509_get_error_string().
872  *
873  * @ingroup hx509_name
874  */
875
876 int
877 hx509_name_binary(const hx509_name name, heim_octet_string *os)
878 {
879     size_t size;
880     int ret;
881
882     ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
883     if (ret)
884         return ret;
885     if (os->length != size)
886         _hx509_abort("internal ASN.1 encoder error");
887
888     return 0;
889 }
890
891 int
892 _hx509_unparse_Name(const Name *aname, char **str)
893 {
894     hx509_name name;
895     int ret;
896
897     ret = _hx509_name_from_Name(aname, &name);
898     if (ret)
899         return ret;
900
901     ret = hx509_name_to_string(name, str);
902     hx509_name_free(&name);
903     return ret;
904 }
905
906 /**
907  * Unparse the hx509 name in name into a string.
908  *
909  * @param name the name to check if its empty/null.
910  *
911  * @return non zero if the name is empty/null.
912  *
913  * @ingroup hx509_name
914  */
915
916 int
917 hx509_name_is_null_p(const hx509_name name)
918 {
919     return name->der_name.u.rdnSequence.len == 0;
920 }
921
922 /**
923  * Unparse the hx509 name in name into a string.
924  *
925  * @param name the name to print
926  * @param str an allocated string returns the name in string form
927  *
928  * @return An hx509 error code, see hx509_get_error_string().
929  *
930  * @ingroup hx509_name
931  */
932
933 int
934 hx509_general_name_unparse(GeneralName *name, char **str)
935 {
936     struct rk_strpool *strpool = NULL;
937
938     *str = NULL;
939
940     switch (name->element) {
941     case choice_GeneralName_otherName: {
942         char *oid;
943         hx509_oid_sprint(&name->u.otherName.type_id, &oid);
944         if (oid == NULL)
945             return ENOMEM;
946         strpool = rk_strpoolprintf(strpool, "otherName: %s", oid);
947         free(oid);
948         break;
949     }
950     case choice_GeneralName_rfc822Name:
951         strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n",
952                                    (int)name->u.rfc822Name.length,
953                                    (char *)name->u.rfc822Name.data);
954         break;
955     case choice_GeneralName_dNSName:
956         strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n",
957                                    (int)name->u.dNSName.length,
958                                    (char *)name->u.dNSName.data);
959         break;
960     case choice_GeneralName_directoryName: {
961         Name dir;
962         char *s;
963         int ret;
964         memset(&dir, 0, sizeof(dir));
965         dir.element = name->u.directoryName.element;
966         dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
967         ret = _hx509_unparse_Name(&dir, &s);
968         if (ret)
969             return ret;
970         strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
971         free(s);
972         break;
973     }
974     case choice_GeneralName_uniformResourceIdentifier:
975         strpool = rk_strpoolprintf(strpool, "URI: %.*s",
976                                    (int)name->u.uniformResourceIdentifier.length,
977                                    (char *)name->u.uniformResourceIdentifier.data);
978         break;
979     case choice_GeneralName_iPAddress: {
980         unsigned char *a = name->u.iPAddress.data;
981
982         strpool = rk_strpoolprintf(strpool, "IPAddress: ");
983         if (strpool == NULL)
984             break;
985         if (name->u.iPAddress.length == 4)
986             strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
987                                        a[0], a[1], a[2], a[3]);
988         else if (name->u.iPAddress.length == 16)
989             strpool = rk_strpoolprintf(strpool,
990                                        "%02X:%02X:%02X:%02X:"
991                                        "%02X:%02X:%02X:%02X:"
992                                        "%02X:%02X:%02X:%02X:"
993                                        "%02X:%02X:%02X:%02X",
994                                        a[0], a[1], a[2], a[3],
995                                        a[4], a[5], a[6], a[7],
996                                        a[8], a[9], a[10], a[11],
997                                        a[12], a[13], a[14], a[15]);
998         else
999             strpool = rk_strpoolprintf(strpool,
1000                                        "unknown IP address of length %lu",
1001                                        (unsigned long)name->u.iPAddress.length);
1002         break;
1003     }
1004     case choice_GeneralName_registeredID: {
1005         char *oid;
1006         hx509_oid_sprint(&name->u.registeredID, &oid);
1007         if (oid == NULL)
1008             return ENOMEM;
1009         strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid);
1010         free(oid);
1011         break;
1012     }
1013     default:
1014         return EINVAL;
1015     }
1016     if (strpool == NULL)
1017         return ENOMEM;
1018
1019     *str = rk_strpoolcollect(strpool);
1020
1021     return 0;
1022 }