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