Coverage Report

Created: 2022-08-24 06:30

/src/libressl/crypto/x509/x509_constraints.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: x509_constraints.c,v 1.28 2022/06/27 15:03:11 beck Exp $ */
2
/*
3
 * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <ctype.h>
19
#include <errno.h>
20
#include <stdio.h>
21
#include <string.h>
22
#include <time.h>
23
#include <unistd.h>
24
25
#include <sys/socket.h>
26
#include <arpa/inet.h>
27
28
#include <openssl/safestack.h>
29
#include <openssl/x509.h>
30
#include <openssl/x509v3.h>
31
32
#include "x509_internal.h"
33
34
/* RFC 2821 section 4.5.3.1 */
35
0
#define LOCAL_PART_MAX_LEN 64
36
0
#define DOMAIN_PART_MAX_LEN 255
37
38
struct x509_constraints_name *
39
x509_constraints_name_new(void)
40
0
{
41
0
  return (calloc(1, sizeof(struct x509_constraints_name)));
42
0
}
43
44
void
45
x509_constraints_name_clear(struct x509_constraints_name *name)
46
0
{
47
0
  free(name->name);
48
0
  free(name->local);
49
0
  free(name->der);
50
0
  memset(name, 0, sizeof(*name));
51
0
}
52
53
void
54
x509_constraints_name_free(struct x509_constraints_name *name)
55
0
{
56
0
  if (name == NULL)
57
0
    return;
58
0
  x509_constraints_name_clear(name);
59
0
  free(name);
60
0
}
61
62
struct x509_constraints_name *
63
x509_constraints_name_dup(struct x509_constraints_name *name)
64
0
{
65
0
  struct x509_constraints_name *new;
66
67
0
  if ((new = x509_constraints_name_new()) == NULL)
68
0
    goto err;
69
0
  new->type = name->type;
70
0
  new->af = name->af;
71
0
  new->der_len = name->der_len;
72
0
  if (name->der_len > 0) {
73
0
    if ((new->der = malloc(name->der_len)) == NULL)
74
0
      goto err;
75
0
    memcpy(new->der, name->der, name->der_len);
76
0
  }
77
0
  if (name->name != NULL && (new->name = strdup(name->name)) == NULL)
78
0
    goto err;
79
0
  if (name->local != NULL && (new->local = strdup(name->local)) == NULL)
80
0
    goto err;
81
0
  memcpy(new->address, name->address, sizeof(name->address));
82
0
  return new;
83
0
 err:
84
0
  x509_constraints_name_free(new);
85
0
  return NULL;
86
0
}
87
88
struct x509_constraints_names *
89
x509_constraints_names_new(size_t names_max)
90
0
{
91
0
  struct x509_constraints_names *new;
92
93
0
  if ((new = calloc(1, sizeof(struct x509_constraints_names))) == NULL)
94
0
    return NULL;
95
96
0
  new->names_max = names_max;
97
98
0
  return new;
99
0
}
100
101
void
102
x509_constraints_names_clear(struct x509_constraints_names *names)
103
0
{
104
0
  size_t i;
105
106
0
  for (i = 0; i < names->names_count; i++)
107
0
    x509_constraints_name_free(names->names[i]);
108
0
  free(names->names);
109
0
  memset(names, 0, sizeof(*names));
110
0
}
111
112
void
113
x509_constraints_names_free(struct x509_constraints_names *names)
114
0
{
115
0
  if (names == NULL)
116
0
    return;
117
118
0
  x509_constraints_names_clear(names);
119
0
  free(names);
120
0
}
121
122
int
123
x509_constraints_names_add(struct x509_constraints_names *names,
124
    struct x509_constraints_name *name)
125
0
{
126
0
  if (names->names_count >= names->names_max)
127
0
    return 0;
128
0
  if (names->names_count == names->names_len) {
129
0
    struct x509_constraints_name **tmp;
130
0
    if ((tmp = recallocarray(names->names, names->names_len,
131
0
        names->names_len + 32, sizeof(*tmp))) == NULL)
132
0
      return 0;
133
0
    names->names_len += 32;
134
0
    names->names = tmp;
135
0
  }
136
0
  names->names[names->names_count] = name;
137
0
  names->names_count++;
138
0
  return 1;
139
0
}
140
141
struct x509_constraints_names *
142
x509_constraints_names_dup(struct x509_constraints_names *names)
143
0
{
144
0
  struct x509_constraints_names *new = NULL;
145
0
  struct x509_constraints_name *name = NULL;
146
0
  size_t i;
147
148
0
  if (names == NULL)
149
0
    return NULL;
150
151
0
  if ((new = x509_constraints_names_new(names->names_max)) == NULL)
152
0
    goto err;
153
154
0
  for (i = 0; i < names->names_count; i++) {
155
0
    if ((name = x509_constraints_name_dup(names->names[i])) == NULL)
156
0
      goto err;
157
0
    if (!x509_constraints_names_add(new, name))
158
0
      goto err;
159
0
  }
160
161
0
  return new;
162
0
 err:
163
0
  x509_constraints_names_free(new);
164
0
  x509_constraints_name_free(name);
165
0
  return NULL;
166
0
}
167
168
169
/*
170
 * Validate that the name contains only a hostname consisting of RFC
171
 * 5890 compliant A-labels (see RFC 6066 section 3). This is more
172
 * permissive to allow for a leading '.'  for a subdomain based
173
 * constraint, as well as allowing for '_' which is commonly accepted
174
 * by nonconformant DNS implementaitons.
175
 *
176
 * if "wildcards" is set it allows '*' to occur in the string at the end of a
177
 * component.
178
 */
179
static int
180
x509_constraints_valid_domain_internal(uint8_t *name, size_t len, int wildcards)
181
0
{
182
0
  uint8_t prev, c = 0;
183
0
  int component = 0;
184
0
  int first;
185
0
  size_t i;
186
187
0
  if (len > DOMAIN_PART_MAX_LEN)
188
0
    return 0;
189
190
0
  for (i = 0; i < len; i++) {
191
0
    prev = c;
192
0
    c = name[i];
193
194
0
    first = (i == 0);
195
196
    /* Everything has to be ASCII, with no NUL byte */
197
0
    if (!isascii(c) || c == '\0')
198
0
      return 0;
199
    /* It must be alphanumeric, a '-', '.', '_' or '*' */
200
0
    if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*')
201
0
      return 0;
202
203
    /* if it is a '*', fail if not wildcards */
204
0
    if (!wildcards && c == '*')
205
0
      return 0;
206
207
    /* '-' must not start a component or be at the end. */
208
0
    if (c == '-' && (component == 0 || i == len - 1))
209
0
      return 0;
210
211
    /*
212
     * '.' must not be at the end. It may be first overall
213
     * but must not otherwise start a component.
214
     */
215
0
    if (c == '.' && ((component == 0 && !first) || i == len - 1))
216
0
      return 0;
217
218
0
    if (c == '.') {
219
      /* Components can not end with a dash. */
220
0
      if (prev == '-')
221
0
        return 0;
222
      /* Start new component */
223
0
      component = 0;
224
0
      continue;
225
0
    }
226
    /*
227
     * Wildcards can only occur at the end of a component.
228
     * c*.com is valid, c*c.com is not.
229
     */
230
0
    if (prev == '*')
231
0
      return 0;
232
233
    /* Components must be 63 chars or less. */
234
0
    if (++component > 63)
235
0
      return 0;
236
0
  }
237
0
  return 1;
238
0
}
239
240
int
241
x509_constraints_valid_domain(uint8_t *name, size_t len)
242
0
{
243
0
  if (len == 0)
244
0
    return 0;
245
  /*
246
   * A domain may not be less than two characters, so you can't
247
   * have a require subdomain name with less than that.
248
   */
249
0
  if (len < 3 && name[0] == '.')
250
0
    return 0;
251
0
  return x509_constraints_valid_domain_internal(name, len, 0);
252
0
}
253
254
int
255
x509_constraints_valid_host(uint8_t *name, size_t len)
256
0
{
257
0
  struct sockaddr_in sin4;
258
0
  struct sockaddr_in6 sin6;
259
260
0
  if (len == 0)
261
0
    return 0;
262
0
  if (name[0] == '.') /* leading . not allowed in a host name*/
263
0
    return 0;
264
0
  if (inet_pton(AF_INET, name, &sin4) == 1)
265
0
    return 0;
266
0
  if (inet_pton(AF_INET6, name, &sin6) == 1)
267
0
    return 0;
268
0
  return x509_constraints_valid_domain_internal(name, len, 0);
269
0
}
270
271
int
272
x509_constraints_valid_sandns(uint8_t *name, size_t len)
273
0
{
274
0
  if (len == 0)
275
0
    return 0;
276
277
0
  if (name[0] == '.') /* leading . not allowed in a SAN DNS name */
278
0
    return 0;
279
  /*
280
   * A domain may not be less than two characters, so you
281
   * can't wildcard a single domain of less than that
282
   */
283
0
  if (len < 4 && name[0] == '*')
284
0
    return 0;
285
  /*
286
   * A wildcard may only be followed by a '.'
287
   */
288
0
  if (len >= 4 && name[0] == '*' && name[1] != '.')
289
0
    return 0;
290
291
0
  return x509_constraints_valid_domain_internal(name, len, 1);
292
0
}
293
294
static inline int
295
local_part_ok(char c)
296
0
{
297
0
  return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
298
0
      ('A' <= c && c <= 'Z') || c == '!' || c == '#' || c == '$' ||
299
0
      c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' ||
300
0
      c == '-' || c == '/' || c == '=' || c == '?' ||  c == '^' ||
301
0
      c == '_' || c == '`' || c == '{' || c == '|' || c == '}' ||
302
0
      c == '~' || c == '.');
303
0
}
304
305
/*
306
 * Parse "candidate" as an RFC 2821 mailbox.
307
 * Returns 0 if candidate is not a valid mailbox or if an error occurs.
308
 * Returns 1 if candidate is a mailbox and adds newly allocated
309
 * local and domain parts of the mailbox to "name->local" and name->name"
310
 */
311
int
312
x509_constraints_parse_mailbox(uint8_t *candidate, size_t len,
313
    struct x509_constraints_name *name)
314
0
{
315
0
  char working[DOMAIN_PART_MAX_LEN + 1] = { 0 };
316
0
  char *candidate_local = NULL;
317
0
  char *candidate_domain = NULL;
318
0
  size_t i, wi = 0;
319
0
  int accept = 0;
320
0
  int quoted = 0;
321
322
0
  if (candidate == NULL)
323
0
    return 0;
324
325
  /* It can't be bigger than the local part, domain part and the '@' */
326
0
  if (len > LOCAL_PART_MAX_LEN + DOMAIN_PART_MAX_LEN + 1)
327
0
    return 0;
328
329
0
  for (i = 0; i < len; i++) {
330
0
    char c = candidate[i];
331
    /* non ascii, cr, lf, or nul is never allowed */
332
0
    if (!isascii(c) || c == '\r' || c == '\n' || c == '\0')
333
0
      goto bad;
334
0
    if (i == 0) {
335
      /* local part is quoted part */
336
0
      if (c == '"')
337
0
        quoted = 1;
338
      /* can not start with a . */
339
0
      if (c == '.')
340
0
        goto bad;
341
0
    }
342
0
    if (accept) {
343
0
      if (wi >= DOMAIN_PART_MAX_LEN)
344
0
        goto bad;
345
0
      working[wi++] = c;
346
0
      accept = 0;
347
0
      continue;
348
0
    }
349
0
    if (candidate_local != NULL) {
350
      /* We are looking for the domain part */
351
0
      if (wi >= DOMAIN_PART_MAX_LEN)
352
0
        goto bad;
353
0
      working[wi++] = c;
354
0
      if (i == len - 1) {
355
0
        if (wi == 0)
356
0
          goto bad;
357
0
        if (candidate_domain != NULL)
358
0
          goto bad;
359
0
        candidate_domain = strdup(working);
360
0
        if (candidate_domain == NULL)
361
0
          goto bad;
362
0
      }
363
0
      continue;
364
0
    }
365
    /* We are looking for the local part */
366
0
    if (wi >= LOCAL_PART_MAX_LEN)
367
0
      break;
368
369
0
    if (quoted) {
370
0
      if (c == '\\') {
371
0
        accept = 1;
372
0
        continue;
373
0
      }
374
0
      if (c == '"' && i != 0) {
375
        /* end the quoted part. @ must be next */
376
0
        if (i + 1 == len || candidate[i + 1] != '@')
377
0
          goto bad;
378
0
        quoted = 0;
379
0
      }
380
      /*
381
       * XXX Go strangely permits sp but forbids ht
382
       * mimic that for now
383
       */
384
0
      if (c == 9)
385
0
        goto bad;
386
0
      if (wi >= LOCAL_PART_MAX_LEN)
387
0
        goto bad;
388
0
      working[wi++] = c;
389
0
      continue; /* all's good inside our quoted string */
390
0
    }
391
0
    if (c == '@') {
392
0
      if (wi == 0)
393
0
        goto bad;
394
0
      if (candidate_local != NULL)
395
0
        goto bad;
396
0
      candidate_local = strdup(working);
397
0
      if (candidate_local == NULL)
398
0
        goto bad;
399
0
      memset(working, 0, sizeof(working));
400
0
      wi = 0;
401
0
      continue;
402
0
    }
403
0
    if (c == '\\') {
404
      /*
405
       * RFC 3936 hints these can happen outside of
406
       * quotend string. don't include the \ but
407
       * next character must be ok.
408
       */
409
0
      if (i + 1 == len)
410
0
        goto bad;
411
0
      if (!local_part_ok(candidate[i + 1]))
412
0
        goto bad;
413
0
      accept = 1;
414
0
    }
415
0
    if (!local_part_ok(c))
416
0
      goto bad;
417
0
    if (wi >= LOCAL_PART_MAX_LEN)
418
0
      goto bad;
419
0
    working[wi++] = c;
420
0
  }
421
0
  if (candidate_local == NULL || candidate_domain == NULL)
422
0
    goto bad;
423
0
  if (!x509_constraints_valid_host(candidate_domain,
424
0
      strlen(candidate_domain)))
425
0
    goto bad;
426
427
0
  if (name != NULL) {
428
0
    name->local = candidate_local;
429
0
    name->name = candidate_domain;
430
0
    name->type = GEN_EMAIL;
431
0
  } else {
432
0
    free(candidate_local);
433
0
    free(candidate_domain);
434
0
  }
435
0
  return 1;
436
0
 bad:
437
0
  free(candidate_local);
438
0
  free(candidate_domain);
439
0
  return 0;
440
0
}
441
442
int
443
x509_constraints_valid_domain_constraint(uint8_t *constraint, size_t len)
444
0
{
445
0
  if (len == 0)
446
0
    return 1; /* empty constraints match */
447
448
  /*
449
   * A domain may not be less than two characters, so you
450
   * can't match a single domain of less than that
451
   */
452
0
  if (len < 3 && constraint[0] == '.')
453
0
    return 0;
454
0
  return x509_constraints_valid_domain_internal(constraint, len, 0);
455
0
}
456
457
/*
458
 * Extract the host part of a URI. On failure to parse a valid host part of the
459
 * URI, 0 is returned indicating an invalid URI. If the host part parses as
460
 * valid, or is not present, 1 is returned indicating a possibly valid URI.
461
 *
462
 * In the case of a valid URI, *hostpart will be set to a copy of the host part
463
 * of the URI, or the empty string if no URI is present. If memory allocation
464
 * fails *hostpart will be set to NULL, even though we returned 1. It is the
465
 * caller's responsibility to indicate an error for memory allocation failure,
466
 * and the callers responsibility to free *hostpart.
467
 *
468
 * RFC 3986:
469
 * the authority part of a uri starts with // and is terminated with
470
 * the next '/', '?', '#' or end of the URI.
471
 *
472
 * The authority itself contains [userinfo '@'] host [: port]
473
 *
474
 * so the host starts at the start or after the '@', and ends
475
 * with end of URI, '/', '?', "#', or ':'.
476
 */
477
int
478
x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart)
479
0
{
480
0
  size_t i, hostlen = 0;
481
0
  uint8_t *authority = NULL;
482
0
  char *host = NULL;
483
484
  /*
485
   * Find first '//'. there must be at least a '//' and
486
   * something else.
487
   */
488
0
  if (len < 3)
489
0
    return 0;
490
0
  for (i = 0; i < len - 1; i++) {
491
0
    if (!isascii(uri[i]))
492
0
      return 0;
493
0
    if (uri[i] == '/' && uri[i + 1] == '/') {
494
0
      authority = uri + i + 2;
495
0
      break;
496
0
    }
497
0
  }
498
0
  if (authority == NULL) {
499
    /*
500
     * There is no authority, so no host part in this
501
     * URI. This might be ok or might not, but it must
502
     * fail if we run into a name constraint later, so
503
     * we indicate that we have a URI with an empty
504
     * host part, and succeed.
505
     */
506
0
    *hostpart = strdup("");
507
0
    return 1;
508
0
  }
509
0
  for (i = authority - uri; i < len; i++) {
510
0
    if (!isascii(uri[i]))
511
0
      return 0;
512
    /* it has a userinfo part */
513
0
    if (uri[i] == '@') {
514
0
      hostlen = 0;
515
      /* it can only have one */
516
0
      if (host != NULL)
517
0
        break;
518
      /* start after the userinfo part */
519
0
      host = uri + i + 1;
520
0
      continue;
521
0
    }
522
    /* did we find the end? */
523
0
    if (uri[i] == ':' || uri[i] == '/' || uri[i] == '?' ||
524
0
        uri[i] == '#')
525
0
      break;
526
0
    hostlen++;
527
0
  }
528
0
  if (hostlen == 0)
529
0
    return 0;
530
0
  if (host == NULL)
531
0
    host = authority;
532
0
  if (!x509_constraints_valid_host(host, hostlen))
533
0
    return 0;
534
0
  if (hostpart != NULL)
535
0
    *hostpart = strndup(host, hostlen);
536
0
  return 1;
537
0
}
538
539
int
540
x509_constraints_sandns(char *sandns, size_t dlen, char *constraint, size_t len)
541
0
{
542
0
  char *suffix;
543
544
0
  if (len == 0)
545
0
    return 1; /* an empty constraint matches everything */
546
547
  /* match the end of the domain */
548
0
  if (dlen < len)
549
0
    return 0;
550
0
  suffix = sandns + (dlen - len);
551
0
  return (strncasecmp(suffix, constraint, len) == 0);
552
0
}
553
554
/*
555
 * Validate a pre-validated domain of length dlen against a pre-validated
556
 * constraint of length len.
557
 *
558
 * returns 1 if the domain and constraint match.
559
 * returns 0 otherwise.
560
 *
561
 * an empty constraint matches everyting.
562
 * constraint will be matched against the domain as a suffix if it
563
 * starts with a '.'.
564
 * domain will be matched against the constraint as a suffix if it
565
 * starts with a '.'.
566
 */
567
int
568
x509_constraints_domain(char *domain, size_t dlen, char *constraint, size_t len)
569
0
{
570
0
  if (len == 0)
571
0
    return 1; /* an empty constraint matches everything */
572
573
0
  if (constraint[0] == '.') {
574
    /* match the end of the domain */
575
0
    char *suffix;
576
0
    if (dlen < len)
577
0
      return 0;
578
0
    suffix = domain + (dlen - len);
579
0
    return (strncasecmp(suffix, constraint, len) == 0);
580
0
  }
581
0
  if (domain[0] == '.') {
582
    /* match the end of the constraint */
583
0
    char *suffix;
584
0
    if (len < dlen)
585
0
      return 0;
586
0
    suffix = constraint + (len - dlen);
587
0
    return (strncasecmp(suffix, domain, dlen) == 0);
588
0
  }
589
  /* otherwise we must exactly match the constraint */
590
0
  if (dlen != len)
591
0
    return 0;
592
0
  return (strncasecmp(domain, constraint, len) == 0);
593
0
}
594
595
int
596
x509_constraints_uri(uint8_t *uri, size_t ulen, uint8_t *constraint, size_t len,
597
    int *error)
598
0
{
599
0
  int ret = 0;
600
0
  char *hostpart = NULL;
601
602
0
  if (!x509_constraints_uri_host(uri, ulen, &hostpart)) {
603
0
    *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
604
0
    goto err;
605
0
  }
606
0
  if (hostpart == NULL) {
607
0
    *error = X509_V_ERR_OUT_OF_MEM;
608
0
    goto err;
609
0
  }
610
0
  if (!x509_constraints_valid_domain_constraint(constraint, len)) {
611
0
    *error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
612
0
    goto err;
613
0
  }
614
0
  ret = x509_constraints_domain(hostpart, strlen(hostpart), constraint,
615
0
      len);
616
0
 err:
617
0
  free(hostpart);
618
0
  return ret;
619
0
}
620
621
/*
622
 * Verify a validated address of size alen with a validated contraint
623
 * of size constraint_len. returns 1 if matching, 0 if not.
624
 * Addresses are assumed to be pre-validated for a length of 4 and 8
625
 * respectively for ipv4 addreses and constraints, and a length of
626
 * 16 and 32 respectively for ipv6 address constraints by the caller.
627
 */
628
int
629
x509_constraints_ipaddr(uint8_t *address, size_t alen, uint8_t *constraint,
630
    size_t len)
631
0
{
632
0
  uint8_t *mask;
633
0
  size_t i;
634
635
0
  if (alen * 2 != len)
636
0
    return 0;
637
638
0
  mask = constraint + alen;
639
0
  for (i = 0; i < alen; i++) {
640
0
    if ((address[i] & mask[i]) != (constraint[i] & mask[i]))
641
0
      return 0;
642
0
  }
643
0
  return 1;
644
0
}
645
646
/*
647
 * Verify a canonicalized der encoded constraint dirname
648
 * a canonicalized der encoded constraint.
649
 */
650
int
651
x509_constraints_dirname(uint8_t *dirname, size_t dlen,
652
    uint8_t *constraint, size_t len)
653
0
{
654
  /*
655
   * The constraint must be a prefix in DER format, so it can't be
656
   * longer than the name it is checked against.
657
   */
658
0
  if (len > dlen)
659
0
    return 0;
660
0
  return (memcmp(constraint, dirname, len) == 0);
661
0
}
662
663
/*
664
 * De-obfuscate a GENERAL_NAME into useful bytes for a name or constraint.
665
 */
666
int
667
x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes,
668
    size_t *len)
669
0
{
670
0
  *bytes = NULL;
671
0
  *len = 0;
672
673
0
  if (name->type == GEN_DNS) {
674
0
    ASN1_IA5STRING *aname = name->d.dNSName;
675
676
0
    *bytes = aname->data;
677
0
    *len = aname->length;
678
679
0
    return name->type;
680
0
  }
681
0
  if (name->type == GEN_EMAIL) {
682
0
    ASN1_IA5STRING *aname = name->d.rfc822Name;
683
684
0
    *bytes = aname->data;
685
0
    *len = aname->length;
686
687
0
    return name->type;
688
0
  }
689
0
  if (name->type == GEN_URI) {
690
0
    ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier;
691
692
0
    *bytes = aname->data;
693
0
    *len = aname->length;
694
695
0
    return name->type;
696
0
  }
697
0
  if (name->type == GEN_DIRNAME) {
698
0
    X509_NAME *dname = name->d.directoryName;
699
700
0
    if (!dname->modified || i2d_X509_NAME(dname, NULL) >= 0) {
701
0
      *bytes = dname->canon_enc;
702
0
      *len = dname->canon_enclen;
703
704
0
      return name->type;
705
0
    }
706
0
  }
707
0
  if (name->type == GEN_IPADD) {
708
0
    *bytes = name->d.ip->data;
709
0
    *len = name->d.ip->length;
710
711
0
    return name->type;
712
0
  }
713
714
0
  return 0;
715
0
}
716
717
718
/*
719
 * Extract the relevant names for constraint checking from "cert",
720
 * validate them, and add them to the list of cert names for "chain".
721
 * returns 1 on success sets error and returns 0 on failure.
722
 */
723
int
724
x509_constraints_extract_names(struct x509_constraints_names *names,
725
    X509 *cert, int is_leaf, int *error)
726
0
{
727
0
  struct x509_constraints_name *vname = NULL;
728
0
  X509_NAME *subject_name;
729
0
  GENERAL_NAME *name;
730
0
  ssize_t i = 0;
731
0
  int name_type, include_cn = is_leaf, include_email = is_leaf;
732
733
  /* first grab the altnames */
734
0
  while ((name = sk_GENERAL_NAME_value(cert->altname, i++)) != NULL) {
735
0
    uint8_t *bytes = NULL;
736
0
    size_t len = 0;
737
738
0
    if ((vname = x509_constraints_name_new()) == NULL) {
739
0
      *error = X509_V_ERR_OUT_OF_MEM;
740
0
      goto err;
741
0
    }
742
743
0
    name_type = x509_constraints_general_to_bytes(name, &bytes,
744
0
        &len);
745
0
    switch(name_type) {
746
0
    case GEN_DNS:
747
0
      if (!x509_constraints_valid_sandns(bytes, len)) {
748
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
749
0
        goto err;
750
0
      }
751
0
      if ((vname->name = strndup(bytes, len)) == NULL) {
752
0
        *error = X509_V_ERR_OUT_OF_MEM;
753
0
        goto err;
754
0
      }
755
0
      vname->type = GEN_DNS;
756
0
      include_cn = 0; /* don't use cn from subject */
757
0
      break;
758
0
    case GEN_EMAIL:
759
0
      if (!x509_constraints_parse_mailbox(bytes, len,
760
0
          vname)) {
761
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
762
0
        goto err;
763
0
      }
764
0
      vname->type = GEN_EMAIL;
765
0
      include_email = 0; /* don't use email from subject */
766
0
      break;
767
0
    case GEN_URI:
768
0
      if (!x509_constraints_uri_host(bytes, len, &vname->name)) {
769
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
770
0
        goto err;
771
0
      }
772
0
      if (vname->name == NULL) {
773
0
        *error = X509_V_ERR_OUT_OF_MEM;
774
0
        goto err;
775
0
      }
776
0
      vname->type = GEN_URI;
777
0
      break;
778
0
    case GEN_DIRNAME:
779
0
      if (len == 0) {
780
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
781
0
        goto err;
782
0
      }
783
0
      if (bytes == NULL || ((vname->der = malloc(len)) ==
784
0
          NULL)) {
785
0
        *error = X509_V_ERR_OUT_OF_MEM;
786
0
        goto err;
787
0
      }
788
0
      memcpy(vname->der, bytes, len);
789
0
      vname->der_len = len;
790
0
      vname->type = GEN_DIRNAME;
791
0
      break;
792
0
    case GEN_IPADD:
793
0
      if (len == 4)
794
0
        vname->af = AF_INET;
795
0
      if (len == 16)
796
0
        vname->af = AF_INET6;
797
0
      if (vname->af != AF_INET && vname->af != AF_INET6) {
798
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
799
0
        goto err;
800
0
      }
801
0
      memcpy(vname->address, bytes, len);
802
0
      vname->type = GEN_IPADD;
803
0
      break;
804
0
    default:
805
      /* Ignore this name */
806
0
      x509_constraints_name_free(vname);
807
0
      vname = NULL;
808
0
      continue;
809
0
    }
810
0
    if (!x509_constraints_names_add(names, vname)) {
811
0
      *error = X509_V_ERR_OUT_OF_MEM;
812
0
      goto err;
813
0
    }
814
0
    vname = NULL;
815
0
  }
816
817
0
  x509_constraints_name_free(vname);
818
0
  vname = NULL;
819
820
0
  subject_name = X509_get_subject_name(cert);
821
0
  if (X509_NAME_entry_count(subject_name) > 0) {
822
0
    X509_NAME_ENTRY *email;
823
0
    X509_NAME_ENTRY *cn;
824
    /*
825
     * This cert has a non-empty subject, so we must add
826
     * the subject as a dirname to be compared against
827
     * any dirname constraints
828
     */
829
0
    if ((subject_name->modified &&
830
0
        i2d_X509_NAME(subject_name, NULL) < 0) ||
831
0
        (vname = x509_constraints_name_new()) == NULL ||
832
0
        (vname->der = malloc(subject_name->canon_enclen)) == NULL) {
833
0
      *error = X509_V_ERR_OUT_OF_MEM;
834
0
      goto err;
835
0
    }
836
837
0
    memcpy(vname->der, subject_name->canon_enc,
838
0
        subject_name->canon_enclen);
839
0
    vname->der_len = subject_name->canon_enclen;
840
0
    vname->type = GEN_DIRNAME;
841
0
    if (!x509_constraints_names_add(names, vname)) {
842
0
      *error = X509_V_ERR_OUT_OF_MEM;
843
0
      goto err;
844
0
    }
845
0
    vname = NULL;
846
    /*
847
     * Get any email addresses from the subject line, and
848
     * add them as mbox names to be compared against any
849
     * email constraints
850
     */
851
0
    while (include_email &&
852
0
        (i = X509_NAME_get_index_by_NID(subject_name,
853
0
        NID_pkcs9_emailAddress, i)) >= 0) {
854
0
      ASN1_STRING *aname;
855
0
      if ((email = X509_NAME_get_entry(subject_name, i)) == NULL ||
856
0
          (aname = X509_NAME_ENTRY_get_data(email)) == NULL) {
857
0
        *error = X509_V_ERR_OUT_OF_MEM;
858
0
        goto err;
859
0
      }
860
0
      if ((vname = x509_constraints_name_new()) == NULL) {
861
0
        *error = X509_V_ERR_OUT_OF_MEM;
862
0
        goto err;
863
0
      }
864
0
      if (!x509_constraints_parse_mailbox(aname->data,
865
0
          aname->length, vname)) {
866
0
        *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
867
0
        goto err;
868
0
      }
869
0
      vname->type = GEN_EMAIL;
870
0
      if (!x509_constraints_names_add(names, vname)) {
871
0
        *error = X509_V_ERR_OUT_OF_MEM;
872
0
        goto err;
873
0
      }
874
0
      vname = NULL;
875
0
    }
876
    /*
877
     * Include the CN as a hostname to be checked againt
878
     * name constraints if it looks like a hostname.
879
     */
880
0
    while (include_cn &&
881
0
        (i = X509_NAME_get_index_by_NID(subject_name,
882
0
        NID_commonName, i)) >= 0) {
883
0
      ASN1_STRING *aname;
884
0
      if ((cn = X509_NAME_get_entry(subject_name, i)) == NULL ||
885
0
          (aname = X509_NAME_ENTRY_get_data(cn)) == NULL) {
886
0
        *error = X509_V_ERR_OUT_OF_MEM;
887
0
        goto err;
888
0
      }
889
0
      if (!x509_constraints_valid_host(aname->data,
890
0
          aname->length))
891
0
        continue; /* ignore it if not a hostname */
892
0
      if ((vname = x509_constraints_name_new()) == NULL) {
893
0
        *error = X509_V_ERR_OUT_OF_MEM;
894
0
        goto err;
895
0
      }
896
0
      if ((vname->name = strndup(aname->data,
897
0
          aname->length)) == NULL) {
898
0
        *error = X509_V_ERR_OUT_OF_MEM;
899
0
        goto err;
900
0
      }
901
0
      vname->type = GEN_DNS;
902
0
      if (!x509_constraints_names_add(names, vname)) {
903
0
        *error = X509_V_ERR_OUT_OF_MEM;
904
0
        goto err;
905
0
      }
906
0
      vname = NULL;
907
0
    }
908
0
  }
909
0
  return 1;
910
0
 err:
911
0
  x509_constraints_name_free(vname);
912
0
  return 0;
913
0
}
914
915
/*
916
 * Validate a constraint in a general name, putting the relevant data
917
 * into "name" if valid. returns 0, and sets error if the constraint is
918
 * not valid. returns 1 if the constraint validated. name->type will be
919
 * set to a valid type if there is constraint data in name, or unmodified
920
 * if the GENERAL_NAME had a valid type but was ignored.
921
 */
922
int
923
x509_constraints_validate(GENERAL_NAME *constraint,
924
    struct x509_constraints_name **out_name, int *out_error)
925
0
{
926
0
  uint8_t *bytes = NULL;
927
0
  size_t len = 0;
928
0
  struct x509_constraints_name *name;
929
0
  int error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX;
930
0
  int name_type;
931
932
0
  if (out_name == NULL || *out_name != NULL)
933
0
    return 0;
934
935
0
  if (out_error != NULL)
936
0
    *out_error = 0;
937
938
0
  if ((name = x509_constraints_name_new()) == NULL) {
939
0
    error = X509_V_ERR_OUT_OF_MEM;
940
0
    goto err;
941
0
  }
942
943
0
  name_type = x509_constraints_general_to_bytes(constraint, &bytes, &len);
944
0
  switch (name_type) {
945
0
  case GEN_DIRNAME:
946
0
    if (len == 0)
947
0
      goto err; /* XXX The RFCs are delightfully vague */
948
0
    if (bytes == NULL || (name->der = malloc(len)) == NULL) {
949
0
      error = X509_V_ERR_OUT_OF_MEM;
950
0
      goto err;
951
0
    }
952
0
    memcpy(name->der, bytes, len);
953
0
    name->der_len = len;
954
0
    name->type = GEN_DIRNAME;
955
0
    break;
956
0
  case GEN_DNS:
957
0
    if (!x509_constraints_valid_domain_constraint(bytes, len))
958
0
      goto err;
959
0
    if ((name->name = strndup(bytes, len)) == NULL) {
960
0
      error = X509_V_ERR_OUT_OF_MEM;
961
0
      goto err;
962
0
    }
963
0
    name->type = GEN_DNS;
964
0
    break;
965
0
  case GEN_EMAIL:
966
0
    if (len > 0 && memchr(bytes + 1, '@', len - 1) != NULL) {
967
0
      if (!x509_constraints_parse_mailbox(bytes, len, name))
968
0
        goto err;
969
0
      break;
970
0
    }
971
    /*
972
     * Mail constraints of the form @domain.com are accepted by
973
     * OpenSSL and Microsoft.
974
     */
975
0
    if (len > 0 && bytes[0] == '@') {
976
0
      bytes++;
977
0
      len--;
978
0
    }
979
0
    if (!x509_constraints_valid_domain_constraint(bytes, len))
980
0
      goto err;
981
0
    if ((name->name = strndup(bytes, len)) == NULL) {
982
0
      error = X509_V_ERR_OUT_OF_MEM;
983
0
      goto err;
984
0
    }
985
0
    name->type = GEN_EMAIL;
986
0
    break;
987
0
  case GEN_IPADD:
988
    /* Constraints are ip then mask */
989
0
    if (len == 8)
990
0
      name->af = AF_INET;
991
0
    else if (len == 32)
992
0
      name->af = AF_INET6;
993
0
    else
994
0
      goto err;
995
0
    memcpy(&name->address[0], bytes, len);
996
0
    name->type = GEN_IPADD;
997
0
    break;
998
0
  case GEN_URI:
999
0
    if (!x509_constraints_valid_domain_constraint(bytes, len))
1000
0
      goto err;
1001
0
    if ((name->name = strndup(bytes, len)) == NULL) {
1002
0
      error = X509_V_ERR_OUT_OF_MEM;
1003
0
      goto err;
1004
0
    }
1005
0
    name->type = GEN_URI;
1006
0
    break;
1007
0
  default:
1008
0
    break;
1009
0
  }
1010
1011
0
  *out_name = name;
1012
1013
0
  return 1;
1014
1015
0
 err:
1016
0
  x509_constraints_name_free(name);
1017
0
  if (out_error != NULL)
1018
0
    *out_error = error;
1019
1020
0
  return 0;
1021
0
}
1022
1023
int
1024
x509_constraints_extract_constraints(X509 *cert,
1025
    struct x509_constraints_names *permitted,
1026
    struct x509_constraints_names *excluded,
1027
    int *error)
1028
0
{
1029
0
  struct x509_constraints_name *vname = NULL;
1030
0
  NAME_CONSTRAINTS *nc = cert->nc;
1031
0
  GENERAL_SUBTREE *subtree;
1032
0
  int i;
1033
1034
0
  if (nc == NULL)
1035
0
    return 1;
1036
1037
0
  for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
1038
1039
0
    subtree = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
1040
0
    if (subtree->minimum || subtree->maximum) {
1041
0
      *error = X509_V_ERR_SUBTREE_MINMAX;
1042
0
      return 0;
1043
0
    }
1044
0
    if (!x509_constraints_validate(subtree->base, &vname, error))
1045
0
      return 0;
1046
0
    if (vname->type == 0) {
1047
0
      x509_constraints_name_free(vname);
1048
0
      vname = NULL;
1049
0
      continue;
1050
0
    }
1051
0
    if (!x509_constraints_names_add(permitted, vname)) {
1052
0
      x509_constraints_name_free(vname);
1053
0
      vname = NULL;
1054
0
      *error = X509_V_ERR_OUT_OF_MEM;
1055
0
      return 0;
1056
0
    }
1057
0
    vname = NULL;
1058
0
  }
1059
1060
0
  for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
1061
0
    subtree = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
1062
0
    if (subtree->minimum || subtree->maximum) {
1063
0
      *error = X509_V_ERR_SUBTREE_MINMAX;
1064
0
      return 0;
1065
0
    }
1066
0
    if (!x509_constraints_validate(subtree->base, &vname, error))
1067
0
      return 0;
1068
0
    if (vname->type == 0) {
1069
0
      x509_constraints_name_free(vname);
1070
0
      vname = NULL;
1071
0
      continue;
1072
0
    }
1073
0
    if (!x509_constraints_names_add(excluded, vname)) {
1074
0
      x509_constraints_name_free(vname);
1075
0
      vname = NULL;
1076
0
      *error = X509_V_ERR_OUT_OF_MEM;
1077
0
      return 0;
1078
0
    }
1079
0
    vname = NULL;
1080
0
  }
1081
1082
0
  return 1;
1083
0
}
1084
1085
/*
1086
 * Match a validated name in "name" against a validated constraint in
1087
 * "constraint" return 1 if then name matches, 0 otherwise.
1088
 */
1089
int
1090
x509_constraints_match(struct x509_constraints_name *name,
1091
    struct x509_constraints_name *constraint)
1092
0
{
1093
0
  if (name->type != constraint->type)
1094
0
    return 0;
1095
0
  if (name->type == GEN_DNS)
1096
0
    return x509_constraints_sandns(name->name, strlen(name->name),
1097
0
        constraint->name, strlen(constraint->name));
1098
0
  if (name->type == GEN_URI)
1099
0
    return x509_constraints_domain(name->name, strlen(name->name),
1100
0
        constraint->name, strlen(constraint->name));
1101
0
  if (name->type == GEN_IPADD) {
1102
0
    size_t nlen = name->af == AF_INET ? 4 : 16;
1103
0
    size_t clen = name->af == AF_INET ? 8 : 32;
1104
0
    if (name->af != AF_INET && name->af != AF_INET6)
1105
0
      return 0;
1106
0
    if (constraint->af != AF_INET && constraint->af != AF_INET6)
1107
0
      return 0;
1108
0
    if (name->af != constraint->af)
1109
0
      return 0;
1110
0
    return x509_constraints_ipaddr(name->address, nlen,
1111
0
        constraint->address, clen);
1112
0
  }
1113
0
  if (name->type == GEN_EMAIL) {
1114
0
    if (constraint->local) {
1115
      /* mailbox local and domain parts must exactly match */
1116
0
      return (strcmp(name->local, constraint->local) == 0 &&
1117
0
          strcmp(name->name, constraint->name) == 0);
1118
0
    }
1119
    /* otherwise match the constraint to the domain part */
1120
0
    return x509_constraints_domain(name->name, strlen(name->name),
1121
0
        constraint->name, strlen(constraint->name));
1122
0
  }
1123
0
  if (name->type == GEN_DIRNAME)
1124
0
    return x509_constraints_dirname(name->der, name->der_len,
1125
0
        constraint->der, constraint->der_len);
1126
0
  return 0;
1127
0
}
1128
1129
/*
1130
 * Make sure every name in names does not match any excluded
1131
 * constraints, and does match at least one permitted constraint if
1132
 * any are present. Returns 1 if ok, 0, and sets error if not.
1133
 */
1134
int
1135
x509_constraints_check(struct x509_constraints_names *names,
1136
    struct x509_constraints_names *permitted,
1137
    struct x509_constraints_names *excluded, int *error)
1138
0
{
1139
0
  size_t i, j;
1140
1141
0
  for (i = 0; i < names->names_count; i++) {
1142
0
    int permitted_seen = 0;
1143
0
    int permitted_matched = 0;
1144
1145
0
    for (j = 0; j < excluded->names_count; j++) {
1146
0
      if (x509_constraints_match(names->names[i],
1147
0
          excluded->names[j])) {
1148
0
        *error = X509_V_ERR_EXCLUDED_VIOLATION;
1149
0
        return 0;
1150
0
      }
1151
0
    }
1152
0
    for (j = 0; j < permitted->names_count; j++) {
1153
0
      if (permitted->names[j]->type == names->names[i]->type)
1154
0
        permitted_seen++;
1155
0
      if (x509_constraints_match(names->names[i],
1156
0
          permitted->names[j])) {
1157
0
        permitted_matched++;
1158
0
        break;
1159
0
      }
1160
0
    }
1161
0
    if (permitted_seen && !permitted_matched) {
1162
0
      *error = X509_V_ERR_PERMITTED_VIOLATION;
1163
0
      return 0;
1164
0
    }
1165
0
  }
1166
0
  return 1;
1167
0
}
1168
1169
/*
1170
 * Walk a validated chain of X509 certs, starting at the leaf, and
1171
 * validate the name constraints in the chain. Intended for use with
1172
 * the legacy X509 validtion code in x509_vfy.c
1173
 *
1174
 * returns 1 if the constraints are ok, 0 otherwise, setting error and
1175
 * depth
1176
 */
1177
int
1178
x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth)
1179
0
{
1180
0
  int chain_length, verify_err = X509_V_ERR_UNSPECIFIED, i = 0;
1181
0
  struct x509_constraints_names *names = NULL;
1182
0
  struct x509_constraints_names *excluded = NULL;
1183
0
  struct x509_constraints_names *permitted = NULL;
1184
0
  size_t constraints_count = 0;
1185
0
  X509 *cert;
1186
1187
0
  if (chain == NULL || (chain_length = sk_X509_num(chain)) == 0)
1188
0
    goto err;
1189
0
  if (chain_length == 1)
1190
0
    return 1;
1191
0
  if ((names = x509_constraints_names_new(
1192
0
      X509_VERIFY_MAX_CHAIN_NAMES)) == NULL) {
1193
0
    verify_err = X509_V_ERR_OUT_OF_MEM;
1194
0
    goto err;
1195
0
  }
1196
1197
0
  if ((cert = sk_X509_value(chain, 0)) == NULL)
1198
0
    goto err;
1199
0
  if (!x509_constraints_extract_names(names, cert, 1, &verify_err))
1200
0
    goto err;
1201
0
  for (i = 1; i < chain_length; i++) {
1202
0
    if ((cert = sk_X509_value(chain, i)) == NULL)
1203
0
      goto err;
1204
0
    if (cert->nc != NULL) {
1205
0
      if ((permitted = x509_constraints_names_new(
1206
0
          X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
1207
0
        verify_err = X509_V_ERR_OUT_OF_MEM;
1208
0
        goto err;
1209
0
      }
1210
0
      if ((excluded = x509_constraints_names_new(
1211
0
          X509_VERIFY_MAX_CHAIN_CONSTRAINTS)) == NULL) {
1212
0
        verify_err = X509_V_ERR_OUT_OF_MEM;
1213
0
        goto err;
1214
0
      }
1215
0
      if (!x509_constraints_extract_constraints(cert,
1216
0
          permitted, excluded, &verify_err))
1217
0
        goto err;
1218
0
      constraints_count += permitted->names_count;
1219
0
      constraints_count += excluded->names_count;
1220
0
      if (constraints_count >
1221
0
          X509_VERIFY_MAX_CHAIN_CONSTRAINTS) {
1222
0
        verify_err = X509_V_ERR_OUT_OF_MEM;
1223
0
        goto err;
1224
0
      }
1225
0
      if (!x509_constraints_check(names, permitted, excluded,
1226
0
          &verify_err))
1227
0
        goto err;
1228
0
      x509_constraints_names_free(excluded);
1229
0
      excluded = NULL;
1230
0
      x509_constraints_names_free(permitted);
1231
0
      permitted = NULL;
1232
0
    }
1233
0
    if (!x509_constraints_extract_names(names, cert, 0,
1234
0
        &verify_err))
1235
0
      goto err;
1236
0
  }
1237
1238
0
  x509_constraints_names_free(names);
1239
0
  return 1;
1240
1241
0
 err:
1242
0
  *error = verify_err;
1243
0
  *depth = i;
1244
0
  x509_constraints_names_free(excluded);
1245
0
  x509_constraints_names_free(permitted);
1246
0
  x509_constraints_names_free(names);
1247
0
  return 0;
1248
0
}