Line data Source code
1 : /*
2 : * Copyright (c) 2004 - 2009 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * 3. Neither the name of the Institute nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "hx_locl.h"
35 : #include <wind.h>
36 : #include "char_map.h"
37 :
38 : /**
39 : * @page page_name PKIX/X.509 Names
40 : *
41 : * There are several names in PKIX/X.509, GeneralName and Name.
42 : *
43 : * A Name consists of an ordered list of Relative Distinguished Names
44 : * (RDN). Each RDN consists of an unordered list of typed strings. The
45 : * types are defined by OID and have long and short description. For
46 : * example id-at-commonName (2.5.4.3) have the long name CommonName
47 : * and short name CN. The string itself can be of several encoding,
48 : * UTF8, UTF16, Teltex string, etc. The type limit what encoding
49 : * should be used.
50 : *
51 : * GeneralName is a broader nametype that can contains al kind of
52 : * stuff like Name, IP addresses, partial Name, etc.
53 : *
54 : * Name is mapped into a hx509_name object.
55 : *
56 : * Parse and string name into a hx509_name object with hx509_parse_name(),
57 : * make it back into string representation with hx509_name_to_string().
58 : *
59 : * Name string are defined rfc2253, rfc1779 and X.501.
60 : *
61 : * See the library functions here: @ref hx509_name
62 : */
63 :
64 : static const struct {
65 : const char *n;
66 : const heim_oid *o;
67 : int type_choice; /* Preference for DirectoryString choice; 0 -> no pref */
68 : wind_profile_flags flags;
69 : /*
70 : * RFC52380 imposes maximum lengths for some strings in Names. These are
71 : * ASN.1 size limits. We should implement these in our copy of the PKIX
72 : * ASN.1 module. For now we treat them as maximum byte counts rather than
73 : * maximum character counts, and we encode and enforce them here.
74 : *
75 : * 0 -> no max
76 : *
77 : * Some of these attributes aren't of type DirectoryString, so our
78 : * type_choice isn't really correct. We're not really set up for
79 : * attributes whose types aren't DirectoryString or one of its choice arms'
80 : * type, much less are we set up for non-string attribute value types.
81 : */
82 : size_t max_bytes;
83 : } no[] = {
84 : { "C", &asn1_oid_id_at_countryName,
85 : choice_DirectoryString_printableString, 0, 2 },
86 : { "CN", &asn1_oid_id_at_commonName, 0, 0, ub_common_name },
87 : { "DC", &asn1_oid_id_domainComponent, choice_DirectoryString_ia5String,
88 : 0, 63 }, /* DNS label */
89 : { "L", &asn1_oid_id_at_localityName, 0, 0, ub_locality_name },
90 : { "O", &asn1_oid_id_at_organizationName, 0, 0, ub_organization_name },
91 : { "OU", &asn1_oid_id_at_organizationalUnitName, 0, 0,
92 : ub_organizational_unit_name },
93 : { "S", &asn1_oid_id_at_stateOrProvinceName, 0, 0, ub_state_name },
94 : { "STREET", &asn1_oid_id_at_streetAddress, 0, 0, 0 }, /* ENOTSUP */
95 : { "UID", &asn1_oid_id_Userid, 0, 0, ub_numeric_user_id_length },
96 : { "emailAddress", &asn1_oid_id_pkcs9_emailAddress,
97 : choice_DirectoryString_ia5String, 0, ub_emailaddress_length },
98 : /* This is for DevID certificates and maybe others */
99 : { "serialNumber", &asn1_oid_id_at_serialNumber, 0, 0, ub_serial_number },
100 : /* These are for TPM 2.0 Endorsement Key Certificates (EKCerts) */
101 : { "TPMManufacturer", &asn1_oid_tcg_at_tpmManufacturer, 0, 0,
102 : ub_emailaddress_length },
103 : { "TPMModel", &asn1_oid_tcg_at_tpmModel, 0, 0, ub_emailaddress_length },
104 : { "TPMVersion", &asn1_oid_tcg_at_tpmVersion, 0, 0, ub_emailaddress_length },
105 : };
106 :
107 : static char *
108 640 : quote_string(const char *f, size_t len, int flags, size_t *rlen)
109 : {
110 0 : size_t i, j, tolen;
111 640 : const unsigned char *from = (const unsigned char *)f;
112 0 : unsigned char *to;
113 :
114 640 : tolen = len * 3 + 1;
115 640 : to = malloc(tolen);
116 640 : if (to == NULL)
117 0 : return NULL;
118 :
119 12701 : for (i = 0, j = 0; i < len; i++) {
120 12061 : unsigned char map = char_map[from[i]] & flags;
121 12061 : if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) {
122 0 : to[j++] = '\\';
123 0 : to[j++] = from[i];
124 12061 : } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) {
125 :
126 0 : to[j++] = '\\';
127 0 : to[j++] = from[i];
128 12061 : } else if (map & Q_RFC2253_QUOTE) {
129 32 : to[j++] = '\\';
130 32 : to[j++] = from[i];
131 12029 : } else if (map & Q_RFC2253_HEX) {
132 0 : int l = snprintf((char *)&to[j], tolen - j - 1,
133 0 : "#%02x", (unsigned char)from[i]);
134 0 : j += l;
135 : } else {
136 12029 : to[j++] = from[i];
137 : }
138 : }
139 640 : to[j] = '\0';
140 640 : assert(j < tolen);
141 640 : *rlen = j;
142 640 : return (char *)to;
143 : }
144 :
145 :
146 : static int
147 2448 : append_string(char **str, size_t *total_len, const char *ss,
148 : size_t len, int quote)
149 : {
150 0 : char *s, *qs;
151 :
152 2448 : if (quote)
153 640 : qs = quote_string(ss, len, Q_RFC2253, &len);
154 : else
155 1808 : qs = rk_UNCONST(ss);
156 :
157 2448 : s = realloc(*str, len + *total_len + 1);
158 2448 : if (s == NULL)
159 0 : _hx509_abort("allocation failure"); /* XXX */
160 2448 : memcpy(s + *total_len, qs, len);
161 2448 : if (qs != ss)
162 640 : free(qs);
163 2448 : s[*total_len + len] = '\0';
164 2448 : *str = s;
165 2448 : *total_len += len;
166 2448 : return 0;
167 : }
168 :
169 : static char *
170 640 : oidtostring(const heim_oid *type, int *type_choice)
171 : {
172 0 : char *s;
173 0 : size_t i;
174 :
175 640 : if (type_choice)
176 0 : *type_choice = choice_DirectoryString_utf8String;
177 :
178 3152 : for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
179 3152 : if (der_heim_oid_cmp(no[i].o, type) == 0) {
180 640 : if (type_choice && no[i].type_choice)
181 0 : *type_choice = no[i].type_choice;
182 640 : return strdup(no[i].n);
183 : }
184 : }
185 0 : if (der_print_heim_oid(type, '.', &s) != 0)
186 0 : return NULL;
187 0 : return s;
188 : }
189 :
190 : static size_t
191 0 : oidtomaxlen(const heim_oid *type)
192 : {
193 0 : size_t i;
194 :
195 0 : for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
196 0 : if (der_heim_oid_cmp(no[i].o, type) == 0)
197 0 : return no[i].max_bytes;
198 : }
199 0 : return 0;
200 : }
201 :
202 : static int
203 0 : stringtooid(const char *name, size_t len, heim_oid *oid)
204 : {
205 0 : int ret;
206 0 : size_t i;
207 0 : char *s;
208 :
209 0 : memset(oid, 0, sizeof(*oid));
210 :
211 0 : for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
212 0 : if (strncasecmp(no[i].n, name, len) == 0)
213 0 : return der_copy_oid(no[i].o, oid);
214 : }
215 0 : s = malloc(len + 1);
216 0 : if (s == NULL)
217 0 : return ENOMEM;
218 0 : memcpy(s, name, len);
219 0 : s[len] = '\0';
220 0 : ret = der_parse_heim_oid(s, ".", oid);
221 0 : free(s);
222 0 : return ret;
223 : }
224 :
225 : /**
226 : * Convert the hx509 name object into a printable string.
227 : * The resulting string should be freed with free().
228 : *
229 : * @param name name to print
230 : * @param str the string to return
231 : *
232 : * @return An hx509 error code, see hx509_get_error_string().
233 : *
234 : * @ingroup hx509_name
235 : */
236 :
237 : HX509_LIB_FUNCTION int HX509_LIB_CALL
238 112 : hx509_name_to_string(const hx509_name name, char **str)
239 : {
240 112 : return _hx509_Name_to_string(&name->der_name, str);
241 : }
242 :
243 : HX509_LIB_FUNCTION int HX509_LIB_CALL
244 112 : _hx509_Name_to_string(const Name *n, char **str)
245 : {
246 112 : size_t total_len = 0;
247 0 : size_t i, j, m;
248 0 : int ret;
249 :
250 112 : *str = strdup("");
251 112 : if (*str == NULL)
252 0 : return ENOMEM;
253 :
254 752 : for (m = n->u.rdnSequence.len; m > 0; m--) {
255 0 : size_t len;
256 640 : i = m - 1;
257 :
258 1280 : for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
259 640 : DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
260 0 : char *oidname;
261 0 : char *ss;
262 :
263 640 : oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type, NULL);
264 :
265 640 : switch(ds->element) {
266 80 : case choice_DirectoryString_ia5String:
267 80 : ss = ds->u.ia5String.data;
268 80 : len = ds->u.ia5String.length;
269 80 : break;
270 112 : case choice_DirectoryString_printableString:
271 112 : ss = ds->u.printableString.data;
272 112 : len = ds->u.printableString.length;
273 112 : break;
274 448 : case choice_DirectoryString_utf8String:
275 448 : ss = ds->u.utf8String;
276 448 : len = strlen(ss);
277 448 : break;
278 0 : case choice_DirectoryString_bmpString: {
279 0 : const uint16_t *bmp = ds->u.bmpString.data;
280 0 : size_t bmplen = ds->u.bmpString.length;
281 0 : size_t k;
282 :
283 0 : ret = wind_ucs2utf8_length(bmp, bmplen, &k);
284 0 : if (ret) {
285 0 : free(oidname);
286 0 : free(*str);
287 0 : *str = NULL;
288 0 : return ret;
289 : }
290 :
291 0 : ss = malloc(k + 1);
292 0 : if (ss == NULL)
293 0 : _hx509_abort("allocation failure"); /* XXX */
294 0 : ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
295 0 : if (ret) {
296 0 : free(oidname);
297 0 : free(ss);
298 0 : free(*str);
299 0 : *str = NULL;
300 0 : return ret;
301 : }
302 0 : ss[k] = '\0';
303 0 : len = k;
304 0 : break;
305 : }
306 0 : case choice_DirectoryString_teletexString:
307 0 : ss = ds->u.teletexString;
308 0 : len = strlen(ss);
309 0 : break;
310 0 : case choice_DirectoryString_universalString: {
311 0 : const uint32_t *uni = ds->u.universalString.data;
312 0 : size_t unilen = ds->u.universalString.length;
313 0 : size_t k;
314 :
315 0 : ret = wind_ucs4utf8_length(uni, unilen, &k);
316 0 : if (ret) {
317 0 : free(oidname);
318 0 : free(*str);
319 0 : *str = NULL;
320 0 : return ret;
321 : }
322 :
323 0 : ss = malloc(k + 1);
324 0 : if (ss == NULL)
325 0 : _hx509_abort("allocation failure"); /* XXX */
326 0 : ret = wind_ucs4utf8(uni, unilen, ss, NULL);
327 0 : if (ret) {
328 0 : free(ss);
329 0 : free(oidname);
330 0 : free(*str);
331 0 : *str = NULL;
332 0 : return ret;
333 : }
334 0 : ss[k] = '\0';
335 0 : len = k;
336 0 : break;
337 : }
338 0 : default:
339 0 : _hx509_abort("unknown directory type: %d", ds->element);
340 : exit(1);
341 : }
342 640 : append_string(str, &total_len, oidname, strlen(oidname), 0);
343 640 : free(oidname);
344 640 : append_string(str, &total_len, "=", 1, 0);
345 640 : append_string(str, &total_len, ss, len, 1);
346 640 : if (ds->element == choice_DirectoryString_bmpString ||
347 640 : ds->element == choice_DirectoryString_universalString)
348 : {
349 0 : free(ss);
350 : }
351 640 : if (j + 1 < n->u.rdnSequence.val[i].len)
352 0 : append_string(str, &total_len, "+", 1, 0);
353 : }
354 :
355 640 : if (i > 0)
356 528 : append_string(str, &total_len, ",", 1, 0);
357 : }
358 112 : return 0;
359 : }
360 :
361 : #define COPYCHARARRAY(_ds,_el,_l,_n) \
362 : (_l) = strlen(_ds->u._el); \
363 : (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
364 : if ((_n) == NULL) \
365 : return ENOMEM; \
366 : for (i = 0; i < (_l); i++) \
367 : (_n)[i] = _ds->u._el[i]
368 :
369 :
370 : #define COPYVALARRAY(_ds,_el,_l,_n) \
371 : (_l) = _ds->u._el.length; \
372 : (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
373 : if ((_n) == NULL) \
374 : return ENOMEM; \
375 : for (i = 0; i < (_l); i++) \
376 : (_n)[i] = _ds->u._el.data[i]
377 :
378 : #define COPYVOIDARRAY(_ds,_el,_l,_n) \
379 : (_l) = _ds->u._el.length; \
380 : (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
381 : if ((_n) == NULL) \
382 : return ENOMEM; \
383 : for (i = 0; i < (_l); i++) \
384 : (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
385 :
386 :
387 :
388 : static int
389 3934 : dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
390 : {
391 0 : wind_profile_flags flags;
392 0 : size_t i, len;
393 3934 : int ret = 0;
394 0 : uint32_t *name;
395 :
396 3934 : *rname = NULL;
397 3934 : *rlen = 0;
398 :
399 3934 : switch(ds->element) {
400 562 : case choice_DirectoryString_ia5String:
401 562 : flags = WIND_PROFILE_LDAP;
402 21918 : COPYVOIDARRAY(ds, ia5String, len, name);
403 562 : break;
404 562 : case choice_DirectoryString_printableString:
405 562 : flags = WIND_PROFILE_LDAP;
406 562 : flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
407 1686 : COPYVOIDARRAY(ds, printableString, len, name);
408 562 : break;
409 0 : case choice_DirectoryString_teletexString:
410 0 : flags = WIND_PROFILE_LDAP_CASE;
411 0 : COPYCHARARRAY(ds, teletexString, len, name);
412 0 : break;
413 0 : case choice_DirectoryString_bmpString:
414 0 : flags = WIND_PROFILE_LDAP;
415 0 : COPYVALARRAY(ds, bmpString, len, name);
416 0 : break;
417 0 : case choice_DirectoryString_universalString:
418 0 : flags = WIND_PROFILE_LDAP;
419 0 : COPYVALARRAY(ds, universalString, len, name);
420 0 : break;
421 2810 : case choice_DirectoryString_utf8String:
422 2810 : flags = WIND_PROFILE_LDAP;
423 2810 : ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
424 2810 : if (ret)
425 0 : return ret;
426 2810 : name = malloc((len + 1) * sizeof(name[0]));
427 2810 : if (name == NULL)
428 0 : return ENOMEM;
429 2810 : ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
430 2810 : if (ret) {
431 0 : free(name);
432 0 : return ret;
433 : }
434 2810 : break;
435 0 : default:
436 0 : _hx509_abort("unknown directory type: %d", ds->element);
437 : }
438 :
439 3934 : *rlen = len;
440 : /* try a couple of times to get the length right, XXX gross */
441 3934 : for (i = 0; i < 4; i++) {
442 3934 : *rlen = *rlen * 2;
443 3934 : if ((*rname = malloc((rlen[0] + 1) * sizeof((*rname)[0]))) == NULL) {
444 0 : ret = ENOMEM;
445 0 : break;
446 : }
447 :
448 3934 : ret = wind_stringprep(name, len, *rname, rlen, flags);
449 3934 : if (ret == WIND_ERR_OVERRUN) {
450 0 : free(*rname);
451 0 : *rname = NULL;
452 0 : continue;
453 : } else
454 3934 : break;
455 : }
456 3934 : free(name);
457 3934 : if (ret) {
458 0 : if (*rname)
459 0 : free(*rname);
460 0 : *rname = NULL;
461 0 : *rlen = 0;
462 0 : return ret;
463 : }
464 :
465 3934 : return 0;
466 : }
467 :
468 : HX509_LIB_FUNCTION int HX509_LIB_CALL
469 1967 : _hx509_name_ds_cmp(const DirectoryString *ds1,
470 : const DirectoryString *ds2,
471 : int *diff)
472 : {
473 0 : uint32_t *ds1lp, *ds2lp;
474 0 : size_t ds1len, ds2len, i;
475 0 : int ret;
476 :
477 1967 : ret = dsstringprep(ds1, &ds1lp, &ds1len);
478 1967 : if (ret)
479 0 : return ret;
480 1967 : ret = dsstringprep(ds2, &ds2lp, &ds2len);
481 1967 : if (ret) {
482 0 : free(ds1lp);
483 0 : return ret;
484 : }
485 :
486 1967 : if (ds1len != ds2len)
487 0 : *diff = ds1len - ds2len;
488 : else {
489 34844 : for (i = 0; i < ds1len; i++) {
490 32877 : *diff = ds1lp[i] - ds2lp[i];
491 32877 : if (*diff)
492 0 : break;
493 : }
494 : }
495 1967 : free(ds1lp);
496 1967 : free(ds2lp);
497 :
498 1967 : return 0;
499 : }
500 :
501 : HX509_LIB_FUNCTION int HX509_LIB_CALL
502 449 : _hx509_name_cmp(const Name *n1, const Name *n2, int *c)
503 : {
504 0 : int ret;
505 0 : size_t i, j;
506 :
507 449 : *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
508 449 : if (*c)
509 168 : return 0;
510 :
511 2248 : for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
512 1967 : *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
513 1967 : if (*c)
514 0 : return 0;
515 :
516 3934 : for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
517 3934 : *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
518 1967 : &n1->u.rdnSequence.val[i].val[j].type);
519 1967 : if (*c)
520 0 : return 0;
521 :
522 1967 : ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
523 1967 : &n2->u.rdnSequence.val[i].val[j].value,
524 : c);
525 1967 : if (ret)
526 0 : return ret;
527 1967 : if (*c)
528 0 : return 0;
529 : }
530 : }
531 281 : *c = 0;
532 281 : return 0;
533 : }
534 :
535 : /**
536 : * Compare to hx509 name object, useful for sorting.
537 : *
538 : * @param n1 a hx509 name object.
539 : * @param n2 a hx509 name object.
540 : *
541 : * @return 0 the objects are the same, returns > 0 is n2 is "larger"
542 : * then n2, < 0 if n1 is "smaller" then n2.
543 : *
544 : * @ingroup hx509_name
545 : */
546 :
547 : HX509_LIB_FUNCTION int HX509_LIB_CALL
548 0 : hx509_name_cmp(hx509_name n1, hx509_name n2)
549 : {
550 0 : int ret, diff;
551 0 : ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
552 0 : if (ret)
553 0 : return ret;
554 0 : return diff;
555 : }
556 :
557 :
558 : HX509_LIB_FUNCTION int HX509_LIB_CALL
559 354 : _hx509_name_from_Name(const Name *n, hx509_name *name)
560 : {
561 0 : int ret;
562 354 : *name = calloc(1, sizeof(**name));
563 354 : if (*name == NULL)
564 0 : return ENOMEM;
565 354 : ret = copy_Name(n, &(*name)->der_name);
566 354 : if (ret) {
567 0 : free(*name);
568 0 : *name = NULL;
569 : }
570 354 : return ret;
571 : }
572 :
573 : HX509_LIB_FUNCTION int HX509_LIB_CALL
574 0 : _hx509_name_modify(hx509_context context,
575 : Name *name,
576 : int append,
577 : const heim_oid *oid,
578 : const char *str)
579 : {
580 0 : RelativeDistinguishedName rdn;
581 0 : size_t max_len = oidtomaxlen(oid);
582 0 : char *s = NULL;
583 0 : int type_choice = choice_DirectoryString_printableString;
584 0 : int ret;
585 :
586 : /*
587 : * Check string length upper bounds.
588 : *
589 : * Because we don't have these bounds in our copy of the PKIX ASN.1 module,
590 : * and because we might like to catch these early anyways, we enforce them
591 : * here.
592 : */
593 0 : if (max_len && strlen(str) > max_len) {
594 0 : char *a = oidtostring(oid, &type_choice);
595 :
596 0 : ret = HX509_PARSING_NAME_FAILED;
597 0 : hx509_set_error_string(context, 0, ret, "RDN attribute %s value too "
598 : "long (max %llu): %s", a ? a : "<unknown>",
599 : max_len, str);
600 0 : free(a);
601 0 : return ret;
602 : }
603 :
604 0 : memset(&rdn, 0, sizeof(rdn));
605 0 : if ((rdn.val = malloc(sizeof(rdn.val[0]))) == NULL) {
606 0 : hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
607 0 : return ENOMEM;
608 : }
609 0 : rdn.len = 1;
610 :
611 : /*
612 : * How best to pick a type for this attribute value?
613 : *
614 : * Options:
615 : *
616 : * 1) the API deals only in UTF-8, let the callers convert to/from UTF-8
617 : * and whatever the current locale wants
618 : *
619 : * 2) use the best type for the codeset of the current locale.
620 : *
621 : * We choose (1).
622 : *
623 : * However, for some cases we really should prefer other types when the
624 : * input string is all printable ASCII.
625 : */
626 0 : rdn.val[0].value.element = type_choice;
627 0 : if ((s = strdup(str)) == NULL ||
628 0 : der_copy_oid(oid, &rdn.val[0].type)) {
629 0 : free(rdn.val);
630 0 : free(s);
631 0 : return hx509_enomem(context);
632 : }
633 0 : switch (rdn.val[0].value.element) {
634 : /* C strings: */
635 0 : case choice_DirectoryString_utf8String:
636 0 : rdn.val[0].value.u.utf8String = s;
637 0 : break;
638 0 : case choice_DirectoryString_teletexString:
639 0 : rdn.val[0].value.u.teletexString = s;
640 0 : break;
641 :
642 : /* Length and pointer */
643 0 : case choice_DirectoryString_ia5String:
644 0 : rdn.val[0].value.u.ia5String.data = s;
645 0 : rdn.val[0].value.u.ia5String.length = strlen(s);
646 0 : break;
647 0 : case choice_DirectoryString_printableString:
648 0 : rdn.val[0].value.u.printableString.data = s;
649 0 : rdn.val[0].value.u.printableString.length = strlen(s);
650 0 : break;
651 0 : case choice_DirectoryString_universalString:
652 0 : free(s);
653 0 : free(rdn.val);
654 0 : hx509_set_error_string(context, 0, ENOTSUP, "UniversalString not supported");
655 0 : return ENOTSUP;
656 0 : case choice_DirectoryString_bmpString:
657 0 : free(s);
658 0 : free(rdn.val);
659 0 : hx509_set_error_string(context, 0, ENOTSUP, "BMPString not supported");
660 0 : return ENOTSUP;
661 0 : default:
662 0 : free(s);
663 0 : free(rdn.val);
664 0 : hx509_set_error_string(context, 0, ENOTSUP,
665 : "Internal error; unknown DirectoryString choice");
666 0 : return ENOTSUP;
667 : }
668 :
669 : /* Append RDN. If the caller wanted to prepend instead, we'll rotate. */
670 0 : ret = add_RDNSequence(&name->u.rdnSequence, &rdn);
671 0 : free_RelativeDistinguishedName(&rdn);
672 :
673 0 : if (ret || append || name->u.rdnSequence.len < 2)
674 0 : return ret;
675 :
676 : /* Rotate */
677 0 : rdn = name->u.rdnSequence.val[name->u.rdnSequence.len - 1];
678 0 : memmove(&name->u.rdnSequence.val[1],
679 0 : &name->u.rdnSequence.val[0],
680 0 : (name->u.rdnSequence.len - 1) *
681 : sizeof(name->u.rdnSequence.val[0]));
682 0 : name->u.rdnSequence.val[0] = rdn;
683 0 : return 0;
684 : }
685 :
686 : HX509_LIB_FUNCTION int HX509_LIB_CALL
687 0 : hx509_empty_name(hx509_context context, hx509_name *name)
688 : {
689 0 : if ((*name = calloc(1, sizeof(**name))) == NULL) {
690 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
691 0 : return ENOMEM;
692 : }
693 0 : (*name)->der_name.element = choice_Name_rdnSequence;
694 0 : (*name)->der_name.u.rdnSequence.val = 0;
695 0 : (*name)->der_name.u.rdnSequence.len = 0;
696 0 : return 0;
697 : }
698 :
699 : /**
700 : * Parse a string into a hx509 name object.
701 : *
702 : * @param context A hx509 context.
703 : * @param str a string to parse.
704 : * @param name the resulting object, NULL in case of error.
705 : *
706 : * @return An hx509 error code, see hx509_get_error_string().
707 : *
708 : * @ingroup hx509_name
709 : */
710 :
711 : HX509_LIB_FUNCTION int HX509_LIB_CALL
712 0 : hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
713 : {
714 0 : const char *p, *q;
715 0 : size_t len;
716 0 : hx509_name n;
717 0 : int ret;
718 :
719 0 : *name = NULL;
720 :
721 0 : n = calloc(1, sizeof(*n));
722 0 : if (n == NULL) {
723 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
724 0 : return ENOMEM;
725 : }
726 :
727 0 : n->der_name.element = choice_Name_rdnSequence;
728 :
729 0 : p = str;
730 :
731 0 : while (p != NULL && *p != '\0') {
732 0 : heim_oid oid;
733 0 : int last;
734 :
735 0 : q = strchr(p, ',');
736 0 : if (q) {
737 0 : len = (q - p);
738 0 : last = 1;
739 : } else {
740 0 : len = strlen(p);
741 0 : last = 0;
742 : }
743 :
744 0 : q = strchr(p, '=');
745 0 : if (q == NULL) {
746 0 : ret = HX509_PARSING_NAME_FAILED;
747 0 : hx509_set_error_string(context, 0, ret, "missing = in %s", p);
748 0 : goto out;
749 : }
750 0 : if (q == p) {
751 0 : ret = HX509_PARSING_NAME_FAILED;
752 0 : hx509_set_error_string(context, 0, ret,
753 : "missing name before = in %s", p);
754 0 : goto out;
755 : }
756 :
757 0 : if ((size_t)(q - p) > len) {
758 0 : ret = HX509_PARSING_NAME_FAILED;
759 0 : hx509_set_error_string(context, 0, ret, " = after , in %s", p);
760 0 : goto out;
761 : }
762 :
763 0 : ret = stringtooid(p, q - p, &oid);
764 0 : if (ret) {
765 0 : ret = HX509_PARSING_NAME_FAILED;
766 0 : hx509_set_error_string(context, 0, ret,
767 0 : "unknown type: %.*s", (int)(q - p), p);
768 0 : goto out;
769 : }
770 :
771 : {
772 0 : size_t pstr_len = len - (q - p) - 1;
773 0 : const char *pstr = p + (q - p) + 1;
774 0 : char *r;
775 :
776 0 : r = malloc(pstr_len + 1);
777 0 : if (r == NULL) {
778 0 : der_free_oid(&oid);
779 0 : ret = ENOMEM;
780 0 : hx509_set_error_string(context, 0, ret, "out of memory");
781 0 : goto out;
782 : }
783 0 : memcpy(r, pstr, pstr_len);
784 0 : r[pstr_len] = '\0';
785 :
786 0 : ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
787 0 : free(r);
788 0 : der_free_oid(&oid);
789 0 : if(ret)
790 0 : goto out;
791 : }
792 0 : p += len + last;
793 : }
794 :
795 0 : *name = n;
796 :
797 0 : return 0;
798 0 : out:
799 0 : hx509_name_free(&n);
800 0 : return HX509_NAME_MALFORMED;
801 : }
802 :
803 : /**
804 : * Copy a hx509 name object.
805 : *
806 : * @param context A hx509 cotext.
807 : * @param from the name to copy from
808 : * @param to the name to copy to
809 : *
810 : * @return An hx509 error code, see hx509_get_error_string().
811 : *
812 : * @ingroup hx509_name
813 : */
814 :
815 : HX509_LIB_FUNCTION int HX509_LIB_CALL
816 0 : hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
817 : {
818 0 : int ret;
819 :
820 0 : *to = calloc(1, sizeof(**to));
821 0 : if (*to == NULL)
822 0 : return ENOMEM;
823 0 : ret = copy_Name(&from->der_name, &(*to)->der_name);
824 0 : if (ret) {
825 0 : free(*to);
826 0 : *to = NULL;
827 0 : return ENOMEM;
828 : }
829 0 : return 0;
830 : }
831 :
832 : /**
833 : * Convert a hx509_name into a Name.
834 : *
835 : * @param from the name to copy from
836 : * @param to the name to copy to
837 : *
838 : * @return An hx509 error code, see hx509_get_error_string().
839 : *
840 : * @ingroup hx509_name
841 : */
842 :
843 : HX509_LIB_FUNCTION int HX509_LIB_CALL
844 121 : hx509_name_to_Name(const hx509_name from, Name *to)
845 : {
846 121 : return copy_Name(&from->der_name, to);
847 : }
848 :
849 : HX509_LIB_FUNCTION int HX509_LIB_CALL
850 0 : hx509_name_normalize(hx509_context context, hx509_name name)
851 : {
852 0 : return 0;
853 : }
854 :
855 : /**
856 : * Expands variables in the name using env. Variables are on the form
857 : * ${name}. Useful when dealing with certificate templates.
858 : *
859 : * @param context A hx509 cotext.
860 : * @param name the name to expand.
861 : * @param env environment variable to expand.
862 : *
863 : * @return An hx509 error code, see hx509_get_error_string().
864 : *
865 : * @ingroup hx509_name
866 : */
867 :
868 : HX509_LIB_FUNCTION int HX509_LIB_CALL
869 0 : hx509_name_expand(hx509_context context,
870 : hx509_name name,
871 : hx509_env env)
872 : {
873 0 : Name *n = &name->der_name;
874 0 : size_t i, j;
875 0 : int bounds_check = 1;
876 :
877 0 : if (env == NULL)
878 0 : return 0;
879 :
880 0 : if (n->element != choice_Name_rdnSequence) {
881 0 : hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
882 0 : return EINVAL;
883 : }
884 :
885 0 : for (i = 0 ; i < n->u.rdnSequence.len; i++) {
886 0 : for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
887 : /** Only UTF8String rdnSequence names are allowed */
888 : /*
889 : THIS SHOULD REALLY BE:
890 : COMP = n->u.rdnSequence.val[i].val[j];
891 : normalize COMP to utf8
892 : check if there are variables
893 : expand variables
894 : convert back to orignal format, store in COMP
895 : free normalized utf8 string
896 : */
897 0 : DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
898 0 : heim_oid *type = &n->u.rdnSequence.val[i].val[j].type;
899 0 : const char *sval = NULL;
900 0 : char *p, *p2;
901 0 : char *s = NULL;
902 0 : struct rk_strpool *strpool = NULL;
903 :
904 0 : switch (ds->element) {
905 0 : case choice_DirectoryString_utf8String:
906 0 : sval = ds->u.utf8String;
907 0 : break;
908 0 : case choice_DirectoryString_teletexString:
909 0 : sval = ds->u.utf8String;
910 0 : break;
911 0 : case choice_DirectoryString_ia5String:
912 0 : s = strndup(ds->u.ia5String.data,
913 : ds->u.ia5String.length);
914 0 : break;
915 0 : case choice_DirectoryString_printableString:
916 0 : s = strndup(ds->u.printableString.data,
917 : ds->u.printableString.length);
918 0 : break;
919 0 : case choice_DirectoryString_universalString:
920 0 : hx509_set_error_string(context, 0, ENOTSUP, "UniversalString not supported");
921 0 : return ENOTSUP;
922 0 : case choice_DirectoryString_bmpString:
923 0 : hx509_set_error_string(context, 0, ENOTSUP, "BMPString not supported");
924 0 : return ENOTSUP;
925 : }
926 0 : if (sval == NULL && s == NULL)
927 0 : return hx509_enomem(context);
928 0 : if (s)
929 0 : sval = s;
930 :
931 0 : p = strstr(sval, "${");
932 0 : if (p) {
933 0 : strpool = rk_strpoolprintf(strpool, "%.*s", (int)(p - sval), sval);
934 0 : if (strpool == NULL) {
935 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
936 0 : free(s);
937 0 : return ENOMEM;
938 : }
939 : }
940 :
941 0 : while (p != NULL) {
942 : /* expand variables */
943 0 : const char *value;
944 0 : p2 = strchr(p, '}');
945 0 : if (p2 == NULL) {
946 0 : hx509_set_error_string(context, 0, EINVAL, "missing }");
947 0 : rk_strpoolfree(strpool);
948 0 : free(s);
949 0 : return EINVAL;
950 : }
951 0 : p += 2;
952 0 : value = hx509_env_lfind(context, env, p, p2 - p);
953 0 : if (value == NULL) {
954 0 : hx509_set_error_string(context, 0, EINVAL,
955 : "variable %.*s missing",
956 0 : (int)(p2 - p), p);
957 0 : rk_strpoolfree(strpool);
958 0 : free(s);
959 0 : return EINVAL;
960 : }
961 0 : strpool = rk_strpoolprintf(strpool, "%s", value);
962 0 : if (strpool == NULL) {
963 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
964 0 : free(s);
965 0 : return ENOMEM;
966 : }
967 0 : p2++;
968 :
969 0 : p = strstr(p2, "${");
970 0 : if (p)
971 0 : strpool = rk_strpoolprintf(strpool, "%.*s",
972 0 : (int)(p - p2), p2);
973 : else
974 0 : strpool = rk_strpoolprintf(strpool, "%s", p2);
975 0 : if (strpool == NULL) {
976 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
977 0 : free(s);
978 0 : return ENOMEM;
979 : }
980 : }
981 :
982 0 : free(s);
983 0 : s = NULL;
984 :
985 0 : if (strpool) {
986 0 : size_t max_bytes;
987 :
988 0 : if ((s = rk_strpoolcollect(strpool)) == NULL) {
989 0 : hx509_set_error_string(context, 0, ENOMEM, "out of memory");
990 0 : return ENOMEM;
991 : }
992 :
993 : /* Check upper bounds! */
994 0 : if ((max_bytes = oidtomaxlen(type)) && strlen(s) > max_bytes)
995 0 : bounds_check = 0;
996 :
997 0 : switch (ds->element) {
998 : /* C strings: */
999 0 : case choice_DirectoryString_utf8String:
1000 0 : free(ds->u.utf8String);
1001 0 : ds->u.utf8String = s;
1002 0 : break;
1003 0 : case choice_DirectoryString_teletexString:
1004 0 : free(ds->u.teletexString);
1005 0 : ds->u.teletexString = s;
1006 0 : break;
1007 :
1008 : /* Length and pointer */
1009 0 : case choice_DirectoryString_ia5String:
1010 0 : free(ds->u.ia5String.data);
1011 0 : ds->u.ia5String.data = s;
1012 0 : ds->u.ia5String.length = strlen(s);
1013 0 : break;
1014 0 : case choice_DirectoryString_printableString:
1015 0 : free(ds->u.printableString.data);
1016 0 : ds->u.printableString.data = s;
1017 0 : ds->u.printableString.length = strlen(s);
1018 0 : break;
1019 0 : default:
1020 0 : break; /* Handled above */
1021 : }
1022 : }
1023 : }
1024 : }
1025 :
1026 0 : if (!bounds_check) {
1027 0 : hx509_set_error_string(context, 0, HX509_PARSING_NAME_FAILED,
1028 : "some expanded RDNs are too long");
1029 0 : return HX509_PARSING_NAME_FAILED;
1030 : }
1031 0 : return 0;
1032 : }
1033 :
1034 : /**
1035 : * Free a hx509 name object, upond return *name will be NULL.
1036 : *
1037 : * @param name a hx509 name object to be freed.
1038 : *
1039 : * @ingroup hx509_name
1040 : */
1041 :
1042 : HX509_LIB_FUNCTION void HX509_LIB_CALL
1043 354 : hx509_name_free(hx509_name *name)
1044 : {
1045 354 : free_Name(&(*name)->der_name);
1046 354 : memset(*name, 0, sizeof(**name));
1047 354 : free(*name);
1048 354 : *name = NULL;
1049 354 : }
1050 :
1051 : /**
1052 : * Convert a DER encoded name info a string.
1053 : *
1054 : * @param data data to a DER/BER encoded name
1055 : * @param length length of data
1056 : * @param str the resulting string, is NULL on failure.
1057 : *
1058 : * @return An hx509 error code, see hx509_get_error_string().
1059 : *
1060 : * @ingroup hx509_name
1061 : */
1062 :
1063 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1064 0 : hx509_unparse_der_name(const void *data, size_t length, char **str)
1065 : {
1066 0 : Name name;
1067 0 : int ret;
1068 :
1069 0 : *str = NULL;
1070 :
1071 0 : ret = decode_Name(data, length, &name, NULL);
1072 0 : if (ret)
1073 0 : return ret;
1074 0 : ret = _hx509_Name_to_string(&name, str);
1075 0 : free_Name(&name);
1076 0 : return ret;
1077 : }
1078 :
1079 : /**
1080 : * Convert a hx509_name object to DER encoded name.
1081 : *
1082 : * @param name name to concert
1083 : * @param os data to a DER encoded name, free the resulting octet
1084 : * string with hx509_xfree(os->data).
1085 : *
1086 : * @return An hx509 error code, see hx509_get_error_string().
1087 : *
1088 : * @ingroup hx509_name
1089 : */
1090 :
1091 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1092 0 : hx509_name_binary(const hx509_name name, heim_octet_string *os)
1093 : {
1094 0 : size_t size;
1095 0 : int ret;
1096 :
1097 0 : ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
1098 0 : if (ret)
1099 0 : return ret;
1100 0 : if (os->length != size)
1101 0 : _hx509_abort("internal ASN.1 encoder error");
1102 :
1103 0 : return 0;
1104 : }
1105 :
1106 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1107 0 : _hx509_unparse_Name(const Name *aname, char **str)
1108 : {
1109 0 : hx509_name name;
1110 0 : int ret;
1111 :
1112 0 : ret = _hx509_name_from_Name(aname, &name);
1113 0 : if (ret)
1114 0 : return ret;
1115 :
1116 0 : ret = hx509_name_to_string(name, str);
1117 0 : hx509_name_free(&name);
1118 0 : return ret;
1119 : }
1120 :
1121 : /**
1122 : * Check if a name is empty.
1123 : *
1124 : * @param name the name to check if its empty/null.
1125 : *
1126 : * @return non zero if the name is empty/null.
1127 : *
1128 : * @ingroup hx509_name
1129 : */
1130 :
1131 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1132 121 : hx509_name_is_null_p(const hx509_name name)
1133 : {
1134 242 : return name->der_name.element == choice_Name_rdnSequence &&
1135 121 : name->der_name.u.rdnSequence.len == 0;
1136 : }
1137 :
1138 : int
1139 0 : _hx509_unparse_PermanentIdentifier(hx509_context context,
1140 : struct rk_strpool **strpool,
1141 : heim_any *value)
1142 : {
1143 0 : PermanentIdentifier pi;
1144 0 : size_t len;
1145 0 : const char *pid = "";
1146 0 : char *s = NULL;
1147 0 : int ret;
1148 :
1149 0 : ret = decode_PermanentIdentifier(value->data, value->length, &pi, &len);
1150 0 : if (ret == 0 && pi.assigner &&
1151 0 : der_print_heim_oid(pi.assigner, '.', &s) != 0)
1152 0 : ret = hx509_enomem(context);
1153 0 : if (pi.identifierValue && *pi.identifierValue)
1154 0 : pid = *pi.identifierValue;
1155 0 : if (ret == 0 &&
1156 0 : (*strpool = rk_strpoolprintf(*strpool, "%s:%s", s ? s : "", pid)) == NULL)
1157 0 : ret = hx509_enomem(context);
1158 0 : free_PermanentIdentifier(&pi);
1159 0 : free(s);
1160 0 : if (ret) {
1161 0 : rk_strpoolfree(*strpool);
1162 0 : *strpool = rk_strpoolprintf(NULL,
1163 : "<error-decoding-PermanentIdentifier");
1164 0 : hx509_set_error_string(context, 0, ret,
1165 : "Failed to decode PermanentIdentifier");
1166 : }
1167 0 : return ret;
1168 : }
1169 :
1170 : int
1171 0 : _hx509_unparse_HardwareModuleName(hx509_context context,
1172 : struct rk_strpool **strpool,
1173 : heim_any *value)
1174 : {
1175 0 : HardwareModuleName hm;
1176 0 : size_t len;
1177 0 : char *s = NULL;
1178 0 : int ret;
1179 :
1180 0 : ret = decode_HardwareModuleName(value->data, value->length, &hm, &len);
1181 0 : if (ret == 0 && hm.hwSerialNum.length > 256)
1182 0 : hm.hwSerialNum.length = 256;
1183 0 : if (ret == 0)
1184 0 : ret = der_print_heim_oid(&hm.hwType, '.', &s);
1185 0 : if (ret == 0) {
1186 0 : *strpool = rk_strpoolprintf(*strpool, "%s:%.*s%s", s,
1187 0 : (int)hm.hwSerialNum.length,
1188 0 : (char *)hm.hwSerialNum.data,
1189 0 : value->length == len ? "" : ", <garbage>");
1190 0 : if (*strpool == NULL)
1191 0 : ret = hx509_enomem(context);
1192 : }
1193 0 : free_HardwareModuleName(&hm);
1194 0 : free(s);
1195 0 : if (ret) {
1196 0 : rk_strpoolfree(*strpool);
1197 0 : *strpool = rk_strpoolprintf(NULL,
1198 : "<error-decoding-HardwareModuleName");
1199 0 : hx509_set_error_string(context, 0, ret,
1200 : "Failed to decode HardwareModuleName");
1201 : }
1202 0 : return ret;
1203 : }
1204 :
1205 : /*
1206 : * This necessarily duplicates code from libkrb5, and has to unless we move
1207 : * common code here or to lib/roken for it. We do have slightly different
1208 : * needs (e.g., we want space quoted, and we want to indicate whether we saw
1209 : * trailing garbage, we have no need for flags, no special realm treatment,
1210 : * etc) than the corresponding code in libkrb5, so for now we duplicate this
1211 : * code.
1212 : *
1213 : * The relevant RFCs here are RFC1964 for the string representation of Kerberos
1214 : * principal names, and RFC4556 for the KRB5PrincipalName ASN.1 type (Kerberos
1215 : * lacks such a type because on the wire the name and realm are sent
1216 : * separately as a form of cheap compression).
1217 : *
1218 : * Note that we cannot handle embedded NULs because of Heimdal's representation
1219 : * of ASN.1 strings as C strings.
1220 : */
1221 : int
1222 0 : _hx509_unparse_KRB5PrincipalName(hx509_context context,
1223 : struct rk_strpool **strpool,
1224 : heim_any *value)
1225 : {
1226 0 : KRB5PrincipalName kn;
1227 0 : size_t len;
1228 0 : int ret;
1229 :
1230 0 : ret = decode_KRB5PrincipalName(value->data, value->length, &kn, &len);
1231 0 : if (ret == 0 &&
1232 0 : (*strpool = _hx509_unparse_kerberos_name(*strpool, &kn)) == NULL)
1233 0 : ret = hx509_enomem(context);
1234 0 : free_KRB5PrincipalName(&kn);
1235 0 : if (ret == 0 && (value->length != len) &&
1236 0 : (*strpool = rk_strpoolprintf(*strpool, " <garbage>")) == NULL)
1237 0 : ret = hx509_enomem(context);
1238 0 : if (ret) {
1239 0 : rk_strpoolfree(*strpool);
1240 0 : *strpool = rk_strpoolprintf(NULL,
1241 : "<error-decoding-PrincipalName");
1242 0 : hx509_set_error_string(context, 0, ret,
1243 : "Failed to decode PermanentIdentifier");
1244 : }
1245 0 : return ret;
1246 : }
1247 :
1248 : struct rk_strpool *
1249 0 : _hx509_unparse_kerberos_name(struct rk_strpool *strpool, KRB5PrincipalName *kn)
1250 : {
1251 0 : static const char comp_quotable_chars[] = " \n\t\b\\/@";
1252 0 : static const char realm_quotable_chars[] = " \n\t\b\\@";
1253 0 : const char *s;
1254 0 : size_t i, k, len, plen;
1255 0 : int need_slash = 0;
1256 :
1257 0 : for (i = 0; i < kn->principalName.name_string.len; i++) {
1258 0 : s = kn->principalName.name_string.val[i];
1259 0 : len = strlen(s);
1260 :
1261 0 : if (need_slash)
1262 0 : strpool = rk_strpoolprintf(strpool, "/");
1263 0 : need_slash = 1;
1264 :
1265 0 : for (k = 0; k < len; s += plen, k += plen) {
1266 0 : char c;
1267 :
1268 0 : plen = strcspn(s, comp_quotable_chars);
1269 0 : if (plen)
1270 0 : strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
1271 0 : if (k + plen >= len)
1272 0 : continue;
1273 0 : switch ((c = s[plen++])) {
1274 0 : case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
1275 0 : case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
1276 0 : case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
1277 : /* default -> '@', ' ', '\\', or '/' */
1278 0 : default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
1279 : }
1280 : }
1281 : }
1282 0 : if (!kn->realm)
1283 0 : return strpool;
1284 0 : strpool = rk_strpoolprintf(strpool, "@");
1285 :
1286 0 : s = kn->realm;
1287 0 : len = strlen(kn->realm);
1288 0 : for (k = 0; k < len; s += plen, k += plen) {
1289 0 : char c;
1290 :
1291 0 : plen = strcspn(s, realm_quotable_chars);
1292 0 : if (plen)
1293 0 : strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
1294 0 : if (k + plen >= len)
1295 0 : continue;
1296 0 : switch ((c = s[plen++])) {
1297 0 : case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
1298 0 : case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
1299 0 : case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
1300 : /* default -> '@', ' ', or '\\' */
1301 0 : default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
1302 : }
1303 : }
1304 0 : return strpool;
1305 : }
1306 :
1307 : int
1308 0 : _hx509_unparse_utf8_string_name(hx509_context context,
1309 : struct rk_strpool **strpool,
1310 : heim_any *value)
1311 : {
1312 0 : PKIXXmppAddr us;
1313 0 : size_t size;
1314 0 : int ret;
1315 :
1316 0 : ret = decode_PKIXXmppAddr(value->data, value->length, &us, &size);
1317 0 : if (ret == 0 &&
1318 0 : (*strpool = rk_strpoolprintf(*strpool, "%s", us)) == NULL)
1319 0 : ret = hx509_enomem(context);
1320 0 : if (ret) {
1321 0 : rk_strpoolfree(*strpool);
1322 0 : *strpool = rk_strpoolprintf(NULL,
1323 : "<error-decoding-UTF8String-SAN>");
1324 0 : hx509_set_error_string(context, 0, ret,
1325 : "Failed to decode UTF8String SAN");
1326 : }
1327 0 : free_PKIXXmppAddr(&us);
1328 0 : return ret;
1329 : }
1330 :
1331 : int
1332 0 : _hx509_unparse_ia5_string_name(hx509_context context,
1333 : struct rk_strpool **strpool,
1334 : heim_any *value)
1335 : {
1336 0 : SRVName us;
1337 0 : size_t size;
1338 0 : int ret;
1339 :
1340 0 : ret = decode_SRVName(value->data, value->length, &us, &size);
1341 0 : if (ret == 0) {
1342 0 : rk_strpoolfree(*strpool);
1343 0 : *strpool = rk_strpoolprintf(NULL,
1344 : "<error-decoding-IA5String-SAN>");
1345 0 : hx509_set_error_string(context, 0, ret,
1346 : "Failed to decode UTF8String SAN");
1347 0 : return ret;
1348 : }
1349 0 : *strpool = rk_strpoolprintf(*strpool, "%.*s",
1350 0 : (int)us.length, (char *)us.data);
1351 0 : free_SRVName(&us);
1352 0 : return ret;
1353 : }
1354 :
1355 : typedef int (*other_unparser_f)(hx509_context,
1356 : struct rk_strpool **,
1357 : heim_any *);
1358 :
1359 : struct {
1360 : const heim_oid *oid;
1361 : const char *friendly_name;
1362 : other_unparser_f f;
1363 : } o_unparsers[] = {
1364 : { &asn1_oid_id_pkinit_san,
1365 : "KerberosPrincipalName",
1366 : _hx509_unparse_KRB5PrincipalName },
1367 : { &asn1_oid_id_pkix_on_permanentIdentifier,
1368 : "PermanentIdentifier",
1369 : _hx509_unparse_PermanentIdentifier },
1370 : { &asn1_oid_id_on_hardwareModuleName,
1371 : "HardwareModuleName",
1372 : _hx509_unparse_HardwareModuleName },
1373 : { &asn1_oid_id_pkix_on_xmppAddr,
1374 : "XMPPName",
1375 : _hx509_unparse_utf8_string_name },
1376 : { &asn1_oid_id_pkinit_ms_san,
1377 : "MSFTKerberosPrincipalName",
1378 : _hx509_unparse_utf8_string_name },
1379 : { &asn1_oid_id_pkix_on_dnsSRV,
1380 : "SRVName",
1381 : _hx509_unparse_ia5_string_name },
1382 : };
1383 :
1384 : /**
1385 : * Unparse the hx509 name in name into a string.
1386 : *
1387 : * @param name the name to print
1388 : * @param str an allocated string returns the name in string form
1389 : *
1390 : * @return An hx509 error code, see hx509_get_error_string().
1391 : *
1392 : * @ingroup hx509_name
1393 : */
1394 :
1395 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1396 0 : hx509_general_name_unparse(GeneralName *name, char **str)
1397 : {
1398 0 : hx509_context context;
1399 0 : int ret;
1400 :
1401 0 : if ((ret = hx509_context_init(&context)))
1402 0 : return ret;
1403 0 : ret = hx509_general_name_unparse2(context, name, str);
1404 0 : hx509_context_free(&context);
1405 0 : return ret;
1406 : }
1407 :
1408 : /**
1409 : * Unparse the hx509 name in name into a string.
1410 : *
1411 : * @param context hx509 library context
1412 : * @param name the name to print
1413 : * @param str an allocated string returns the name in string form
1414 : *
1415 : * @return An hx509 error code, see hx509_get_error_string().
1416 : *
1417 : * @ingroup hx509_name
1418 : */
1419 :
1420 : HX509_LIB_FUNCTION int HX509_LIB_CALL
1421 0 : hx509_general_name_unparse2(hx509_context context,
1422 : GeneralName *name,
1423 : char **str)
1424 : {
1425 0 : struct rk_strpool *strpool = NULL;
1426 0 : int ret = 0;
1427 :
1428 0 : *str = NULL;
1429 :
1430 0 : switch (name->element) {
1431 0 : case choice_GeneralName_otherName: {
1432 0 : size_t i;
1433 0 : char *oid;
1434 :
1435 0 : ret = hx509_oid_sprint(&name->u.otherName.type_id, &oid);
1436 0 : if (ret == 0)
1437 0 : strpool = rk_strpoolprintf(strpool, "otherName: %s ", oid);
1438 0 : if (strpool == NULL)
1439 0 : ret = ENOMEM;
1440 :
1441 0 : for (i = 0; ret == 0 && i < sizeof(o_unparsers)/sizeof(o_unparsers[0]); i++) {
1442 0 : if (der_heim_oid_cmp(&name->u.otherName.type_id,
1443 : o_unparsers[i].oid))
1444 0 : continue;
1445 0 : strpool = rk_strpoolprintf(strpool, "%s ",o_unparsers[i].friendly_name);
1446 0 : if (strpool == NULL)
1447 0 : ret = ENOMEM;
1448 0 : if (ret == 0)
1449 0 : ret = o_unparsers[i].f(context, &strpool, &name->u.otherName.value);
1450 0 : break;
1451 : }
1452 0 : if (ret == 0 && i == sizeof(o_unparsers)/sizeof(o_unparsers[0])) {
1453 0 : strpool = rk_strpoolprintf(strpool, "<unknown-other-name-type>");
1454 0 : ret = ENOTSUP;
1455 : }
1456 0 : free(oid);
1457 0 : break;
1458 : }
1459 0 : case choice_GeneralName_rfc822Name:
1460 0 : strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s",
1461 0 : (int)name->u.rfc822Name.length,
1462 0 : (char *)name->u.rfc822Name.data);
1463 0 : break;
1464 0 : case choice_GeneralName_dNSName:
1465 0 : strpool = rk_strpoolprintf(strpool, "dNSName: %.*s",
1466 0 : (int)name->u.dNSName.length,
1467 0 : (char *)name->u.dNSName.data);
1468 0 : break;
1469 0 : case choice_GeneralName_directoryName: {
1470 0 : Name dir;
1471 0 : char *s;
1472 0 : memset(&dir, 0, sizeof(dir));
1473 0 : dir.element = (enum Name_enum)name->u.directoryName.element;
1474 0 : dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
1475 0 : ret = _hx509_unparse_Name(&dir, &s);
1476 0 : if (ret)
1477 0 : return ret;
1478 0 : strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
1479 0 : free(s);
1480 0 : break;
1481 : }
1482 0 : case choice_GeneralName_uniformResourceIdentifier:
1483 0 : strpool = rk_strpoolprintf(strpool, "URI: %.*s",
1484 0 : (int)name->u.uniformResourceIdentifier.length,
1485 0 : (char *)name->u.uniformResourceIdentifier.data);
1486 0 : break;
1487 0 : case choice_GeneralName_iPAddress: {
1488 0 : unsigned char *a = name->u.iPAddress.data;
1489 :
1490 0 : strpool = rk_strpoolprintf(strpool, "IPAddress: ");
1491 0 : if (strpool == NULL)
1492 0 : break;
1493 0 : if (name->u.iPAddress.length == 4)
1494 0 : strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
1495 0 : a[0], a[1], a[2], a[3]);
1496 0 : else if (name->u.iPAddress.length == 16)
1497 0 : strpool = rk_strpoolprintf(strpool,
1498 : "%02X:%02X:%02X:%02X:"
1499 : "%02X:%02X:%02X:%02X:"
1500 : "%02X:%02X:%02X:%02X:"
1501 : "%02X:%02X:%02X:%02X",
1502 0 : a[0], a[1], a[2], a[3],
1503 0 : a[4], a[5], a[6], a[7],
1504 0 : a[8], a[9], a[10], a[11],
1505 0 : a[12], a[13], a[14], a[15]);
1506 : else
1507 0 : strpool = rk_strpoolprintf(strpool,
1508 : "unknown IP address of length %lu",
1509 0 : (unsigned long)name->u.iPAddress.length);
1510 0 : break;
1511 : }
1512 0 : case choice_GeneralName_registeredID: {
1513 0 : char *oid;
1514 0 : hx509_oid_sprint(&name->u.registeredID, &oid);
1515 0 : if (oid == NULL)
1516 0 : return ENOMEM;
1517 0 : strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid);
1518 0 : free(oid);
1519 0 : break;
1520 : }
1521 0 : default:
1522 0 : return EINVAL;
1523 : }
1524 0 : if (ret)
1525 0 : rk_strpoolfree(strpool);
1526 0 : else if (strpool == NULL || (*str = rk_strpoolcollect(strpool)) == NULL)
1527 0 : return ENOMEM;
1528 0 : return ret;
1529 : }
|