r23456: Update Samba4 to current lorikeet-heimdal.
[sfrench/samba-autobuild/.git] / source4 / 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 20891 2007-06-04 22:51:41Z lha $");
36
37 /* 
38  * name parsing from rfc2253
39  * fix so parsing rfc1779 works too 
40  * rfc3280
41  */
42
43 static const struct {
44     const char *n;
45     const heim_oid *(*o)(void);
46 } no[] = {
47     { "C", oid_id_at_countryName },
48     { "CN", oid_id_at_commonName },
49     { "DC", oid_id_domainComponent },
50     { "L", oid_id_at_localityName },
51     { "O", oid_id_at_organizationName },
52     { "OU", oid_id_at_organizationalUnitName },
53     { "S", oid_id_at_stateOrProvinceName },
54     { "STREET", oid_id_at_streetAddress },
55     { "UID", oid_id_Userid },
56     { "emailAddress", oid_id_pkcs9_emailAddress },
57     { "serialNumber", oid_id_at_serialNumber }
58 };
59
60 static char *
61 quote_string(const char *f, size_t len, size_t *rlen)
62 {
63     size_t i, j, tolen;
64     const char *from = f;
65     char *to;
66
67     tolen = len * 3 + 1;
68     to = malloc(tolen);
69     if (to == NULL)
70         return NULL;
71
72     for (i = 0, j = 0; i < len; i++) {
73         if (from[i] == ' ' && i + 1 < len)
74             to[j++] = from[i];
75         else if (from[i] == ',' || from[i] == '=' || from[i] == '+' ||
76                  from[i] == '<' || from[i] == '>' || from[i] == '#' ||
77                  from[i] == ';' || from[i] == ' ')
78         {
79             to[j++] = '\\';
80             to[j++] = from[i];
81         } else if (((unsigned char)from[i]) >= 32 && ((unsigned char)from[i]) <= 127) {
82             to[j++] = from[i];
83         } else {
84             int l = snprintf(&to[j], tolen - j - 1,
85                              "#%02x", (unsigned char)from[i]);
86             j += l;
87         }
88     }
89     to[j] = '\0';
90     assert(j < tolen);
91     *rlen = j;
92     return to;
93 }
94
95
96 static int
97 append_string(char **str, size_t *total_len, const char *ss, 
98               size_t len, int quote)
99 {
100     char *s, *qs;
101
102     if (quote)
103         qs = quote_string(ss, len, &len);
104     else
105         qs = rk_UNCONST(ss);
106
107     s = realloc(*str, len + *total_len + 1);
108     if (s == NULL)
109         _hx509_abort("allocation failure"); /* XXX */
110     memcpy(s + *total_len, qs, len);
111     if (qs != ss)
112         free(qs);
113     s[*total_len + len] = '\0';
114     *str = s;
115     *total_len += len;
116     return 0;
117 }
118
119 static char *
120 oidtostring(const heim_oid *type)
121 {
122     char *s;
123     size_t i;
124     
125     for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
126         if (der_heim_oid_cmp((*no[i].o)(), type) == 0)
127             return strdup(no[i].n);
128     }
129     if (der_print_heim_oid(type, '.', &s) != 0)
130         return NULL;
131     return s;
132 }
133
134 static int
135 stringtooid(const char *name, size_t len, heim_oid *oid)
136 {
137     int i, ret;
138     char *s;
139     
140     memset(oid, 0, sizeof(*oid));
141
142     for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
143         if (strncasecmp(no[i].n, name, len) == 0)
144             return der_copy_oid((*no[i].o)(), oid);
145     }
146     s = malloc(len + 1);
147     if (s == NULL)
148         return ENOMEM;
149     memcpy(s, name, len);
150     s[len] = '\0';
151     ret = der_parse_heim_oid(s, ".", oid);
152     free(s);
153     return ret;
154 }
155
156 int
157 hx509_name_to_string(const hx509_name name, char **str)
158 {
159     return _hx509_Name_to_string(&name->der_name, str);
160 }
161
162 int
163 _hx509_Name_to_string(const Name *n, char **str)
164 {
165     size_t total_len = 0;
166     int i, j;
167
168     *str = strdup("");
169     if (*str == NULL)
170         return ENOMEM;
171
172     for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
173         int len;
174
175         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
176             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
177             char *oidname;
178             char *ss;
179             
180             oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
181
182             switch(ds->element) {
183             case choice_DirectoryString_ia5String:
184                 ss = ds->u.ia5String;
185                 break;
186             case choice_DirectoryString_printableString:
187                 ss = ds->u.printableString;
188                 break;
189             case choice_DirectoryString_utf8String:
190                 ss = ds->u.utf8String;
191                 break;
192             case choice_DirectoryString_bmpString: {
193                 uint16_t *bmp = ds->u.bmpString.data;
194                 size_t bmplen = ds->u.bmpString.length;
195                 size_t k;
196
197                 ss = malloc(bmplen + 1);
198                 if (ss == NULL)
199                     _hx509_abort("allocation failure"); /* XXX */
200                 for (k = 0; k < bmplen; k++)
201                     ss[k] = bmp[k] & 0xff; /* XXX */
202                 ss[k] = '\0';
203                 break;
204             }
205             case choice_DirectoryString_teletexString:
206                 ss = malloc(ds->u.teletexString.length + 1);
207                 if (ss == NULL)
208                     _hx509_abort("allocation failure"); /* XXX */
209                 memcpy(ss, ds->u.teletexString.data, ds->u.teletexString.length);
210                 ss[ds->u.teletexString.length] = '\0';
211                 break;
212             case choice_DirectoryString_universalString: {
213                 uint32_t *uni = ds->u.universalString.data;
214                 size_t unilen = ds->u.universalString.length;
215                 size_t k;
216
217                 ss = malloc(unilen + 1);
218                 if (ss == NULL)
219                     _hx509_abort("allocation failure"); /* XXX */
220                 for (k = 0; k < unilen; k++)
221                     ss[k] = uni[k] & 0xff; /* XXX */
222                 ss[k] = '\0';
223                 break;
224             }
225             default:
226                 _hx509_abort("unknown directory type: %d", ds->element);
227                 exit(1);
228             }
229             append_string(str, &total_len, oidname, strlen(oidname), 0);
230             free(oidname);
231             append_string(str, &total_len, "=", 1, 0);
232             len = strlen(ss);
233             append_string(str, &total_len, ss, len, 1);
234             if (ds->element == choice_DirectoryString_universalString ||
235                 ds->element == choice_DirectoryString_bmpString ||
236                 ds->element == choice_DirectoryString_teletexString)
237             {
238                 free(ss);
239             }
240             if (j + 1 < n->u.rdnSequence.val[i].len)
241                 append_string(str, &total_len, "+", 1, 0);
242         }
243
244         if (i > 0)
245             append_string(str, &total_len, ",", 1, 0);
246     }
247     return 0;
248 }
249
250 /*
251  * XXX this function is broken, it needs to compare code points, not
252  * bytes.
253  */
254
255 int
256 _hx509_name_ds_cmp(const DirectoryString *ds1, const DirectoryString *ds2)
257 {
258     int c;
259
260     c = ds1->element - ds2->element;
261     if (c)
262         return c;
263
264     switch(ds1->element) {
265     case choice_DirectoryString_ia5String:
266         c = strcmp(ds1->u.ia5String, ds2->u.ia5String);
267         break;
268     case choice_DirectoryString_teletexString:
269         c = der_heim_octet_string_cmp(&ds1->u.teletexString,
270                                   &ds2->u.teletexString);
271         break;
272     case choice_DirectoryString_printableString:
273         c = strcasecmp(ds1->u.printableString, ds2->u.printableString);
274         break;
275     case choice_DirectoryString_utf8String:
276         c = strcmp(ds1->u.utf8String, ds2->u.utf8String);
277         break;
278     case choice_DirectoryString_universalString:
279         c = der_heim_universal_string_cmp(&ds1->u.universalString,
280                                           &ds2->u.universalString);
281         break;
282     case choice_DirectoryString_bmpString:
283         c = der_heim_bmp_string_cmp(&ds1->u.bmpString,
284                                     &ds2->u.bmpString);
285         break;
286     default:
287         c = 1;
288         break;
289     }
290     return c;
291 }
292
293 int
294 _hx509_name_cmp(const Name *n1, const Name *n2)
295 {
296     int i, j, c;
297
298     c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
299     if (c)
300         return c;
301
302     for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
303         c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
304         if (c)
305             return c;
306
307         for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
308             c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
309                                  &n1->u.rdnSequence.val[i].val[j].type);
310             if (c)
311                 return c;
312                              
313             c = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
314                                    &n2->u.rdnSequence.val[i].val[j].value);
315             if (c)
316                 return c;
317         }
318     }
319     return 0;
320 }
321
322 int
323 hx509_name_cmp(hx509_name n1, hx509_name n2)
324 {
325     return _hx509_name_cmp(&n1->der_name, &n2->der_name);
326 }
327
328
329 int
330 _hx509_name_from_Name(const Name *n, hx509_name *name)
331 {
332     int ret;
333     *name = calloc(1, sizeof(**name));
334     if (*name == NULL)
335         return ENOMEM;
336     ret = copy_Name(n, &(*name)->der_name);
337     if (ret) {
338         free(*name);
339         *name = NULL;
340     }
341     return ret;
342 }
343
344 static int
345 hx509_der_parse_name(const void *data, size_t length, hx509_name *name)
346 {
347     int ret;
348     Name n;
349
350     *name = NULL;
351     ret = decode_Name(data, length, &n, NULL);
352     if (ret)
353         return ret;
354     return _hx509_name_from_Name(&n, name);
355 }
356
357 int
358 _hx509_name_modify(hx509_context context,
359                    Name *name, 
360                    int append,
361                    const heim_oid *oid, 
362                    const char *str)
363 {
364     RelativeDistinguishedName *rdn;
365     int ret;
366     void *ptr;
367
368     ptr = realloc(name->u.rdnSequence.val, 
369                   sizeof(name->u.rdnSequence.val[0]) * 
370                   (name->u.rdnSequence.len + 1));
371     if (ptr == NULL) {
372         hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
373         return ENOMEM;
374     }
375     name->u.rdnSequence.val = ptr;
376
377     if (append) {
378         rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
379     } else {
380         memmove(&name->u.rdnSequence.val[1],
381                 &name->u.rdnSequence.val[0],
382                 name->u.rdnSequence.len * 
383                 sizeof(name->u.rdnSequence.val[0]));
384         
385         rdn = &name->u.rdnSequence.val[0];
386     }
387     rdn->val = malloc(sizeof(rdn->val[0]));
388     if (rdn->val == NULL)
389         return ENOMEM;
390     rdn->len = 1;
391     ret = der_copy_oid(oid, &rdn->val[0].type);
392     if (ret)
393         return ret;
394     rdn->val[0].value.element = choice_DirectoryString_utf8String;
395     rdn->val[0].value.u.utf8String = strdup(str);
396     if (rdn->val[0].value.u.utf8String == NULL)
397         return ENOMEM;
398     name->u.rdnSequence.len += 1;
399
400     return 0;
401 }
402
403 int
404 hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
405 {
406     const char *p, *q;
407     size_t len;
408     hx509_name n;
409     int ret;
410
411     *name = NULL;
412
413     n = calloc(1, sizeof(*n));
414     if (n == NULL) {
415         hx509_set_error_string(context, 0, ENOMEM, "out of memory");
416         return ENOMEM;
417     }
418
419     n->der_name.element = choice_Name_rdnSequence;
420
421     p = str;
422
423     while (p != NULL && *p != '\0') {
424         heim_oid oid;
425         int last;
426
427         q = strchr(p, ',');
428         if (q) {
429             len = (q - p);
430             last = 1;
431         } else {
432             len = strlen(p);
433             last = 0;
434         }
435
436         q = strchr(p, '=');
437         if (q == NULL) {
438             ret = HX509_PARSING_NAME_FAILED;
439             hx509_set_error_string(context, 0, ret, "missing = in %s", p);
440             goto out;
441         }
442         if (q == p) {
443             ret = HX509_PARSING_NAME_FAILED;
444             hx509_set_error_string(context, 0, ret, 
445                                    "missing name before = in %s", p);
446             goto out;
447         }
448         
449         if ((q - p) > len) {
450             ret = HX509_PARSING_NAME_FAILED;
451             hx509_set_error_string(context, 0, ret, " = after , in %s", p);
452             goto out;
453         }
454
455         ret = stringtooid(p, q - p, &oid);
456         if (ret) {
457             ret = HX509_PARSING_NAME_FAILED;
458             hx509_set_error_string(context, 0, ret, 
459                                    "unknown type: %.*s", (int)(q - p), p);
460             goto out;
461         }
462         
463         {
464             size_t pstr_len = len - (q - p) - 1;
465             const char *pstr = p + (q - p) + 1;
466             char *r;
467             
468             r = malloc(pstr_len + 1);
469             if (r == NULL) {
470                 der_free_oid(&oid);
471                 ret = ENOMEM;
472                 hx509_set_error_string(context, 0, ret, "out of memory");
473                 goto out;
474             }
475             memcpy(r, pstr, pstr_len);
476             r[pstr_len] = '\0';
477
478             ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
479             free(r);
480             der_free_oid(&oid);
481             if(ret)
482                 goto out;
483         }
484         p += len + last;
485     }
486
487     *name = n;
488
489     return 0;
490 out:
491     hx509_name_free(&n);
492     return HX509_NAME_MALFORMED;
493 }
494
495 int
496 hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
497 {
498     int ret;
499
500     *to = calloc(1, sizeof(**to));
501     if (*to == NULL)
502         return ENOMEM;
503     ret = copy_Name(&from->der_name, &(*to)->der_name);
504     if (ret) {
505         free(*to);
506         *to = NULL;
507         return ENOMEM;
508     }
509     return 0;
510 }
511
512 int
513 hx509_name_to_Name(const hx509_name from, Name *to)
514 {
515     return copy_Name(&from->der_name, to);
516 }
517
518 int
519 hx509_name_normalize(hx509_context context, hx509_name name)
520 {
521     return 0;
522 }
523
524 int
525 hx509_name_expand(hx509_context context,
526                   hx509_name name,
527                   hx509_env env)
528 {
529     Name *n = &name->der_name;
530     int i, j;
531
532     if (env == NULL)
533         return 0;
534
535     if (n->element != choice_Name_rdnSequence) {
536         hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
537         return EINVAL;
538     }
539
540     for (i = 0 ; i < n->u.rdnSequence.len; i++) {
541         for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
542             /*
543               THIS SHOULD REALLY BE:
544               COMP = n->u.rdnSequence.val[i].val[j];
545               normalize COMP to utf8
546               check if there are variables
547                 expand variables
548                 convert back to orignal format, store in COMP
549               free normalized utf8 string
550             */
551             DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
552             char *p, *p2;
553             struct rk_strpool *strpool = NULL;
554
555             if (ds->element != choice_DirectoryString_utf8String) {
556                 hx509_set_error_string(context, 0, EINVAL, "unsupported type");
557                 return EINVAL;
558             }
559             p = strstr(ds->u.utf8String, "${");
560             if (p) {
561                 strpool = rk_strpoolprintf(strpool, "%.*s", 
562                                            (int)(p - ds->u.utf8String), 
563                                            ds->u.utf8String);
564                 if (strpool == NULL) {
565                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
566                     return ENOMEM;
567                 }
568             }
569             while (p != NULL) {
570                 /* expand variables */
571                 const char *value;
572                 p2 = strchr(p, '}');
573                 if (p2 == NULL) {
574                     hx509_set_error_string(context, 0, EINVAL, "missing }");
575                     rk_strpoolfree(strpool);
576                     return EINVAL;
577                 }
578                 p += 2;
579                 value = hx509_env_lfind(context, env, p, p2 - p);
580                 if (value == NULL) {
581                     hx509_set_error_string(context, 0, EINVAL, 
582                                            "variable %.*s missing",
583                                            (int)(p2 - p), p);
584                     rk_strpoolfree(strpool);
585                     return EINVAL;
586                 }
587                 strpool = rk_strpoolprintf(strpool, "%s", value);
588                 if (strpool == NULL) {
589                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
590                     return ENOMEM;
591                 }
592                 p2++;
593
594                 p = strstr(p2, "${");
595                 if (p)
596                     strpool = rk_strpoolprintf(strpool, "%.*s", 
597                                                (int)(p - p2), p2);
598                 else
599                     strpool = rk_strpoolprintf(strpool, "%s", p2);
600                 if (strpool == NULL) {
601                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
602                     return ENOMEM;
603                 }
604             }
605             if (strpool) {
606                 free(ds->u.utf8String);
607                 ds->u.utf8String = rk_strpoolcollect(strpool);
608                 if (ds->u.utf8String == NULL) {
609                     hx509_set_error_string(context, 0, ENOMEM, "out of memory");
610                     return ENOMEM;
611                 }
612             }
613         }
614     }
615     return 0;
616 }
617
618
619 void
620 hx509_name_free(hx509_name *name)
621 {
622     free_Name(&(*name)->der_name);
623     memset(*name, 0, sizeof(**name));
624     free(*name);
625     *name = NULL;
626 }
627
628 int
629 hx509_unparse_der_name(const void *data, size_t length, char **str)
630 {
631     hx509_name name;
632     int ret;
633
634     ret = hx509_der_parse_name(data, length, &name);
635     if (ret)
636         return ret;
637     
638     ret = hx509_name_to_string(name, str);
639     hx509_name_free(&name);
640     return ret;
641 }
642
643 int
644 hx509_name_to_der_name(const hx509_name name, void **data, size_t *length)
645 {
646     size_t size;
647     int ret;
648
649     ASN1_MALLOC_ENCODE(Name, *data, *length, &name->der_name, &size, ret);
650     if (ret)
651         return ret;
652     if (*length != size)
653         _hx509_abort("internal ASN.1 encoder error");
654
655     return 0;
656 }
657
658
659 int
660 _hx509_unparse_Name(const Name *aname, char **str)
661 {
662     hx509_name name;
663     int ret;
664
665     ret = _hx509_name_from_Name(aname, &name);
666     if (ret)
667         return ret;
668
669     ret = hx509_name_to_string(name, str);
670     hx509_name_free(&name);
671     return ret;
672 }
673
674 int
675 hx509_name_is_null_p(const hx509_name name)
676 {
677     return name->der_name.u.rdnSequence.len == 0;
678 }
679
680 int
681 hx509_general_name_unparse(GeneralName *name, char **str)
682 {
683     struct rk_strpool *strpool = NULL;
684
685     *str = NULL;
686
687     switch (name->element) {
688     case choice_GeneralName_otherName: {
689         char *str;
690         hx509_oid_sprint(&name->u.otherName.type_id, &str);
691         if (str == NULL)
692             return ENOMEM;
693         strpool = rk_strpoolprintf(strpool, "otherName: %s", str);
694         free(str);
695         break;
696     }
697     case choice_GeneralName_rfc822Name:
698         strpool = rk_strpoolprintf(strpool, "rfc822Name: %s\n",
699                                    name->u.rfc822Name);
700         break;
701     case choice_GeneralName_dNSName:
702         strpool = rk_strpoolprintf(strpool, "dNSName: %s\n",
703                                    name->u.dNSName);
704         break;
705     case choice_GeneralName_directoryName: {
706         Name dir;
707         char *s;
708         int ret;
709         memset(&dir, 0, sizeof(dir));
710         dir.element = name->u.directoryName.element;
711         dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
712         ret = _hx509_unparse_Name(&dir, &s);
713         if (ret)
714             return ret;
715         strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
716         free(s);
717         break;
718     }
719     case choice_GeneralName_uniformResourceIdentifier:
720         strpool = rk_strpoolprintf(strpool, "URI: %s", 
721                                    name->u.uniformResourceIdentifier);
722         break;
723     case choice_GeneralName_iPAddress: {
724         unsigned char *a = name->u.iPAddress.data;
725
726         strpool = rk_strpoolprintf(strpool, "IPAddress: ");
727         if (strpool == NULL)
728             break;
729         if (name->u.iPAddress.length == 4)
730             strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d", 
731                                        a[0], a[1], a[2], a[3]);
732         else if (name->u.iPAddress.length == 16)
733             strpool = rk_strpoolprintf(strpool, 
734                                        "%02X:%02X:%02X:%02X:"
735                                        "%02X:%02X:%02X:%02X:"
736                                        "%02X:%02X:%02X:%02X:"
737                                        "%02X:%02X:%02X:%02X", 
738                                        a[0], a[1], a[2], a[3],
739                                        a[4], a[5], a[6], a[7],
740                                        a[8], a[9], a[10], a[11],
741                                        a[12], a[13], a[14], a[15]);
742         else
743             strpool = rk_strpoolprintf(strpool, 
744                                        "unknown IP address of length %lu",
745                                        (unsigned long)name->u.iPAddress.length);
746         break;
747     }
748     case choice_GeneralName_registeredID: {
749         char *str;
750         hx509_oid_sprint(&name->u.registeredID, &str);
751         if (str == NULL)
752             return ENOMEM;
753         strpool = rk_strpoolprintf(strpool, "registeredID: %s", str);
754         free(str);
755         break;
756     }
757     default:
758         return EINVAL;
759     }
760     if (strpool == NULL)
761         return ENOMEM;
762
763     *str = rk_strpoolcollect(strpool);
764
765     return 0;
766 }