c5844f98cc25cadf1888177dfb1be860c8154400
[abartlet/samba.git/.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, i;
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         for (i = 0; i < ds1len; i++) {
418             *diff = ds1lp[i] - ds2lp[i];
419             if (*diff)
420                 break;
421         }
422     }
423     free(ds1lp);
424     free(ds2lp);
425
426     return 0;
427 }
428
429 int
430 _hx509_name_cmp(const Name *n1, const Name *n2, int *c)
431 {
432     int ret, i, j;
433
434     *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
435     if (*c)
436         return 0;
437
438     for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
439         *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
440         if (*c)
441             return 0;
442
443         for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
444             *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
445                                   &n1->u.rdnSequence.val[i].val[j].type);
446             if (*c)
447                 return 0;
448                         
449             ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
450                                      &n2->u.rdnSequence.val[i].val[j].value,
451                                      c);
452             if (ret)
453                 return ret;
454             if (*c)
455                 return 0;
456         }
457     }
458     *c = 0;
459     return 0;
460 }
461
462 /**
463  * Compare to hx509 name object, useful for sorting.
464  *
465  * @param n1 a hx509 name object.
466  * @param n2 a hx509 name object.
467  *
468  * @return 0 the objects are the same, returns > 0 is n2 is "larger"
469  * then n2, < 0 if n1 is "smaller" then n2.
470  *
471  * @ingroup hx509_name
472  */
473
474 int
475 hx509_name_cmp(hx509_name n1, hx509_name n2)
476 {
477     int ret, diff;
478     ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
479     if (ret)
480         return ret;
481     return diff;
482 }
483
484
485 int
486 _hx509_name_from_Name(const Name *n, hx509_name *name)
487 {
488     int ret;
489     *name = calloc(1, sizeof(**name));
490     if (*name == NULL)
491         return ENOMEM;
492     ret = copy_Name(n, &(*name)->der_name);
493     if (ret) {
494         free(*name);
495         *name = NULL;
496     }
497     return ret;
498 }
499
500 int
501 _hx509_name_modify(hx509_context context,
502                    Name *name,
503                    int append,
504                    const heim_oid *oid,
505                    const char *str)
506 {
507     RelativeDistinguishedName *rdn;
508     int ret;
509     void *ptr;
510
511     ptr = realloc(name->u.rdnSequence.val,
512                   sizeof(name->u.rdnSequence.val[0]) *
513                   (name->u.rdnSequence.len + 1));
514     if (ptr == NULL) {
515         hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
516         return ENOMEM;
517     }
518     name->u.rdnSequence.val = ptr;
519
520     if (append) {
521         rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
522     } else {
523         memmove(&name->u.rdnSequence.val[1],
524                 &name->u.rdnSequence.val[0],
525                 name->u.rdnSequence.len *
526                 sizeof(name->u.rdnSequence.val[0]));
527         
528         rdn = &name->u.rdnSequence.val[0];
529     }
530     rdn->val = malloc(sizeof(rdn->val[0]));
531     if (rdn->val == NULL)
532         return ENOMEM;
533     rdn->len = 1;
534     ret = der_copy_oid(oid, &rdn->val[0].type);
535     if (ret)
536         return ret;
537     rdn->val[0].value.element = choice_DirectoryString_utf8String;
538     rdn->val[0].value.u.utf8String = strdup(str);
539     if (rdn->val[0].value.u.utf8String == NULL)
540         return ENOMEM;
541     name->u.rdnSequence.len += 1;
542
543     return 0;
544 }
545
546 /**
547  * Parse a string into a hx509 name object.
548  *
549  * @param context A hx509 context.
550  * @param str a string to parse.
551  * @param name the resulting object, NULL in case of error.
552  *
553  * @return An hx509 error code, see hx509_get_error_string().
554  *
555  * @ingroup hx509_name
556  */
557
558 int
559 hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
560 {
561     const char *p, *q;
562     size_t len;
563     hx509_name n;
564     int ret;
565
566     *name = NULL;
567
568     n = calloc(1, sizeof(*n));
569     if (n == NULL) {
570         hx509_set_error_string(context, 0, ENOMEM, "out of memory");
571         return ENOMEM;
572     }
573
574     n->der_name.element = choice_Name_rdnSequence;
575
576     p = str;
577
578     while (p != NULL && *p != '\0') {
579         heim_oid oid;
580         int last;
581
582         q = strchr(p, ',');
583         if (q) {
584             len = (q - p);
585             last = 1;
586         } else {
587             len = strlen(p);
588             last = 0;
589         }
590
591         q = strchr(p, '=');
592         if (q == NULL) {
593             ret = HX509_PARSING_NAME_FAILED;
594             hx509_set_error_string(context, 0, ret, "missing = in %s", p);
595             goto out;
596         }
597         if (q == p) {
598             ret = HX509_PARSING_NAME_FAILED;
599             hx509_set_error_string(context, 0, ret,
600                                    "missing name before = in %s", p);
601             goto out;
602         }
603         
604         if ((q - p) > len) {
605             ret = HX509_PARSING_NAME_FAILED;
606             hx509_set_error_string(context, 0, ret, " = after , in %s", p);
607             goto out;
608         }
609
610         ret = stringtooid(p, q - p, &oid);
611         if (ret) {
612             ret = HX509_PARSING_NAME_FAILED;
613             hx509_set_error_string(context, 0, ret,
614                                    "unknown type: %.*s", (int)(q - p), p);
615             goto out;
616         }
617         
618         {
619             size_t pstr_len = len - (q - p) - 1;
620             const char *pstr = p + (q - p) + 1;
621             char *r;
622         
623             r = malloc(pstr_len + 1);
624             if (r == NULL) {
625                 der_free_oid(&oid);
626                 ret = ENOMEM;
627                 hx509_set_error_string(context, 0, ret, "out of memory");
628                 goto out;
629             }
630             memcpy(r, pstr, pstr_len);
631             r[pstr_len] = '\0';
632
633             ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
634             free(r);
635             der_free_oid(&oid);
636             if(ret)
637                 goto out;
638         }
639         p += len + last;
640     }
641
642     *name = n;
643
644     return 0;
645 out:
646     hx509_name_free(&n);
647     return HX509_NAME_MALFORMED;
648 }
649
650 /**
651  * Copy a hx509 name object.
652  *
653  * @param context A hx509 cotext.
654  * @param from the name to copy from
655  * @param to the name to copy to
656  *
657  * @return An hx509 error code, see hx509_get_error_string().
658  *
659  * @ingroup hx509_name
660  */
661
662 int
663 hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
664 {
665     int ret;
666
667     *to = calloc(1, sizeof(**to));
668     if (*to == NULL)
669         return ENOMEM;
670     ret = copy_Name(&from->der_name, &(*to)->der_name);
671     if (ret) {
672         free(*to);
673         *to = NULL;
674         return ENOMEM;
675     }
676     return 0;
677 }
678
679 /**
680  * Convert a hx509_name into a Name.
681  *
682  * @param from the name to copy from
683  * @param to the name to copy to
684  *
685  * @return An hx509 error code, see hx509_get_error_string().
686  *
687  * @ingroup hx509_name
688  */
689
690 int
691 hx509_name_to_Name(const hx509_name from, Name *to)
692 {
693     return copy_Name(&from->der_name, to);
694 }
695
696 int
697 hx509_name_normalize(hx509_context context, hx509_name name)
698 {
699     return 0;
700 }
701
702 /**
703  * Expands variables in the name using env. Variables are on the form
704  * ${name}. Useful when dealing with certificate templates.
705  *
706  * @param context A hx509 cotext.
707  * @param name the name to expand.
708  * @param env environment variable to expand.
709  *
710  * @return An hx509 error code, see hx509_get_error_string().
711  *
712  * @ingroup hx509_name
713  */
714
715 int
716 hx509_name_expand(hx509_context context,
717                   hx509_name name,
718                   hx509_env env)
719 {
720     Name *n = &name->der_name;
721     int i, j;
722
723     if (env == NULL)
724         return 0;
725
726     if (n->element != choice_Name_rdnSequence) {
727         hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
728         return EINVAL;
729     }
730
731     for (i = 0 ; i < n->u.rdnSequence.len; i++) {
732         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
733             /** Only UTF8String rdnSequence names are allowed */
734             /*
735               THIS SHOULD REALLY BE:
736               COMP = n->u.rdnSequence.val[i].val[j];
737               normalize COMP to utf8
738               check if there are variables
739                 expand variables
740                 convert back to orignal format, store in COMP
741               free normalized utf8 string
742             */
743             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
744             char *p, *p2;
745             struct rk_strpool *strpool = NULL;
746
747             if (ds->element != choice_DirectoryString_utf8String) {
748                 hx509_set_error_string(context, 0, EINVAL, "unsupported type");
749                 return EINVAL;
750             }
751             p = strstr(ds->u.utf8String, "${");
752             if (p) {
753                 strpool = rk_strpoolprintf(strpool, "%.*s",
754                                            (int)(p - ds->u.utf8String),
755                                            ds->u.utf8String);
756                 if (strpool == NULL) {
757                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
758                     return ENOMEM;
759                 }
760             }
761             while (p != NULL) {
762                 /* expand variables */
763                 const char *value;
764                 p2 = strchr(p, '}');
765                 if (p2 == NULL) {
766                     hx509_set_error_string(context, 0, EINVAL, "missing }");
767                     rk_strpoolfree(strpool);
768                     return EINVAL;
769                 }
770                 p += 2;
771                 value = hx509_env_lfind(context, env, p, p2 - p);
772                 if (value == NULL) {
773                     hx509_set_error_string(context, 0, EINVAL,
774                                            "variable %.*s missing",
775                                            (int)(p2 - p), p);
776                     rk_strpoolfree(strpool);
777                     return EINVAL;
778                 }
779                 strpool = rk_strpoolprintf(strpool, "%s", value);
780                 if (strpool == NULL) {
781                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
782                     return ENOMEM;
783                 }
784                 p2++;
785
786                 p = strstr(p2, "${");
787                 if (p)
788                     strpool = rk_strpoolprintf(strpool, "%.*s",
789                                                (int)(p - p2), p2);
790                 else
791                     strpool = rk_strpoolprintf(strpool, "%s", p2);
792                 if (strpool == NULL) {
793                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
794                     return ENOMEM;
795                 }
796             }
797             if (strpool) {
798                 free(ds->u.utf8String);
799                 ds->u.utf8String = rk_strpoolcollect(strpool);
800                 if (ds->u.utf8String == NULL) {
801                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
802                     return ENOMEM;
803                 }
804             }
805         }
806     }
807     return 0;
808 }
809
810 /**
811  * Free a hx509 name object, upond return *name will be NULL.
812  *
813  * @param name a hx509 name object to be freed.
814  *
815  * @ingroup hx509_name
816  */
817
818 void
819 hx509_name_free(hx509_name *name)
820 {
821     free_Name(&(*name)->der_name);
822     memset(*name, 0, sizeof(**name));
823     free(*name);
824     *name = NULL;
825 }
826
827 /**
828  * Convert a DER encoded name info a string.
829  *
830  * @param data data to a DER/BER encoded name
831  * @param length length of data
832  * @param str the resulting string, is NULL on failure.
833  *
834  * @return An hx509 error code, see hx509_get_error_string().
835  *
836  * @ingroup hx509_name
837  */
838
839 int
840 hx509_unparse_der_name(const void *data, size_t length, char **str)
841 {
842     Name name;
843     int ret;
844
845     *str = NULL;
846
847     ret = decode_Name(data, length, &name, NULL);
848     if (ret)
849         return ret;
850     ret = _hx509_Name_to_string(&name, str);
851     free_Name(&name);
852     return ret;
853 }
854
855 /**
856  * Convert a hx509_name object to DER encoded name.
857  *
858  * @param name name to concert
859  * @param os data to a DER encoded name, free the resulting octet
860  * string with hx509_xfree(os->data).
861  *
862  * @return An hx509 error code, see hx509_get_error_string().
863  *
864  * @ingroup hx509_name
865  */
866
867 int
868 hx509_name_binary(const hx509_name name, heim_octet_string *os)
869 {
870     size_t size;
871     int ret;
872
873     ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
874     if (ret)
875         return ret;
876     if (os->length != size)
877         _hx509_abort("internal ASN.1 encoder error");
878
879     return 0;
880 }
881
882 int
883 _hx509_unparse_Name(const Name *aname, char **str)
884 {
885     hx509_name name;
886     int ret;
887
888     ret = _hx509_name_from_Name(aname, &name);
889     if (ret)
890         return ret;
891
892     ret = hx509_name_to_string(name, str);
893     hx509_name_free(&name);
894     return ret;
895 }
896
897 /**
898  * Unparse the hx509 name in name into a string.
899  *
900  * @param name the name to check if its empty/null.
901  *
902  * @return non zero if the name is empty/null.
903  *
904  * @ingroup hx509_name
905  */
906
907 int
908 hx509_name_is_null_p(const hx509_name name)
909 {
910     return name->der_name.u.rdnSequence.len == 0;
911 }
912
913 /**
914  * Unparse the hx509 name in name into a string.
915  *
916  * @param name the name to print
917  * @param str an allocated string returns the name in string form
918  *
919  * @return An hx509 error code, see hx509_get_error_string().
920  *
921  * @ingroup hx509_name
922  */
923
924 int
925 hx509_general_name_unparse(GeneralName *name, char **str)
926 {
927     struct rk_strpool *strpool = NULL;
928
929     *str = NULL;
930
931     switch (name->element) {
932     case choice_GeneralName_otherName: {
933         char *str2;
934         hx509_oid_sprint(&name->u.otherName.type_id, &str2);
935         if (str2 == NULL)
936             return ENOMEM;
937         strpool = rk_strpoolprintf(strpool, "otherName: %s", str2);
938         free(str2);
939         break;
940     }
941     case choice_GeneralName_rfc822Name:
942         strpool = rk_strpoolprintf(strpool, "rfc822Name: %s\n",
943                                    name->u.rfc822Name);
944         break;
945     case choice_GeneralName_dNSName:
946         strpool = rk_strpoolprintf(strpool, "dNSName: %s\n",
947                                    name->u.dNSName);
948         break;
949     case choice_GeneralName_directoryName: {
950         Name dir;
951         char *s;
952         int ret;
953         memset(&dir, 0, sizeof(dir));
954         dir.element = name->u.directoryName.element;
955         dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
956         ret = _hx509_unparse_Name(&dir, &s);
957         if (ret)
958             return ret;
959         strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
960         free(s);
961         break;
962     }
963     case choice_GeneralName_uniformResourceIdentifier:
964         strpool = rk_strpoolprintf(strpool, "URI: %s",
965                                    name->u.uniformResourceIdentifier);
966         break;
967     case choice_GeneralName_iPAddress: {
968         unsigned char *a = name->u.iPAddress.data;
969
970         strpool = rk_strpoolprintf(strpool, "IPAddress: ");
971         if (strpool == NULL)
972             break;
973         if (name->u.iPAddress.length == 4)
974             strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
975                                        a[0], a[1], a[2], a[3]);
976         else if (name->u.iPAddress.length == 16)
977             strpool = rk_strpoolprintf(strpool,
978                                        "%02X:%02X:%02X:%02X:"
979                                        "%02X:%02X:%02X:%02X:"
980                                        "%02X:%02X:%02X:%02X:"
981                                        "%02X:%02X:%02X:%02X",
982                                        a[0], a[1], a[2], a[3],
983                                        a[4], a[5], a[6], a[7],
984                                        a[8], a[9], a[10], a[11],
985                                        a[12], a[13], a[14], a[15]);
986         else
987             strpool = rk_strpoolprintf(strpool,
988                                        "unknown IP address of length %lu",
989                                        (unsigned long)name->u.iPAddress.length);
990         break;
991     }
992     case choice_GeneralName_registeredID: {
993         char *str2;
994         hx509_oid_sprint(&name->u.registeredID, &str2);
995         if (str2 == NULL)
996             return ENOMEM;
997         strpool = rk_strpoolprintf(strpool, "registeredID: %s", str2);
998         free(str2);
999         break;
1000     }
1001     default:
1002         return EINVAL;
1003     }
1004     if (strpool == NULL)
1005         return ENOMEM;
1006
1007     *str = rk_strpoolcollect(strpool);
1008
1009     return 0;
1010 }