x509parse.c revision 362181
1/*
2 *  X.509 certificate and private key decoding
3 *
4 *  Based on XySSL: Copyright (C) 2006-2008   Christophe Devine
5 *
6 *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
7 *
8 *  All rights reserved.
9 *
10 *  Redistribution and use in source and binary forms, with or without
11 *  modification, are permitted provided that the following conditions
12 *  are met:
13 *
14 *    * Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 *    * Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*
36 *  The ITU-T X.509 standard defines a certificate format for PKI.
37 *
38 *  http://www.ietf.org/rfc/rfc5280.txt
39 *  http://www.ietf.org/rfc/rfc3279.txt
40 *  http://www.ietf.org/rfc/rfc6818.txt
41 *
42 *  ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
43 *
44 *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
45 *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
46 */
47
48#include <apr_pools.h>
49#include <apr_tables.h>
50#include "svn_hash.h"
51#include "svn_string.h"
52#include "svn_time.h"
53#include "svn_checksum.h"
54#include "svn_utf.h"
55#include "svn_ctype.h"
56#include "private/svn_utf_private.h"
57#include "private/svn_string_private.h"
58
59#include "x509.h"
60
61#include <string.h>
62#include <stdio.h>
63
64/*
65 * ASN.1 DER decoding routines
66 */
67static svn_error_t *
68asn1_get_len(const unsigned char **p, const unsigned char *end,
69             ptrdiff_t *len)
70{
71  if ((end - *p) < 1)
72    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
73
74  if ((**p & 0x80) == 0)
75    *len = *(*p)++;
76  else
77    switch (**p & 0x7F)
78      {
79      case 1:
80        if ((end - *p) < 2)
81          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
82
83        *len = (*p)[1];
84        (*p) += 2;
85        break;
86
87      case 2:
88        if ((end - *p) < 3)
89          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
90
91        *len = ((*p)[1] << 8) | (*p)[2];
92        (*p) += 3;
93        break;
94
95      default:
96        return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
97        break;
98      }
99
100  if (*len > (end - *p))
101    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
102
103  return SVN_NO_ERROR;
104}
105
106static svn_error_t *
107asn1_get_tag(const unsigned char **p,
108             const unsigned char *end, ptrdiff_t *len, int tag)
109{
110  if ((end - *p) < 1)
111    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
112
113  if (**p != tag)
114    return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
115
116  (*p)++;
117
118  return svn_error_trace(asn1_get_len(p, end, len));
119}
120
121static svn_error_t *
122asn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
123{
124  ptrdiff_t len;
125
126  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
127
128  /* Reject bit patterns that would overflow the output and those that
129     represent negative values. */
130  if (len > (int)sizeof(int) || (**p & 0x80) != 0)
131    return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
132
133  *val = 0;
134
135  while (len-- > 0) {
136    /* This would be undefined for bit-patterns of negative values. */
137    *val = (*val << 8) | **p;
138    (*p)++;
139  }
140
141  return SVN_NO_ERROR;
142}
143
144static svn_boolean_t
145equal(const void *left, apr_size_t left_len,
146      const void *right, apr_size_t right_len)
147{
148  if (left_len != right_len)
149    return FALSE;
150
151  return memcmp(left, right, right_len) == 0;
152}
153
154static svn_boolean_t
155oids_equal(x509_buf *left, x509_buf *right)
156{
157  return equal(left->p, left->len,
158               right->p, right->len);
159}
160
161/*
162 *  Version   ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
163 */
164static svn_error_t *
165x509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
166{
167  svn_error_t *err;
168  ptrdiff_t len;
169
170  /*
171   * As defined in the Basic Certificate fields:
172   *   version         [0]  EXPLICIT Version DEFAULT v1,
173   * the version is the context specific tag 0.
174   */
175  err = asn1_get_tag(p, end, &len,
176                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
177  if (err)
178    {
179      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
180        {
181          svn_error_clear(err);
182          *ver = 0;
183          return SVN_NO_ERROR;
184        }
185
186      return svn_error_trace(err);
187    }
188
189  end = *p + len;
190
191  err = asn1_get_int(p, end, ver);
192  if (err)
193    return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
194
195  if (*p != end)
196    {
197      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
198      return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
199    }
200
201  return SVN_NO_ERROR;
202}
203
204/*
205 *  CertificateSerialNumber   ::=  INTEGER
206 */
207static svn_error_t *
208x509_get_serial(const unsigned char **p,
209                const unsigned char *end, x509_buf * serial)
210{
211  svn_error_t *err;
212
213  if ((end - *p) < 1)
214    {
215      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
216      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
217    }
218
219  if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
220      **p != ASN1_INTEGER)
221    {
222      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
223      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
224    }
225
226  serial->tag = *(*p)++;
227
228  err = asn1_get_len(p, end, &serial->len);
229  if (err)
230    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
231
232  serial->p = *p;
233  *p += serial->len;
234
235  return SVN_NO_ERROR;
236}
237
238/*
239 *  AlgorithmIdentifier   ::=  SEQUENCE  {
240 *     algorithm         OBJECT IDENTIFIER,
241 *     parameters        ANY DEFINED BY algorithm OPTIONAL  }
242 */
243static svn_error_t *
244x509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
245{
246  svn_error_t *err;
247  ptrdiff_t len;
248
249  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
250  if (err)
251    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
252
253  end = *p + len;
254  alg->tag = **p;
255
256  err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
257  if (err)
258    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
259
260  alg->p = *p;
261  *p += alg->len;
262
263  if (*p == end)
264    return SVN_NO_ERROR;
265
266  /* The OID encoding of 1.2.840.113549.1.1.10 (id-RSASSA-PSS) */
267#define OID_RSASSA_PSS "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a"
268
269  if (equal(alg->p, alg->len, OID_RSASSA_PSS, sizeof(OID_RSASSA_PSS) - 1))
270    {
271      /* Skip over algorithm parameters for id-RSASSA-PSS (RFC 8017)
272       *
273       * RSASSA-PSS-params ::= SEQUENCE {
274       *  hashAlgorithm      [0] HashAlgorithm    DEFAULT sha1,
275       *  maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
276       *  saltLength         [2] INTEGER          DEFAULT 20,
277       *  trailerField       [3] TrailerField     DEFAULT trailerFieldBC
278       * }
279       */
280      err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
281      if (err)
282        return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
283
284      *p += len;
285    }
286  else
287    {
288      /* Algorithm parameters must be NULL for other algorithms */
289      err = asn1_get_tag(p, end, &len, ASN1_NULL);
290      if (err)
291        return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
292    }
293
294  if (*p != end)
295    {
296      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
297      return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
298    }
299
300  return SVN_NO_ERROR;
301}
302
303/*
304 *  AttributeTypeAndValue ::= SEQUENCE {
305 *    type     AttributeType,
306 *    value     AttributeValue }
307 *
308 *  AttributeType ::= OBJECT IDENTIFIER
309 *
310 *  AttributeValue ::= ANY DEFINED BY AttributeType
311 */
312static svn_error_t *
313x509_get_attribute(const unsigned char **p, const unsigned char *end,
314                   x509_name *cur, apr_pool_t *result_pool)
315{
316  svn_error_t *err;
317  ptrdiff_t len;
318  x509_buf *oid;
319  x509_buf *val;
320
321  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
322  if (err)
323    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
324
325  end = *p + len;
326
327  oid = &cur->oid;
328
329  err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
330  if (err)
331    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
332
333  oid->tag = ASN1_OID;
334  oid->p = *p;
335  *p += oid->len;
336
337  if ((end - *p) < 1)
338    {
339      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
340      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
341    }
342
343  if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
344      **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
345      **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
346    {
347      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
348      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
349    }
350
351  val = &cur->val;
352  val->tag = *(*p)++;
353
354  err = asn1_get_len(p, end, &val->len);
355  if (err)
356    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
357
358  val->p = *p;
359  *p += val->len;
360
361  cur->next = NULL;
362
363  if (*p != end)
364    {
365      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
366      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
367    }
368
369  return SVN_NO_ERROR;
370}
371
372/*
373 *   RelativeDistinguishedName ::=
374 *   SET SIZE (1..MAX) OF AttributeTypeAndValue
375 */
376static svn_error_t *
377x509_get_name(const unsigned char **p, const unsigned char *name_end,
378              x509_name *name, apr_pool_t *result_pool)
379{
380  svn_error_t *err;
381  ptrdiff_t len;
382  const unsigned char *set_end;
383  x509_name *cur = NULL;
384
385  err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET);
386  if (err || len < 1)
387    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
388
389  set_end = *p + len;
390
391  /*
392   * iterate until the end of the SET is reached
393   */
394  while (*p < set_end)
395    {
396      if (!cur)
397        {
398          cur = name;
399        }
400      else
401        {
402          cur->next = apr_palloc(result_pool, sizeof(x509_name));
403          cur = cur->next;
404        }
405      SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool));
406    }
407
408  /*
409   * recurse until end of SEQUENCE (name) is reached
410   */
411  if (*p == name_end)
412    return SVN_NO_ERROR;
413
414  cur->next = apr_palloc(result_pool, sizeof(x509_name));
415
416  return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool));
417}
418
419/* Retrieve the date from the X.509 cert data between *P and END in either
420 * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
421 * 4.1.2.5.2 respectively) and place the result in WHEN using  SCRATCH_POOL
422 * for temporary allocations. */
423static svn_error_t *
424x509_get_date(apr_time_t *when,
425              const unsigned char **p,
426              const unsigned char *end,
427              apr_pool_t *scratch_pool)
428{
429  svn_error_t *err;
430  apr_status_t ret;
431  int tag;
432  ptrdiff_t len;
433  char *date;
434  apr_time_exp_t xt = { 0 };
435  char tz;
436
437  err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
438  if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
439    {
440      svn_error_clear(err);
441      err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
442      tag = ASN1_GENERALIZED_TIME;
443    }
444  else
445    {
446      tag = ASN1_UTC_TIME;
447    }
448  if (err)
449    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
450
451  date = apr_pstrndup(scratch_pool, (const char *) *p, len);
452  switch (tag)
453    {
454    case ASN1_UTC_TIME:
455      if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
456                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
457                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
458        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
459
460      /* UTCTime only provides a 2 digit year.  X.509 specifies that years
461       * greater than or equal to 50 must be interpreted as 19YY and years
462       * less than 50 be interpreted as 20YY.  This format is not used for
463       * years greater than 2049. apr_time_exp_t wants years as the number
464       * of years since 1900, so don't convert to 4 digits here. */
465      xt.tm_year += 100 * (xt.tm_year < 50);
466      break;
467
468    case ASN1_GENERALIZED_TIME:
469      if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
470                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
471                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
472        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
473
474      /* GeneralizedTime has the full 4 digit year.  But apr_time_exp_t
475       * wants years as the number of years since 1900. */
476      xt.tm_year -= 1900;
477      break;
478
479    default:
480      /* shouldn't ever get here because we should error out above in the
481       * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
482      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
483      break;
484    }
485
486  /* check that the timezone is GMT
487   * ASN.1 allows for the timezone to be specified but X.509 says it must
488   * always be GMT.  A little bit of extra paranoia here seems like a good
489   * idea. */
490  if (tz != 'Z')
491    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
492
493  /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
494  xt.tm_mon -= 1;
495
496  /* range checks (as per definition of apr_time_exp_t in apr_time.h) */
497  if (xt.tm_usec < 0 ||
498      xt.tm_sec < 0 || xt.tm_sec > 61 ||
499      xt.tm_min < 0 || xt.tm_min > 59 ||
500      xt.tm_hour < 0 || xt.tm_hour > 23 ||
501      xt.tm_mday < 1 || xt.tm_mday > 31 ||
502      xt.tm_mon < 0 || xt.tm_mon > 11 ||
503      xt.tm_year < 0 ||
504      xt.tm_wday < 0 || xt.tm_wday > 6 ||
505      xt.tm_yday < 0 || xt.tm_yday > 365)
506    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
507
508  ret = apr_time_exp_gmt_get(when, &xt);
509  if (ret)
510    return svn_error_wrap_apr(ret, NULL);
511
512  *p += len;
513
514  return SVN_NO_ERROR;
515}
516
517/*
518 *  Validity ::= SEQUENCE {
519 *     notBefore    Time,
520 *     notAfter    Time }
521 *
522 *  Time ::= CHOICE {
523 *     utcTime    UTCTime,
524 *     generalTime  GeneralizedTime }
525 */
526static svn_error_t *
527x509_get_dates(apr_time_t *from,
528               apr_time_t *to,
529               const unsigned char **p,
530               const unsigned char *end,
531               apr_pool_t *scratch_pool)
532{
533  svn_error_t *err;
534  ptrdiff_t len;
535
536  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
537  if (err)
538    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
539
540  end = *p + len;
541
542  SVN_ERR(x509_get_date(from, p, end, scratch_pool));
543
544  SVN_ERR(x509_get_date(to, p, end, scratch_pool));
545
546  if (*p != end)
547    {
548      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
549      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
550    }
551
552  return SVN_NO_ERROR;
553}
554
555static svn_error_t *
556x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
557{
558  svn_error_t *err;
559  ptrdiff_t len;
560
561  err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
562  if (err)
563    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
564
565  sig->tag = ASN1_BIT_STRING;
566
567  if (--len < 1 || *(*p)++ != 0)
568    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
569
570  sig->len = len;
571  sig->p = *p;
572
573  *p += len;
574
575  return SVN_NO_ERROR;
576}
577
578/*
579 * X.509 v2/v3 unique identifier (not parsed)
580 */
581static svn_error_t *
582x509_get_uid(const unsigned char **p,
583             const unsigned char *end, x509_buf * uid, int n)
584{
585  svn_error_t *err;
586
587  if (*p == end)
588    return SVN_NO_ERROR;
589
590  err = asn1_get_tag(p, end, &uid->len,
591                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
592  if (err)
593    {
594      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
595        {
596          svn_error_clear(err);
597          return SVN_NO_ERROR;
598        }
599
600      return svn_error_trace(err);
601    }
602
603  uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n;
604  uid->p = *p;
605  *p += uid->len;
606
607  return SVN_NO_ERROR;
608}
609
610/*
611 * X.509 v3 extensions (not parsed)
612 */
613static svn_error_t *
614x509_get_ext(apr_array_header_t *dnsnames,
615             const unsigned char **p,
616             const unsigned char *end)
617{
618  svn_error_t *err;
619  ptrdiff_t len;
620
621  if (*p == end)
622    return SVN_NO_ERROR;
623
624  err = asn1_get_tag(p, end, &len,
625                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
626  if (err)
627    {
628      /* If there aren't extensions that's ok they aren't required */
629      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
630        {
631          svn_error_clear(err);
632          return SVN_NO_ERROR;
633        }
634
635      return svn_error_trace(err);
636    }
637
638  end = *p + len;
639
640  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
641
642  if (end != *p + len)
643    {
644      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
645      return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
646    }
647
648  while (*p < end)
649    {
650      ptrdiff_t ext_len;
651      const unsigned char *ext_start, *sna_end;
652      err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
653      if (err)
654        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
655                                NULL);
656      ext_start = *p;
657
658      err = asn1_get_tag(p, end, &len, ASN1_OID);
659      if (err)
660        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
661                                NULL);
662
663      /* skip all extensions except SubjectAltName */
664      if (!equal(*p, len,
665                 OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1))
666        {
667          *p += ext_len - (*p - ext_start);
668          continue;
669        }
670      *p += len;
671
672      err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
673      if (err)
674        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
675                                NULL);
676
677      /*   SubjectAltName ::= GeneralNames
678
679           GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
680
681           GeneralName ::= CHOICE {
682                other Name                      [0]     OtherName,
683                rfc822Name                      [1]     IA5String,
684                dNSName                         [2]     IA5String,
685                x400Address                     [3]     ORAddress,
686                directoryName                   [4]     Name,
687                ediPartyName                    [5]     EDIPartyName,
688                uniformResourceIdentifier       [6]     IA5String,
689                iPAddress                       [7]     OCTET STRING,
690                registeredID                    [8]     OBJECT IDENTIFIER } */
691      sna_end = *p + len;
692
693      err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
694      if (err)
695        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
696                                NULL);
697
698      if (sna_end != *p + len)
699        {
700          err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
701          return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
702        }
703
704      while (*p < sna_end)
705        {
706          err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
707                             ASN1_PRIMITIVE | 2);
708          if (err)
709            {
710              /* not not a dNSName */
711              if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
712                {
713                  svn_error_clear(err);
714                  /* need to skip the tag and then find the length to
715                   * skip to ignore this SNA entry. */
716                  (*p)++;
717                  SVN_ERR(asn1_get_len(p, sna_end, &len));
718                  *p += len;
719                  continue;
720                }
721
722              return svn_error_trace(err);
723            }
724          else
725            {
726              /* We found a dNSName entry */
727              x509_buf *dnsname = apr_palloc(dnsnames->pool, sizeof(*dnsname));
728              dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
729              dnsname->len = len;
730              dnsname->p = *p;
731              APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
732            }
733
734          *p += len;
735        }
736
737    }
738
739  return SVN_NO_ERROR;
740}
741
742/* Escape all non-ascii or control characters similar to
743 * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy().
744 * All of the encoding formats somewhat overlap with ascii (BMPString
745 * and UniversalString are actually always wider so you'll end up
746 * with a bunch of escaped nul bytes, but ideally we don't get here
747 * for those).  The result is always a nul-terminated C string. */
748static const char *
749fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
750{
751  const char *end = src->data + src->len;
752  const char *p = src->data, *q;
753  svn_stringbuf_t *outstr;
754  char escaped_char[6]; /* ? \ u u u \0 */
755
756  for (q = p; q < end; q++)
757    {
758      if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
759        break;
760    }
761
762  if (q == end)
763    return src->data;
764
765  outstr = svn_stringbuf_create_empty(result_pool);
766  while (1)
767    {
768      q = p;
769
770      /* Traverse till either unsafe character or eos. */
771      while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
772        q++;
773
774      /* copy chunk before marker */
775      svn_stringbuf_appendbytes(outstr, p, q - p);
776
777      if (q == end)
778        break;
779
780      apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
781                   (unsigned char) *q);
782      svn_stringbuf_appendcstr(outstr, escaped_char);
783
784      p = q + 1;
785    }
786
787  return outstr->data;
788}
789
790/* Escape only NUL characters from a string that is presumed to
791 * be UTF-8 encoded and return a nul-terminated C string. */
792static const char *
793nul_escape(const svn_string_t *src, apr_pool_t *result_pool)
794{
795  const char *end = src->data + src->len;
796  const char *p = src->data, *q;
797  svn_stringbuf_t *outstr;
798
799  for (q = p; q < end; q++)
800    {
801      if (*q == '\0')
802        break;
803    }
804
805  if (q == end)
806    return src->data;
807
808  outstr = svn_stringbuf_create_empty(result_pool);
809  while (1)
810    {
811      q = p;
812
813      /* Traverse till either unsafe character or eos. */
814      while (q < end && *q != '\0')
815        q++;
816
817      /* copy chunk before marker */
818      svn_stringbuf_appendbytes(outstr, p, q - p);
819
820      if (q == end)
821        break;
822
823      svn_stringbuf_appendcstr(outstr, "?\\000");
824
825      p = q + 1;
826    }
827
828  return outstr->data;
829}
830
831
832/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
833   ISO-8859-1 is a strict subset of Unicode. */
834static svn_error_t *
835latin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
836               apr_pool_t *result_pool)
837{
838  apr_int32_t *ucs4buf;
839  svn_membuf_t resultbuf;
840  apr_size_t length;
841  apr_size_t i;
842  svn_string_t *res;
843
844  ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf));
845  for (i = 0; i < src->len; ++i)
846    ucs4buf[i] = (unsigned char)(src->data[i]);
847
848  svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
849  SVN_ERR(svn_utf__encode_ucs4_string(
850              &resultbuf, ucs4buf, src->len, &length));
851
852  res = apr_palloc(result_pool, sizeof(*res));
853  res->data = resultbuf.data;
854  res->len = length;
855  *result = res;
856  return SVN_NO_ERROR;
857}
858
859/* Make a best effort to convert a X.509 name to a UTF-8 encoded
860 * string and return it.  If we can't properly convert just do a
861 * fuzzy conversion so we have something to display. */
862static const char *
863x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
864{
865  const svn_string_t *src_string;
866  const svn_string_t *utf8_string;
867  svn_error_t *err;
868
869  src_string = svn_string_ncreate((const char *)name->val.p,
870                                  name->val.len,
871                                  result_pool);
872  switch (name->val.tag)
873    {
874    case ASN1_UTF8_STRING:
875      if (svn_utf__is_valid(src_string->data, src_string->len))
876        return nul_escape(src_string, result_pool);
877      else
878        /* not a valid UTF-8 string, who knows what it is,
879         * so run it through the fuzzy_escape code.  */
880        return fuzzy_escape(src_string, result_pool);
881      break;
882
883      /* Both BMP and UNIVERSAL should always be in Big Endian (aka
884       * network byte order).  But rumor has it that there are certs
885       * out there with other endianess and even Byte Order Marks.
886       * If we actually run into these, we might need to do something
887       * about it. */
888
889    case ASN1_BMP_STRING:
890      if (0 != src_string->len % sizeof(apr_uint16_t))
891          return fuzzy_escape(src_string, result_pool);
892      err = svn_utf__utf16_to_utf8(&utf8_string,
893                                   (const void*)(src_string->data),
894                                   src_string->len / sizeof(apr_uint16_t),
895                                   TRUE, result_pool, result_pool);
896      break;
897
898    case ASN1_UNIVERSAL_STRING:
899      if (0 != src_string->len % sizeof(apr_int32_t))
900          return fuzzy_escape(src_string, result_pool);
901      err = svn_utf__utf32_to_utf8(&utf8_string,
902                                   (const void*)(src_string->data),
903                                   src_string->len / sizeof(apr_int32_t),
904                                   TRUE, result_pool, result_pool);
905      break;
906
907      /* Despite what all the IETF, ISO, ITU bits say everything out
908       * on the Internet that I can find treats this as ISO-8859-1.
909       * Even the name is misleading, it's not actually T.61.  All the
910       * gory details can be found in the Character Sets section of:
911       * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt
912       */
913    case ASN1_T61_STRING:
914      err = latin1_to_utf8(&utf8_string, src_string, result_pool);
915      break;
916
917      /* This leaves two types out there in the wild.  PrintableString,
918       * which is just a subset of ASCII and IA5 which is ASCII (though
919       * 0x24 '$' and 0x23 '#' may be defined with differnet symbols
920       * depending on the location, in practice it seems everyone just
921       * treats it as ASCII).  Since these are just ASCII run through
922       * the fuzzy_escape code to deal with anything that isn't actually
923       * ASCII.  There shouldn't be any other types here but if we find
924       * a cert with some other encoding, the best we can do is the
925       * fuzzy_escape().  Note: Technically IA5 isn't valid in this
926       * context, however in the real world it may pop up. */
927    default:
928      return fuzzy_escape(src_string, result_pool);
929    }
930
931  if (err)
932    {
933      svn_error_clear(err);
934      return fuzzy_escape(src_string, result_pool);
935    }
936
937  return nul_escape(utf8_string, result_pool);
938}
939
940static svn_error_t *
941x509_name_to_certinfo(apr_array_header_t **result,
942                      const x509_name *dn,
943                      apr_pool_t *scratch_pool,
944                      apr_pool_t *result_pool)
945{
946  const x509_name *name = dn;
947
948  *result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *));
949
950  while (name != NULL) {
951    svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t));
952
953    attr->oid_len = name->oid.len;
954    attr->oid = apr_pmemdup(result_pool, name->oid.p, attr->oid_len);
955    attr->utf8_value = x509name_to_utf8_string(name, result_pool);
956    if (!attr->utf8_value)
957      /* this should never happen */
958      attr->utf8_value = apr_pstrdup(result_pool, "??");
959    APR_ARRAY_PUSH(*result, const svn_x509_name_attr_t *) = attr;
960
961    name = name->next;
962  }
963
964  return SVN_NO_ERROR;
965}
966
967static svn_boolean_t
968is_hostname(const char *str)
969{
970  apr_size_t i, len = strlen(str);
971
972  for (i = 0; i < len; i++)
973    {
974      char c = str[i];
975
976      /* '-' is only legal when not at the start or end of a label */
977      if (c == '-')
978        {
979          if (i + 1 != len)
980            {
981              if (str[i + 1] == '.')
982                return FALSE; /* '-' preceeds a '.' */
983            }
984          else
985            return FALSE; /* '-' is at end of string */
986
987          /* determine the previous character. */
988          if (i == 0)
989            return FALSE; /* '-' is at start of string */
990          else
991            if (str[i - 1] == '.')
992              return FALSE; /* '-' follows a '.' */
993        }
994      else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
995        return FALSE; /* some character not allowed */
996    }
997
998  return TRUE;
999}
1000
1001static const char *
1002x509parse_get_cn(apr_array_header_t *subject)
1003{
1004  int i;
1005
1006  for (i = 0; i < subject->nelts; ++i)
1007    {
1008      const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(subject, i, const svn_x509_name_attr_t *);
1009      if (equal(attr->oid, attr->oid_len,
1010                SVN_X509_OID_COMMON_NAME, sizeof(SVN_X509_OID_COMMON_NAME) - 1))
1011        return attr->utf8_value;
1012    }
1013
1014  return NULL;
1015}
1016
1017
1018static void
1019x509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt,
1020                        apr_pool_t *result_pool, apr_pool_t *scratch_pool)
1021{
1022  ci->hostnames = NULL;
1023
1024  if (crt->dnsnames->nelts > 0)
1025    {
1026      int i;
1027
1028      ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts,
1029                                     sizeof(const char*));
1030
1031      /* Subject Alt Names take priority */
1032      for (i = 0; i < crt->dnsnames->nelts; i++)
1033        {
1034          x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *);
1035          const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p,
1036                                                        dnsname->len,
1037                                                        scratch_pool);
1038
1039          APR_ARRAY_PUSH(ci->hostnames, const char*)
1040            = fuzzy_escape(temp, result_pool);
1041        }
1042    }
1043  else
1044    {
1045      /* no SAN then get the hostname from the CommonName on the cert */
1046      const char *utf8_value;
1047
1048      utf8_value = x509parse_get_cn(ci->subject);
1049
1050      if (utf8_value && is_hostname(utf8_value))
1051        {
1052          ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*));
1053          APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value;
1054        }
1055    }
1056}
1057
1058/*
1059 * Parse one certificate.
1060 */
1061svn_error_t *
1062svn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
1063                    const char *buf,
1064                    apr_size_t buflen,
1065                    apr_pool_t *result_pool,
1066                    apr_pool_t *scratch_pool)
1067{
1068  svn_error_t *err;
1069  ptrdiff_t len;
1070  const unsigned char *p;
1071  const unsigned char *end;
1072  x509_cert *crt;
1073  svn_x509_certinfo_t *ci;
1074
1075  crt = apr_pcalloc(scratch_pool, sizeof(*crt));
1076  p = (const unsigned char *)buf;
1077  len = buflen;
1078  end = p + len;
1079
1080  /*
1081   * Certificate  ::=      SEQUENCE  {
1082   *              tbsCertificate           TBSCertificate,
1083   *              signatureAlgorithm       AlgorithmIdentifier,
1084   *              signatureValue           BIT STRING      }
1085   */
1086  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1087  if (err)
1088    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1089
1090  if (len != (end - p))
1091    {
1092      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1093      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1094    }
1095
1096  /*
1097   * TBSCertificate  ::=  SEQUENCE  {
1098   */
1099  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1100  if (err)
1101    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1102
1103  end = p + len;
1104
1105  /*
1106   * Version      ::=      INTEGER  {      v1(0), v2(1), v3(2)  }
1107   *
1108   * CertificateSerialNumber      ::=      INTEGER
1109   *
1110   * signature                    AlgorithmIdentifier
1111   */
1112  SVN_ERR(x509_get_version(&p, end, &crt->version));
1113  SVN_ERR(x509_get_serial(&p, end, &crt->serial));
1114  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1));
1115
1116  crt->version++;
1117
1118  if (crt->version > 3)
1119    return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
1120
1121  /*
1122   * issuer                               Name
1123   */
1124  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1125  if (err)
1126    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1127
1128  SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
1129
1130  /*
1131   * Validity ::= SEQUENCE {
1132   *              notBefore          Time,
1133   *              notAfter           Time }
1134   *
1135   */
1136  SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
1137                         scratch_pool));
1138
1139  /*
1140   * subject                              Name
1141   */
1142  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1143  if (err)
1144    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1145
1146  SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
1147
1148  /*
1149   * SubjectPublicKeyInfo  ::=  SEQUENCE
1150   *              algorithm                        AlgorithmIdentifier,
1151   *              subjectPublicKey         BIT STRING      }
1152   */
1153  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1154  if (err)
1155    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1156
1157  /* Skip pubkey. */
1158  p += len;
1159
1160  /*
1161   *      issuerUniqueID  [1]      IMPLICIT UniqueIdentifier OPTIONAL,
1162   *                                               -- If present, version shall be v2 or v3
1163   *      subjectUniqueID [2]      IMPLICIT UniqueIdentifier OPTIONAL,
1164   *                                               -- If present, version shall be v2 or v3
1165   *      extensions              [3]      EXPLICIT Extensions OPTIONAL
1166   *                                               -- If present, version shall be v3
1167   */
1168  crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
1169
1170  /* Try to parse issuerUniqueID, subjectUniqueID and extensions for *every*
1171   * version (X.509 v1, v2 and v3), not just v2 or v3.  If they aren't present,
1172   * we are fine, but we don't want to throw an error if they are.  v1 and v2
1173   * certificates with the corresponding extra fields are ill-formed per RFC
1174   * 5280 s. 4.1, but we suspect they could exist in the real world.  Other
1175   * X.509 parsers (e.g., within OpenSSL or Microsoft CryptoAPI) aren't picky
1176   * about these certificates, and we also allow them. */
1177  SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1));
1178  SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2));
1179  SVN_ERR(x509_get_ext(crt->dnsnames, &p, end));
1180
1181  if (p != end)
1182    {
1183      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1184      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1185    }
1186
1187  end = (const unsigned char*) buf + buflen;
1188
1189  /*
1190   *      signatureAlgorithm       AlgorithmIdentifier,
1191   *      signatureValue           BIT STRING
1192   */
1193  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
1194
1195  if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2))
1196    return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
1197
1198  SVN_ERR(x509_get_sig(&p, end, &crt->sig));
1199
1200  if (p != end)
1201    {
1202      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1203      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1204    }
1205
1206  ci = apr_pcalloc(result_pool, sizeof(*ci));
1207
1208  /* Get the subject name */
1209  SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject,
1210                                scratch_pool, result_pool));
1211
1212  /* Get the issuer name */
1213  SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer,
1214                                scratch_pool, result_pool));
1215
1216  /* Copy the validity range */
1217  ci->valid_from = crt->valid_from;
1218  ci->valid_to = crt->valid_to;
1219
1220  /* Calculate the SHA1 digest of the certificate, otherwise known as
1221    the fingerprint */
1222  SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
1223                       result_pool));
1224
1225  /* Construct the array of host names */
1226  x509parse_get_hostnames(ci, crt, result_pool, scratch_pool);
1227
1228  *certinfo = ci;
1229  return SVN_NO_ERROR;
1230}
1231
1232