Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/third_party/heimdal/lib/hx509/name.c
Line
Count
Source
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
quote_string(const char *f, size_t len, int flags, size_t *rlen)
109
0
{
110
0
    size_t i, j, tolen;
111
0
    const unsigned char *from = (const unsigned char *)f;
112
0
    unsigned char *to;
113
114
0
    tolen = len * 3 + 1;
115
0
    to = malloc(tolen);
116
0
    if (to == NULL)
117
0
  return NULL;
118
119
0
    for (i = 0, j = 0; i < len; i++) {
120
0
  unsigned char map = char_map[from[i]] & flags;
121
0
  if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) {
122
0
      to[j++] = '\\';
123
0
      to[j++] = from[i];
124
0
  } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) {
125
126
0
      to[j++] = '\\';
127
0
      to[j++] = from[i];
128
0
  } else if (map & Q_RFC2253_QUOTE) {
129
0
      to[j++] = '\\';
130
0
      to[j++] = from[i];
131
0
  } 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
0
  } else {
136
0
      to[j++] = from[i];
137
0
  }
138
0
    }
139
0
    to[j] = '\0';
140
0
    assert(j < tolen);
141
0
    *rlen = j;
142
0
    return (char *)to;
143
0
}
144
145
146
static int
147
append_string(char **str, size_t *total_len, const char *ss,
148
        size_t len, int quote)
149
0
{
150
0
    char *s, *qs;
151
152
0
    if (quote)
153
0
  qs = quote_string(ss, len, Q_RFC2253, &len);
154
0
    else
155
0
  qs = rk_UNCONST(ss);
156
157
0
    s = realloc(*str, len + *total_len + 1);
158
0
    if (s == NULL)
159
0
  _hx509_abort("allocation failure"); /* XXX */
160
0
    memcpy(s + *total_len, qs, len);
161
0
    if (qs != ss)
162
0
  free(qs);
163
0
    s[*total_len + len] = '\0';
164
0
    *str = s;
165
0
    *total_len += len;
166
0
    return 0;
167
0
}
168
169
static char *
170
oidtostring(const heim_oid *type, int *type_choice)
171
0
{
172
0
    char *s;
173
0
    size_t i;
174
175
0
    if (type_choice)
176
0
        *type_choice = choice_DirectoryString_utf8String;
177
178
0
    for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
179
0
  if (der_heim_oid_cmp(no[i].o, type) == 0) {
180
0
            if (type_choice && no[i].type_choice)
181
0
                *type_choice = no[i].type_choice;
182
0
      return strdup(no[i].n);
183
0
        }
184
0
    }
185
0
    if (der_print_heim_oid(type, '.', &s) != 0)
186
0
  return NULL;
187
0
    return s;
188
0
}
189
190
static size_t
191
oidtomaxlen(const heim_oid *type)
192
0
{
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
0
    }
199
0
    return 0;
200
0
}
201
202
static int
203
stringtooid(const char *name, size_t len, heim_oid *oid)
204
0
{
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
0
    }
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
0
}
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
hx509_name_to_string(const hx509_name name, char **str)
239
0
{
240
0
    return _hx509_Name_to_string(&name->der_name, str);
241
0
}
242
243
HX509_LIB_FUNCTION int HX509_LIB_CALL
244
_hx509_Name_to_string(const Name *n, char **str)
245
0
{
246
0
    size_t total_len = 0;
247
0
    size_t i, j, m;
248
0
    int ret;
249
250
0
    *str = strdup("");
251
0
    if (*str == NULL)
252
0
  return ENOMEM;
253
254
0
    for (m = n->u.rdnSequence.len; m > 0; m--) {
255
0
  size_t len;
256
0
  i = m - 1;
257
258
0
  for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
259
0
      DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
260
0
      char *oidname;
261
0
      char *ss;
262
263
0
      oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type, NULL);
264
265
0
      switch(ds->element) {
266
0
      case choice_DirectoryString_ia5String:
267
0
    ss = ds->u.ia5String.data;
268
0
    len = ds->u.ia5String.length;
269
0
    break;
270
0
      case choice_DirectoryString_printableString:
271
0
    ss = ds->u.printableString.data;
272
0
    len = ds->u.printableString.length;
273
0
    break;
274
0
      case choice_DirectoryString_utf8String:
275
0
    ss = ds->u.utf8String;
276
0
    len = strlen(ss);
277
0
    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
0
                }
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
0
    }
302
0
    ss[k] = '\0';
303
0
    len = k;
304
0
    break;
305
0
      }
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
0
                }
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
0
    }
334
0
    ss[k] = '\0';
335
0
    len = k;
336
0
    break;
337
0
      }
338
0
      default:
339
0
    _hx509_abort("unknown directory type: %d", ds->element);
340
0
    exit(1);
341
0
      }
342
0
      append_string(str, &total_len, oidname, strlen(oidname), 0);
343
0
      free(oidname);
344
0
      append_string(str, &total_len, "=", 1, 0);
345
0
      append_string(str, &total_len, ss, len, 1);
346
0
      if (ds->element == choice_DirectoryString_bmpString ||
347
0
    ds->element == choice_DirectoryString_universalString)
348
0
      {
349
0
    free(ss);
350
0
      }
351
0
      if (j + 1 < n->u.rdnSequence.val[i].len)
352
0
    append_string(str, &total_len, "+", 1, 0);
353
0
  }
354
355
0
  if (i > 0)
356
0
      append_string(str, &total_len, ",", 1, 0);
357
0
    }
358
0
    return 0;
359
0
}
360
361
#define COPYCHARARRAY(_ds,_el,_l,_n)      \
362
0
        (_l) = strlen(_ds->u._el);      \
363
0
  (_n) = malloc((_l + 1) * sizeof((_n)[0]));  \
364
0
  if ((_n) == NULL)       \
365
0
      return ENOMEM;       \
366
0
  for (i = 0; i < (_l); i++)     \
367
0
      (_n)[i] = _ds->u._el[i]
368
369
370
#define COPYVALARRAY(_ds,_el,_l,_n)     \
371
0
        (_l) = _ds->u._el.length;     \
372
0
  (_n) = malloc((_l + 1) * sizeof((_n)[0]));  \
373
0
  if ((_n) == NULL)       \
374
0
      return ENOMEM;       \
375
0
  for (i = 0; i < (_l); i++)     \
376
0
      (_n)[i] = _ds->u._el.data[i]
377
378
#define COPYVOIDARRAY(_ds,_el,_l,_n)      \
379
0
        (_l) = _ds->u._el.length;     \
380
0
  (_n) = malloc((_l + 1) * sizeof((_n)[0]));  \
381
0
  if ((_n) == NULL)       \
382
0
      return ENOMEM;       \
383
0
  for (i = 0; i < (_l); i++)     \
384
0
      (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
385
386
387
388
static int
389
dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
390
0
{
391
0
    wind_profile_flags flags;
392
0
    size_t i, len;
393
0
    int ret = 0;
394
0
    uint32_t *name;
395
396
0
    *rname = NULL;
397
0
    *rlen = 0;
398
399
0
    switch(ds->element) {
400
0
    case choice_DirectoryString_ia5String:
401
0
  flags = WIND_PROFILE_LDAP;
402
0
  COPYVOIDARRAY(ds, ia5String, len, name);
403
0
  break;
404
0
    case choice_DirectoryString_printableString:
405
0
  flags = WIND_PROFILE_LDAP;
406
0
  flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
407
0
  COPYVOIDARRAY(ds, printableString, len, name);
408
0
  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
0
    case choice_DirectoryString_utf8String:
422
0
  flags = WIND_PROFILE_LDAP;
423
0
  ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
424
0
  if (ret)
425
0
      return ret;
426
0
  name = malloc((len + 1) * sizeof(name[0]));
427
0
  if (name == NULL)
428
0
      return ENOMEM;
429
0
  ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
430
0
  if (ret) {
431
0
      free(name);
432
0
      return ret;
433
0
  }
434
0
  break;
435
0
    default:
436
0
  _hx509_abort("unknown directory type: %d", ds->element);
437
0
    }
438
439
0
    *rlen = len;
440
    /* try a couple of times to get the length right, XXX gross */
441
0
    for (i = 0; i < 4; i++) {
442
0
  *rlen = *rlen * 2;
443
0
  if ((*rname = malloc((rlen[0] + 1) * sizeof((*rname)[0]))) == NULL) {
444
0
            ret = ENOMEM;
445
0
            break;
446
0
        }
447
448
0
  ret = wind_stringprep(name, len, *rname, rlen, flags);
449
0
  if (ret == WIND_ERR_OVERRUN) {
450
0
      free(*rname);
451
0
      *rname = NULL;
452
0
      continue;
453
0
  } else
454
0
      break;
455
0
    }
456
0
    free(name);
457
0
    if (ret) {
458
0
  if (*rname)
459
0
      free(*rname);
460
0
  *rname = NULL;
461
0
  *rlen = 0;
462
0
  return ret;
463
0
    }
464
465
0
    return 0;
466
0
}
467
468
HX509_LIB_FUNCTION int HX509_LIB_CALL
469
_hx509_name_ds_cmp(const DirectoryString *ds1,
470
       const DirectoryString *ds2,
471
       int *diff)
472
0
{
473
0
    uint32_t *ds1lp, *ds2lp;
474
0
    size_t ds1len, ds2len, i;
475
0
    int ret;
476
477
0
    ret = dsstringprep(ds1, &ds1lp, &ds1len);
478
0
    if (ret)
479
0
  return ret;
480
0
    ret = dsstringprep(ds2, &ds2lp, &ds2len);
481
0
    if (ret) {
482
0
  free(ds1lp);
483
0
  return ret;
484
0
    }
485
486
0
    if (ds1len != ds2len)
487
0
  *diff = ds1len - ds2len;
488
0
    else {
489
0
  for (i = 0; i < ds1len; i++) {
490
0
      *diff = ds1lp[i] - ds2lp[i];
491
0
      if (*diff)
492
0
    break;
493
0
  }
494
0
    }
495
0
    free(ds1lp);
496
0
    free(ds2lp);
497
498
0
    return 0;
499
0
}
500
501
HX509_LIB_FUNCTION int HX509_LIB_CALL
502
_hx509_name_cmp(const Name *n1, const Name *n2, int *c)
503
0
{
504
0
    int ret;
505
0
    size_t i, j;
506
507
0
    *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
508
0
    if (*c)
509
0
  return 0;
510
511
0
    for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
512
0
  *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
513
0
  if (*c)
514
0
      return 0;
515
516
0
  for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
517
0
      *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
518
0
          &n1->u.rdnSequence.val[i].val[j].type);
519
0
      if (*c)
520
0
    return 0;
521
522
0
      ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
523
0
             &n2->u.rdnSequence.val[i].val[j].value,
524
0
             c);
525
0
      if (ret)
526
0
    return ret;
527
0
      if (*c)
528
0
    return 0;
529
0
  }
530
0
    }
531
0
    *c = 0;
532
0
    return 0;
533
0
}
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
hx509_name_cmp(hx509_name n1, hx509_name n2)
549
0
{
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
0
}
556
557
558
HX509_LIB_FUNCTION int HX509_LIB_CALL
559
_hx509_name_from_Name(const Name *n, hx509_name *name)
560
0
{
561
0
    int ret;
562
0
    *name = calloc(1, sizeof(**name));
563
0
    if (*name == NULL)
564
0
  return ENOMEM;
565
0
    ret = copy_Name(n, &(*name)->der_name);
566
0
    if (ret) {
567
0
  free(*name);
568
0
  *name = NULL;
569
0
    }
570
0
    return ret;
571
0
}
572
573
HX509_LIB_FUNCTION int HX509_LIB_CALL
574
_hx509_name_modify(hx509_context context,
575
       Name *name,
576
       int append,
577
       const heim_oid *oid,
578
       const char *str)
579
0
{
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
0
                               "long (max %llu): %s", a ? a : "<unknown>",
599
0
                               max_len, str);
600
0
        free(a);
601
0
        return ret;
602
0
    }
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
0
    }
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
0
    }
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
0
                               "Internal error; unknown DirectoryString choice");
666
0
        return ENOTSUP;
667
0
    }
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
0
            sizeof(name->u.rdnSequence.val[0]));
682
0
    name->u.rdnSequence.val[0] = rdn;
683
0
    return 0;
684
0
}
685
686
HX509_LIB_FUNCTION int HX509_LIB_CALL
687
hx509_empty_name(hx509_context context, hx509_name *name)
688
0
{
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
0
    }
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
0
}
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
hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
713
0
{
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
0
    }
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
0
  } else {
740
0
      len = strlen(p);
741
0
      last = 0;
742
0
  }
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
0
  }
750
0
  if (q == p) {
751
0
      ret = HX509_PARSING_NAME_FAILED;
752
0
      hx509_set_error_string(context, 0, ret,
753
0
           "missing name before = in %s", p);
754
0
      goto out;
755
0
  }
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
0
  }
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
0
  }
770
771
0
  {
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
0
      }
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
0
  }
792
0
  p += len + last;
793
0
    }
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
0
}
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
hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
817
0
{
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
0
    }
829
0
    return 0;
830
0
}
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
hx509_name_to_Name(const hx509_name from, Name *to)
845
0
{
846
0
    return copy_Name(&from->der_name, to);
847
0
}
848
849
HX509_LIB_FUNCTION int HX509_LIB_CALL
850
hx509_name_normalize(hx509_context context, hx509_name name)
851
0
{
852
0
    return 0;
853
0
}
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
hx509_name_expand(hx509_context context,
870
      hx509_name name,
871
      hx509_env env)
872
0
{
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
0
    }
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
0
                            ds->u.ia5String.length);
914
0
                break;
915
0
            case choice_DirectoryString_printableString:
916
0
                s = strndup(ds->u.printableString.data,
917
0
                            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
0
            }
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
0
    }
939
0
      }
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
0
    }
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
0
             "variable %.*s missing",
956
0
             (int)(p2 - p), p);
957
0
        rk_strpoolfree(strpool);
958
0
                    free(s);
959
0
        return EINVAL;
960
0
    }
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
0
    }
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
0
    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
0
    }
980
0
      }
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
0
                }
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
0
                }
1022
0
      }
1023
0
  }
1024
0
    }
1025
1026
0
    if (!bounds_check) {
1027
0
        hx509_set_error_string(context, 0, HX509_PARSING_NAME_FAILED,
1028
0
                               "some expanded RDNs are too long");
1029
0
        return HX509_PARSING_NAME_FAILED;
1030
0
    }
1031
0
    return 0;
1032
0
}
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
hx509_name_free(hx509_name *name)
1044
0
{
1045
0
    free_Name(&(*name)->der_name);
1046
0
    memset(*name, 0, sizeof(**name));
1047
0
    free(*name);
1048
0
    *name = NULL;
1049
0
}
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
hx509_unparse_der_name(const void *data, size_t length, char **str)
1065
0
{
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
0
}
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
hx509_name_binary(const hx509_name name, heim_octet_string *os)
1093
0
{
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
0
}
1105
1106
HX509_LIB_FUNCTION int HX509_LIB_CALL
1107
_hx509_unparse_Name(const Name *aname, char **str)
1108
0
{
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
0
}
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
hx509_name_is_null_p(const hx509_name name)
1133
0
{
1134
0
    return name->der_name.element == choice_Name_rdnSequence &&
1135
0
        name->der_name.u.rdnSequence.len == 0;
1136
0
}
1137
1138
int
1139
_hx509_unparse_PermanentIdentifier(hx509_context context,
1140
                                   struct rk_strpool **strpool,
1141
                                   heim_any *value)
1142
0
{
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
0
                                    "<error-decoding-PermanentIdentifier");
1164
0
        hx509_set_error_string(context, 0, ret,
1165
0
                               "Failed to decode PermanentIdentifier");
1166
0
    }
1167
0
    return ret;
1168
0
}
1169
1170
int
1171
_hx509_unparse_HardwareModuleName(hx509_context context,
1172
                                  struct rk_strpool **strpool,
1173
                                  heim_any *value)
1174
0
{
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
0
    }
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
0
                                    "<error-decoding-HardwareModuleName");
1199
0
        hx509_set_error_string(context, 0, ret,
1200
0
                               "Failed to decode HardwareModuleName");
1201
0
    }
1202
0
    return ret;
1203
0
}
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
_hx509_unparse_KRB5PrincipalName(hx509_context context,
1223
                                 struct rk_strpool **strpool,
1224
                                 heim_any *value)
1225
0
{
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
0
                                    "<error-decoding-PrincipalName");
1242
0
        hx509_set_error_string(context, 0, ret,
1243
0
                               "Failed to decode PermanentIdentifier");
1244
0
    }
1245
0
    return ret;
1246
0
}
1247
1248
struct rk_strpool *
1249
_hx509_unparse_kerberos_name(struct rk_strpool *strpool, KRB5PrincipalName *kn)
1250
0
{
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
0
            }
1280
0
        }
1281
0
    }
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
0
        }
1303
0
    }
1304
0
    return strpool;
1305
0
}
1306
1307
int
1308
_hx509_unparse_utf8_string_name(hx509_context context,
1309
                                struct rk_strpool **strpool,
1310
                                heim_any *value)
1311
0
{
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
0
                                    "<error-decoding-UTF8String-SAN>");
1324
0
        hx509_set_error_string(context, 0, ret,
1325
0
                               "Failed to decode UTF8String SAN");
1326
0
    }
1327
0
    free_PKIXXmppAddr(&us);
1328
0
    return ret;
1329
0
}
1330
1331
int
1332
_hx509_unparse_ia5_string_name(hx509_context context,
1333
                               struct rk_strpool **strpool,
1334
                               heim_any *value)
1335
0
{
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
0
                                    "<error-decoding-IA5String-SAN>");
1345
0
        hx509_set_error_string(context, 0, ret,
1346
0
                               "Failed to decode UTF8String SAN");
1347
0
        return ret;
1348
0
    }
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
0
}
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
hx509_general_name_unparse(GeneralName *name, char **str)
1397
0
{
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
0
}
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
hx509_general_name_unparse2(hx509_context context,
1422
                            GeneralName *name,
1423
                            char **str)
1424
0
{
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
0
                                 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
0
        }
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
0
        }
1456
0
  free(oid);
1457
0
  break;
1458
0
    }
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
0
    }
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
0
               "%02X:%02X:%02X:%02X:"
1499
0
               "%02X:%02X:%02X:%02X:"
1500
0
               "%02X:%02X:%02X:%02X:"
1501
0
               "%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
0
  else
1507
0
      strpool = rk_strpoolprintf(strpool,
1508
0
               "unknown IP address of length %lu",
1509
0
               (unsigned long)name->u.iPAddress.length);
1510
0
  break;
1511
0
    }
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
0
    }
1521
0
    default:
1522
0
  return EINVAL;
1523
0
    }
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
0
}