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