1289177Speter/*
2289177Speter *  X.509 certificate and private key decoding
3289177Speter *
4289177Speter *  Based on XySSL: Copyright (C) 2006-2008   Christophe Devine
5289177Speter *
6289177Speter *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
7289177Speter *
8289177Speter *  All rights reserved.
9289177Speter *
10289177Speter *  Redistribution and use in source and binary forms, with or without
11289177Speter *  modification, are permitted provided that the following conditions
12289177Speter *  are met:
13289177Speter *
14289177Speter *    * Redistributions of source code must retain the above copyright
15289177Speter *    notice, this list of conditions and the following disclaimer.
16289177Speter *    * Redistributions in binary form must reproduce the above copyright
17289177Speter *    notice, this list of conditions and the following disclaimer in the
18289177Speter *    documentation and/or other materials provided with the distribution.
19289177Speter *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
20289177Speter *    may be used to endorse or promote products derived from this software
21289177Speter *    without specific prior written permission.
22289177Speter *
23289177Speter *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24289177Speter *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25289177Speter *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26289177Speter *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27289177Speter *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28289177Speter *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29289177Speter *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30289177Speter *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31289177Speter *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32289177Speter *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33289177Speter *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34289177Speter */
35289177Speter/*
36289177Speter *  The ITU-T X.509 standard defines a certificate format for PKI.
37289177Speter *
38289177Speter *  http://www.ietf.org/rfc/rfc5280.txt
39289177Speter *  http://www.ietf.org/rfc/rfc3279.txt
40289177Speter *  http://www.ietf.org/rfc/rfc6818.txt
41289177Speter *
42289177Speter *  ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
43289177Speter *
44289177Speter *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
45289177Speter *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
46289177Speter */
47289177Speter
48289177Speter#include <apr_pools.h>
49289177Speter#include <apr_tables.h>
50289177Speter#include "svn_hash.h"
51289177Speter#include "svn_string.h"
52289177Speter#include "svn_time.h"
53289177Speter#include "svn_checksum.h"
54289177Speter#include "svn_utf.h"
55289177Speter#include "svn_ctype.h"
56289177Speter#include "private/svn_utf_private.h"
57289177Speter#include "private/svn_string_private.h"
58289177Speter
59289177Speter#include "x509.h"
60289177Speter
61289177Speter#include <string.h>
62289177Speter#include <stdio.h>
63289177Speter
64289177Speter/*
65289177Speter * ASN.1 DER decoding routines
66289177Speter */
67289177Speterstatic svn_error_t *
68289177Speterasn1_get_len(const unsigned char **p, const unsigned char *end,
69289177Speter             ptrdiff_t *len)
70289177Speter{
71289177Speter  if ((end - *p) < 1)
72289177Speter    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
73289177Speter
74289177Speter  if ((**p & 0x80) == 0)
75289177Speter    *len = *(*p)++;
76289177Speter  else
77289177Speter    switch (**p & 0x7F)
78289177Speter      {
79289177Speter      case 1:
80289177Speter        if ((end - *p) < 2)
81289177Speter          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
82289177Speter
83289177Speter        *len = (*p)[1];
84289177Speter        (*p) += 2;
85289177Speter        break;
86289177Speter
87289177Speter      case 2:
88289177Speter        if ((end - *p) < 3)
89289177Speter          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
90289177Speter
91289177Speter        *len = ((*p)[1] << 8) | (*p)[2];
92289177Speter        (*p) += 3;
93289177Speter        break;
94289177Speter
95289177Speter      default:
96289177Speter        return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
97289177Speter        break;
98289177Speter      }
99289177Speter
100289177Speter  if (*len > (end - *p))
101289177Speter    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
102289177Speter
103289177Speter  return SVN_NO_ERROR;
104289177Speter}
105289177Speter
106289177Speterstatic svn_error_t *
107289177Speterasn1_get_tag(const unsigned char **p,
108289177Speter             const unsigned char *end, ptrdiff_t *len, int tag)
109289177Speter{
110289177Speter  if ((end - *p) < 1)
111289177Speter    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
112289177Speter
113289177Speter  if (**p != tag)
114289177Speter    return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
115289177Speter
116289177Speter  (*p)++;
117289177Speter
118289177Speter  return svn_error_trace(asn1_get_len(p, end, len));
119289177Speter}
120289177Speter
121289177Speterstatic svn_error_t *
122289177Speterasn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
123289177Speter{
124289177Speter  ptrdiff_t len;
125289177Speter
126289177Speter  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
127289177Speter
128289177Speter  /* Reject bit patterns that would overflow the output and those that
129289177Speter     represent negative values. */
130289177Speter  if (len > (int)sizeof(int) || (**p & 0x80) != 0)
131289177Speter    return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
132289177Speter
133289177Speter  *val = 0;
134289177Speter
135289177Speter  while (len-- > 0) {
136289177Speter    /* This would be undefined for bit-patterns of negative values. */
137289177Speter    *val = (*val << 8) | **p;
138289177Speter    (*p)++;
139289177Speter  }
140289177Speter
141289177Speter  return SVN_NO_ERROR;
142289177Speter}
143289177Speter
144289177Speterstatic svn_boolean_t
145289177Speterequal(const void *left, apr_size_t left_len,
146289177Speter      const void *right, apr_size_t right_len)
147289177Speter{
148289177Speter  if (left_len != right_len)
149289177Speter    return FALSE;
150289177Speter
151289177Speter  return memcmp(left, right, right_len) == 0;
152289177Speter}
153289177Speter
154289177Speterstatic svn_boolean_t
155289177Speteroids_equal(x509_buf *left, x509_buf *right)
156289177Speter{
157289177Speter  return equal(left->p, left->len,
158289177Speter               right->p, right->len);
159289177Speter}
160289177Speter
161289177Speter/*
162289177Speter *  Version   ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
163289177Speter */
164289177Speterstatic svn_error_t *
165289177Speterx509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
166289177Speter{
167289177Speter  svn_error_t *err;
168289177Speter  ptrdiff_t len;
169289177Speter
170289177Speter  /*
171289177Speter   * As defined in the Basic Certificate fields:
172289177Speter   *   version         [0]  EXPLICIT Version DEFAULT v1,
173289177Speter   * the version is the context specific tag 0.
174289177Speter   */
175289177Speter  err = asn1_get_tag(p, end, &len,
176289177Speter                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
177289177Speter  if (err)
178289177Speter    {
179289177Speter      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
180289177Speter        {
181289177Speter          svn_error_clear(err);
182289177Speter          *ver = 0;
183289177Speter          return SVN_NO_ERROR;
184289177Speter        }
185289177Speter
186289177Speter      return svn_error_trace(err);
187289177Speter    }
188289177Speter
189289177Speter  end = *p + len;
190289177Speter
191289177Speter  err = asn1_get_int(p, end, ver);
192289177Speter  if (err)
193289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
194289177Speter
195289177Speter  if (*p != end)
196289177Speter    {
197289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
198289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
199289177Speter    }
200289177Speter
201289177Speter  return SVN_NO_ERROR;
202289177Speter}
203289177Speter
204289177Speter/*
205289177Speter *  CertificateSerialNumber   ::=  INTEGER
206289177Speter */
207289177Speterstatic svn_error_t *
208289177Speterx509_get_serial(const unsigned char **p,
209289177Speter                const unsigned char *end, x509_buf * serial)
210289177Speter{
211289177Speter  svn_error_t *err;
212289177Speter
213289177Speter  if ((end - *p) < 1)
214289177Speter    {
215289177Speter      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
216289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
217289177Speter    }
218289177Speter
219289177Speter  if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
220289177Speter      **p != ASN1_INTEGER)
221289177Speter    {
222289177Speter      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
223289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
224289177Speter    }
225289177Speter
226289177Speter  serial->tag = *(*p)++;
227289177Speter
228289177Speter  err = asn1_get_len(p, end, &serial->len);
229289177Speter  if (err)
230289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
231289177Speter
232289177Speter  serial->p = *p;
233289177Speter  *p += serial->len;
234289177Speter
235289177Speter  return SVN_NO_ERROR;
236289177Speter}
237289177Speter
238289177Speter/*
239289177Speter *  AlgorithmIdentifier   ::=  SEQUENCE  {
240289177Speter *     algorithm         OBJECT IDENTIFIER,
241289177Speter *     parameters        ANY DEFINED BY algorithm OPTIONAL  }
242289177Speter */
243289177Speterstatic svn_error_t *
244289177Speterx509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
245289177Speter{
246289177Speter  svn_error_t *err;
247289177Speter  ptrdiff_t len;
248289177Speter
249289177Speter  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
250289177Speter  if (err)
251289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
252289177Speter
253289177Speter  end = *p + len;
254289177Speter  alg->tag = **p;
255289177Speter
256289177Speter  err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
257289177Speter  if (err)
258289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
259289177Speter
260289177Speter  alg->p = *p;
261289177Speter  *p += alg->len;
262289177Speter
263289177Speter  if (*p == end)
264289177Speter    return SVN_NO_ERROR;
265289177Speter
266362181Sdim  /* The OID encoding of 1.2.840.113549.1.1.10 (id-RSASSA-PSS) */
267362181Sdim#define OID_RSASSA_PSS "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a"
268289177Speter
269362181Sdim  if (equal(alg->p, alg->len, OID_RSASSA_PSS, sizeof(OID_RSASSA_PSS) - 1))
270362181Sdim    {
271362181Sdim      /* Skip over algorithm parameters for id-RSASSA-PSS (RFC 8017)
272362181Sdim       *
273362181Sdim       * RSASSA-PSS-params ::= SEQUENCE {
274362181Sdim       *  hashAlgorithm      [0] HashAlgorithm    DEFAULT sha1,
275362181Sdim       *  maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
276362181Sdim       *  saltLength         [2] INTEGER          DEFAULT 20,
277362181Sdim       *  trailerField       [3] TrailerField     DEFAULT trailerFieldBC
278362181Sdim       * }
279362181Sdim       */
280362181Sdim      err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
281362181Sdim      if (err)
282362181Sdim        return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
283362181Sdim
284362181Sdim      *p += len;
285362181Sdim    }
286362181Sdim  else
287362181Sdim    {
288362181Sdim      /* Algorithm parameters must be NULL for other algorithms */
289362181Sdim      err = asn1_get_tag(p, end, &len, ASN1_NULL);
290362181Sdim      if (err)
291362181Sdim        return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
292362181Sdim    }
293362181Sdim
294289177Speter  if (*p != end)
295289177Speter    {
296289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
297289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
298289177Speter    }
299289177Speter
300289177Speter  return SVN_NO_ERROR;
301289177Speter}
302289177Speter
303289177Speter/*
304289177Speter *  AttributeTypeAndValue ::= SEQUENCE {
305289177Speter *    type     AttributeType,
306289177Speter *    value     AttributeValue }
307289177Speter *
308289177Speter *  AttributeType ::= OBJECT IDENTIFIER
309289177Speter *
310289177Speter *  AttributeValue ::= ANY DEFINED BY AttributeType
311289177Speter */
312289177Speterstatic svn_error_t *
313289177Speterx509_get_attribute(const unsigned char **p, const unsigned char *end,
314289177Speter                   x509_name *cur, apr_pool_t *result_pool)
315289177Speter{
316289177Speter  svn_error_t *err;
317289177Speter  ptrdiff_t len;
318289177Speter  x509_buf *oid;
319289177Speter  x509_buf *val;
320289177Speter
321289177Speter  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
322289177Speter  if (err)
323289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
324289177Speter
325289177Speter  end = *p + len;
326289177Speter
327289177Speter  oid = &cur->oid;
328289177Speter
329289177Speter  err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
330289177Speter  if (err)
331289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
332289177Speter
333289177Speter  oid->tag = ASN1_OID;
334289177Speter  oid->p = *p;
335289177Speter  *p += oid->len;
336289177Speter
337289177Speter  if ((end - *p) < 1)
338289177Speter    {
339289177Speter      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
340289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
341289177Speter    }
342289177Speter
343289177Speter  if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
344289177Speter      **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
345289177Speter      **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
346289177Speter    {
347289177Speter      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
348289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
349289177Speter    }
350289177Speter
351289177Speter  val = &cur->val;
352289177Speter  val->tag = *(*p)++;
353289177Speter
354289177Speter  err = asn1_get_len(p, end, &val->len);
355289177Speter  if (err)
356289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
357289177Speter
358289177Speter  val->p = *p;
359289177Speter  *p += val->len;
360289177Speter
361289177Speter  cur->next = NULL;
362289177Speter
363289177Speter  if (*p != end)
364289177Speter    {
365289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
366289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
367289177Speter    }
368289177Speter
369289177Speter  return SVN_NO_ERROR;
370289177Speter}
371289177Speter
372289177Speter/*
373289177Speter *   RelativeDistinguishedName ::=
374289177Speter *   SET SIZE (1..MAX) OF AttributeTypeAndValue
375289177Speter */
376289177Speterstatic svn_error_t *
377289177Speterx509_get_name(const unsigned char **p, const unsigned char *name_end,
378289177Speter              x509_name *name, apr_pool_t *result_pool)
379289177Speter{
380289177Speter  svn_error_t *err;
381289177Speter  ptrdiff_t len;
382289177Speter  const unsigned char *set_end;
383289177Speter  x509_name *cur = NULL;
384289177Speter
385289177Speter  err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET);
386362181Sdim  if (err || len < 1)
387289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
388289177Speter
389289177Speter  set_end = *p + len;
390289177Speter
391289177Speter  /*
392289177Speter   * iterate until the end of the SET is reached
393289177Speter   */
394289177Speter  while (*p < set_end)
395289177Speter    {
396289177Speter      if (!cur)
397289177Speter        {
398289177Speter          cur = name;
399289177Speter        }
400289177Speter      else
401289177Speter        {
402289177Speter          cur->next = apr_palloc(result_pool, sizeof(x509_name));
403289177Speter          cur = cur->next;
404289177Speter        }
405289177Speter      SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool));
406289177Speter    }
407289177Speter
408289177Speter  /*
409289177Speter   * recurse until end of SEQUENCE (name) is reached
410289177Speter   */
411289177Speter  if (*p == name_end)
412289177Speter    return SVN_NO_ERROR;
413289177Speter
414289177Speter  cur->next = apr_palloc(result_pool, sizeof(x509_name));
415289177Speter
416289177Speter  return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool));
417289177Speter}
418289177Speter
419289177Speter/* Retrieve the date from the X.509 cert data between *P and END in either
420289177Speter * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
421289177Speter * 4.1.2.5.2 respectively) and place the result in WHEN using  SCRATCH_POOL
422289177Speter * for temporary allocations. */
423289177Speterstatic svn_error_t *
424289177Speterx509_get_date(apr_time_t *when,
425289177Speter              const unsigned char **p,
426289177Speter              const unsigned char *end,
427289177Speter              apr_pool_t *scratch_pool)
428289177Speter{
429289177Speter  svn_error_t *err;
430289177Speter  apr_status_t ret;
431289177Speter  int tag;
432289177Speter  ptrdiff_t len;
433289177Speter  char *date;
434289177Speter  apr_time_exp_t xt = { 0 };
435289177Speter  char tz;
436289177Speter
437289177Speter  err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
438289177Speter  if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
439289177Speter    {
440289177Speter      svn_error_clear(err);
441289177Speter      err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
442289177Speter      tag = ASN1_GENERALIZED_TIME;
443289177Speter    }
444289177Speter  else
445289177Speter    {
446289177Speter      tag = ASN1_UTC_TIME;
447289177Speter    }
448289177Speter  if (err)
449289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
450289177Speter
451289177Speter  date = apr_pstrndup(scratch_pool, (const char *) *p, len);
452289177Speter  switch (tag)
453289177Speter    {
454289177Speter    case ASN1_UTC_TIME:
455289177Speter      if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
456289177Speter                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
457289177Speter                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
458289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
459289177Speter
460289177Speter      /* UTCTime only provides a 2 digit year.  X.509 specifies that years
461289177Speter       * greater than or equal to 50 must be interpreted as 19YY and years
462289177Speter       * less than 50 be interpreted as 20YY.  This format is not used for
463289177Speter       * years greater than 2049. apr_time_exp_t wants years as the number
464289177Speter       * of years since 1900, so don't convert to 4 digits here. */
465289177Speter      xt.tm_year += 100 * (xt.tm_year < 50);
466289177Speter      break;
467289177Speter
468289177Speter    case ASN1_GENERALIZED_TIME:
469289177Speter      if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
470289177Speter                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
471289177Speter                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
472289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
473289177Speter
474289177Speter      /* GeneralizedTime has the full 4 digit year.  But apr_time_exp_t
475289177Speter       * wants years as the number of years since 1900. */
476289177Speter      xt.tm_year -= 1900;
477289177Speter      break;
478289177Speter
479289177Speter    default:
480289177Speter      /* shouldn't ever get here because we should error out above in the
481289177Speter       * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
482289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
483289177Speter      break;
484289177Speter    }
485289177Speter
486289177Speter  /* check that the timezone is GMT
487289177Speter   * ASN.1 allows for the timezone to be specified but X.509 says it must
488289177Speter   * always be GMT.  A little bit of extra paranoia here seems like a good
489289177Speter   * idea. */
490289177Speter  if (tz != 'Z')
491289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
492289177Speter
493289177Speter  /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
494289177Speter  xt.tm_mon -= 1;
495289177Speter
496362181Sdim  /* range checks (as per definition of apr_time_exp_t in apr_time.h) */
497362181Sdim  if (xt.tm_usec < 0 ||
498362181Sdim      xt.tm_sec < 0 || xt.tm_sec > 61 ||
499362181Sdim      xt.tm_min < 0 || xt.tm_min > 59 ||
500362181Sdim      xt.tm_hour < 0 || xt.tm_hour > 23 ||
501362181Sdim      xt.tm_mday < 1 || xt.tm_mday > 31 ||
502362181Sdim      xt.tm_mon < 0 || xt.tm_mon > 11 ||
503362181Sdim      xt.tm_year < 0 ||
504362181Sdim      xt.tm_wday < 0 || xt.tm_wday > 6 ||
505362181Sdim      xt.tm_yday < 0 || xt.tm_yday > 365)
506362181Sdim    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
507362181Sdim
508289177Speter  ret = apr_time_exp_gmt_get(when, &xt);
509289177Speter  if (ret)
510289177Speter    return svn_error_wrap_apr(ret, NULL);
511289177Speter
512289177Speter  *p += len;
513289177Speter
514289177Speter  return SVN_NO_ERROR;
515289177Speter}
516289177Speter
517289177Speter/*
518289177Speter *  Validity ::= SEQUENCE {
519289177Speter *     notBefore    Time,
520289177Speter *     notAfter    Time }
521289177Speter *
522289177Speter *  Time ::= CHOICE {
523289177Speter *     utcTime    UTCTime,
524289177Speter *     generalTime  GeneralizedTime }
525289177Speter */
526289177Speterstatic svn_error_t *
527289177Speterx509_get_dates(apr_time_t *from,
528289177Speter               apr_time_t *to,
529289177Speter               const unsigned char **p,
530289177Speter               const unsigned char *end,
531289177Speter               apr_pool_t *scratch_pool)
532289177Speter{
533289177Speter  svn_error_t *err;
534289177Speter  ptrdiff_t len;
535289177Speter
536289177Speter  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
537289177Speter  if (err)
538289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
539289177Speter
540289177Speter  end = *p + len;
541289177Speter
542289177Speter  SVN_ERR(x509_get_date(from, p, end, scratch_pool));
543289177Speter
544289177Speter  SVN_ERR(x509_get_date(to, p, end, scratch_pool));
545289177Speter
546289177Speter  if (*p != end)
547289177Speter    {
548289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
549289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
550289177Speter    }
551289177Speter
552289177Speter  return SVN_NO_ERROR;
553289177Speter}
554289177Speter
555289177Speterstatic svn_error_t *
556289177Speterx509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
557289177Speter{
558289177Speter  svn_error_t *err;
559289177Speter  ptrdiff_t len;
560289177Speter
561289177Speter  err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
562289177Speter  if (err)
563289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
564289177Speter
565289177Speter  sig->tag = ASN1_BIT_STRING;
566289177Speter
567289177Speter  if (--len < 1 || *(*p)++ != 0)
568289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
569289177Speter
570289177Speter  sig->len = len;
571289177Speter  sig->p = *p;
572289177Speter
573289177Speter  *p += len;
574289177Speter
575289177Speter  return SVN_NO_ERROR;
576289177Speter}
577289177Speter
578289177Speter/*
579289177Speter * X.509 v2/v3 unique identifier (not parsed)
580289177Speter */
581289177Speterstatic svn_error_t *
582289177Speterx509_get_uid(const unsigned char **p,
583289177Speter             const unsigned char *end, x509_buf * uid, int n)
584289177Speter{
585289177Speter  svn_error_t *err;
586289177Speter
587289177Speter  if (*p == end)
588289177Speter    return SVN_NO_ERROR;
589289177Speter
590289177Speter  err = asn1_get_tag(p, end, &uid->len,
591289177Speter                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
592289177Speter  if (err)
593289177Speter    {
594289177Speter      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
595289177Speter        {
596289177Speter          svn_error_clear(err);
597289177Speter          return SVN_NO_ERROR;
598289177Speter        }
599289177Speter
600289177Speter      return svn_error_trace(err);
601289177Speter    }
602289177Speter
603289177Speter  uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n;
604289177Speter  uid->p = *p;
605289177Speter  *p += uid->len;
606289177Speter
607289177Speter  return SVN_NO_ERROR;
608289177Speter}
609289177Speter
610289177Speter/*
611289177Speter * X.509 v3 extensions (not parsed)
612289177Speter */
613289177Speterstatic svn_error_t *
614289177Speterx509_get_ext(apr_array_header_t *dnsnames,
615289177Speter             const unsigned char **p,
616289177Speter             const unsigned char *end)
617289177Speter{
618289177Speter  svn_error_t *err;
619289177Speter  ptrdiff_t len;
620289177Speter
621289177Speter  if (*p == end)
622289177Speter    return SVN_NO_ERROR;
623289177Speter
624289177Speter  err = asn1_get_tag(p, end, &len,
625289177Speter                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
626289177Speter  if (err)
627289177Speter    {
628289177Speter      /* If there aren't extensions that's ok they aren't required */
629289177Speter      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
630289177Speter        {
631289177Speter          svn_error_clear(err);
632289177Speter          return SVN_NO_ERROR;
633289177Speter        }
634289177Speter
635289177Speter      return svn_error_trace(err);
636289177Speter    }
637289177Speter
638289177Speter  end = *p + len;
639289177Speter
640289177Speter  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
641289177Speter
642289177Speter  if (end != *p + len)
643289177Speter    {
644289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
645289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
646289177Speter    }
647289177Speter
648289177Speter  while (*p < end)
649289177Speter    {
650289177Speter      ptrdiff_t ext_len;
651289177Speter      const unsigned char *ext_start, *sna_end;
652289177Speter      err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
653289177Speter      if (err)
654289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
655289177Speter                                NULL);
656289177Speter      ext_start = *p;
657289177Speter
658289177Speter      err = asn1_get_tag(p, end, &len, ASN1_OID);
659289177Speter      if (err)
660289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
661289177Speter                                NULL);
662289177Speter
663289177Speter      /* skip all extensions except SubjectAltName */
664289177Speter      if (!equal(*p, len,
665289177Speter                 OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1))
666289177Speter        {
667289177Speter          *p += ext_len - (*p - ext_start);
668289177Speter          continue;
669289177Speter        }
670289177Speter      *p += len;
671289177Speter
672289177Speter      err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
673289177Speter      if (err)
674289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
675289177Speter                                NULL);
676289177Speter
677289177Speter      /*   SubjectAltName ::= GeneralNames
678289177Speter
679289177Speter           GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
680289177Speter
681289177Speter           GeneralName ::= CHOICE {
682289177Speter                other Name                      [0]     OtherName,
683289177Speter                rfc822Name                      [1]     IA5String,
684289177Speter                dNSName                         [2]     IA5String,
685289177Speter                x400Address                     [3]     ORAddress,
686289177Speter                directoryName                   [4]     Name,
687289177Speter                ediPartyName                    [5]     EDIPartyName,
688289177Speter                uniformResourceIdentifier       [6]     IA5String,
689289177Speter                iPAddress                       [7]     OCTET STRING,
690289177Speter                registeredID                    [8]     OBJECT IDENTIFIER } */
691289177Speter      sna_end = *p + len;
692289177Speter
693289177Speter      err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
694289177Speter      if (err)
695289177Speter        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
696289177Speter                                NULL);
697289177Speter
698289177Speter      if (sna_end != *p + len)
699289177Speter        {
700289177Speter          err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
701289177Speter          return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
702289177Speter        }
703289177Speter
704289177Speter      while (*p < sna_end)
705289177Speter        {
706289177Speter          err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
707289177Speter                             ASN1_PRIMITIVE | 2);
708289177Speter          if (err)
709289177Speter            {
710289177Speter              /* not not a dNSName */
711289177Speter              if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
712289177Speter                {
713289177Speter                  svn_error_clear(err);
714289177Speter                  /* need to skip the tag and then find the length to
715289177Speter                   * skip to ignore this SNA entry. */
716289177Speter                  (*p)++;
717289177Speter                  SVN_ERR(asn1_get_len(p, sna_end, &len));
718289177Speter                  *p += len;
719289177Speter                  continue;
720289177Speter                }
721289177Speter
722289177Speter              return svn_error_trace(err);
723289177Speter            }
724289177Speter          else
725289177Speter            {
726289177Speter              /* We found a dNSName entry */
727362181Sdim              x509_buf *dnsname = apr_palloc(dnsnames->pool, sizeof(*dnsname));
728289177Speter              dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
729289177Speter              dnsname->len = len;
730289177Speter              dnsname->p = *p;
731289177Speter              APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
732289177Speter            }
733289177Speter
734289177Speter          *p += len;
735289177Speter        }
736289177Speter
737289177Speter    }
738289177Speter
739289177Speter  return SVN_NO_ERROR;
740289177Speter}
741289177Speter
742289177Speter/* Escape all non-ascii or control characters similar to
743289177Speter * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy().
744289177Speter * All of the encoding formats somewhat overlap with ascii (BMPString
745289177Speter * and UniversalString are actually always wider so you'll end up
746289177Speter * with a bunch of escaped nul bytes, but ideally we don't get here
747289177Speter * for those).  The result is always a nul-terminated C string. */
748289177Speterstatic const char *
749289177Speterfuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
750289177Speter{
751289177Speter  const char *end = src->data + src->len;
752289177Speter  const char *p = src->data, *q;
753289177Speter  svn_stringbuf_t *outstr;
754289177Speter  char escaped_char[6]; /* ? \ u u u \0 */
755289177Speter
756289177Speter  for (q = p; q < end; q++)
757289177Speter    {
758289177Speter      if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
759289177Speter        break;
760289177Speter    }
761289177Speter
762289177Speter  if (q == end)
763289177Speter    return src->data;
764289177Speter
765289177Speter  outstr = svn_stringbuf_create_empty(result_pool);
766289177Speter  while (1)
767289177Speter    {
768289177Speter      q = p;
769289177Speter
770289177Speter      /* Traverse till either unsafe character or eos. */
771289177Speter      while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
772289177Speter        q++;
773289177Speter
774289177Speter      /* copy chunk before marker */
775289177Speter      svn_stringbuf_appendbytes(outstr, p, q - p);
776289177Speter
777289177Speter      if (q == end)
778289177Speter        break;
779289177Speter
780289177Speter      apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
781289177Speter                   (unsigned char) *q);
782289177Speter      svn_stringbuf_appendcstr(outstr, escaped_char);
783289177Speter
784289177Speter      p = q + 1;
785289177Speter    }
786289177Speter
787289177Speter  return outstr->data;
788289177Speter}
789289177Speter
790289177Speter/* Escape only NUL characters from a string that is presumed to
791289177Speter * be UTF-8 encoded and return a nul-terminated C string. */
792289177Speterstatic const char *
793289177Speternul_escape(const svn_string_t *src, apr_pool_t *result_pool)
794289177Speter{
795289177Speter  const char *end = src->data + src->len;
796289177Speter  const char *p = src->data, *q;
797289177Speter  svn_stringbuf_t *outstr;
798289177Speter
799289177Speter  for (q = p; q < end; q++)
800289177Speter    {
801289177Speter      if (*q == '\0')
802289177Speter        break;
803289177Speter    }
804289177Speter
805289177Speter  if (q == end)
806289177Speter    return src->data;
807289177Speter
808289177Speter  outstr = svn_stringbuf_create_empty(result_pool);
809289177Speter  while (1)
810289177Speter    {
811289177Speter      q = p;
812289177Speter
813289177Speter      /* Traverse till either unsafe character or eos. */
814289177Speter      while (q < end && *q != '\0')
815289177Speter        q++;
816289177Speter
817289177Speter      /* copy chunk before marker */
818289177Speter      svn_stringbuf_appendbytes(outstr, p, q - p);
819289177Speter
820289177Speter      if (q == end)
821289177Speter        break;
822289177Speter
823289177Speter      svn_stringbuf_appendcstr(outstr, "?\\000");
824289177Speter
825289177Speter      p = q + 1;
826289177Speter    }
827289177Speter
828289177Speter  return outstr->data;
829289177Speter}
830289177Speter
831289177Speter
832289177Speter/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
833289177Speter   ISO-8859-1 is a strict subset of Unicode. */
834289177Speterstatic svn_error_t *
835289177Speterlatin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
836289177Speter               apr_pool_t *result_pool)
837289177Speter{
838289177Speter  apr_int32_t *ucs4buf;
839289177Speter  svn_membuf_t resultbuf;
840289177Speter  apr_size_t length;
841289177Speter  apr_size_t i;
842289177Speter  svn_string_t *res;
843289177Speter
844289177Speter  ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf));
845289177Speter  for (i = 0; i < src->len; ++i)
846289177Speter    ucs4buf[i] = (unsigned char)(src->data[i]);
847289177Speter
848289177Speter  svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
849289177Speter  SVN_ERR(svn_utf__encode_ucs4_string(
850289177Speter              &resultbuf, ucs4buf, src->len, &length));
851289177Speter
852289177Speter  res = apr_palloc(result_pool, sizeof(*res));
853289177Speter  res->data = resultbuf.data;
854289177Speter  res->len = length;
855289177Speter  *result = res;
856289177Speter  return SVN_NO_ERROR;
857289177Speter}
858289177Speter
859289177Speter/* Make a best effort to convert a X.509 name to a UTF-8 encoded
860289177Speter * string and return it.  If we can't properly convert just do a
861289177Speter * fuzzy conversion so we have something to display. */
862289177Speterstatic const char *
863289177Speterx509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
864289177Speter{
865289177Speter  const svn_string_t *src_string;
866289177Speter  const svn_string_t *utf8_string;
867289177Speter  svn_error_t *err;
868289177Speter
869289177Speter  src_string = svn_string_ncreate((const char *)name->val.p,
870289177Speter                                  name->val.len,
871289177Speter                                  result_pool);
872289177Speter  switch (name->val.tag)
873289177Speter    {
874289177Speter    case ASN1_UTF8_STRING:
875289177Speter      if (svn_utf__is_valid(src_string->data, src_string->len))
876289177Speter        return nul_escape(src_string, result_pool);
877289177Speter      else
878289177Speter        /* not a valid UTF-8 string, who knows what it is,
879289177Speter         * so run it through the fuzzy_escape code.  */
880289177Speter        return fuzzy_escape(src_string, result_pool);
881289177Speter      break;
882289177Speter
883289177Speter      /* Both BMP and UNIVERSAL should always be in Big Endian (aka
884289177Speter       * network byte order).  But rumor has it that there are certs
885289177Speter       * out there with other endianess and even Byte Order Marks.
886289177Speter       * If we actually run into these, we might need to do something
887289177Speter       * about it. */
888289177Speter
889289177Speter    case ASN1_BMP_STRING:
890289177Speter      if (0 != src_string->len % sizeof(apr_uint16_t))
891289177Speter          return fuzzy_escape(src_string, result_pool);
892289177Speter      err = svn_utf__utf16_to_utf8(&utf8_string,
893289177Speter                                   (const void*)(src_string->data),
894289177Speter                                   src_string->len / sizeof(apr_uint16_t),
895289177Speter                                   TRUE, result_pool, result_pool);
896289177Speter      break;
897289177Speter
898289177Speter    case ASN1_UNIVERSAL_STRING:
899289177Speter      if (0 != src_string->len % sizeof(apr_int32_t))
900289177Speter          return fuzzy_escape(src_string, result_pool);
901289177Speter      err = svn_utf__utf32_to_utf8(&utf8_string,
902289177Speter                                   (const void*)(src_string->data),
903289177Speter                                   src_string->len / sizeof(apr_int32_t),
904289177Speter                                   TRUE, result_pool, result_pool);
905289177Speter      break;
906289177Speter
907289177Speter      /* Despite what all the IETF, ISO, ITU bits say everything out
908289177Speter       * on the Internet that I can find treats this as ISO-8859-1.
909289177Speter       * Even the name is misleading, it's not actually T.61.  All the
910289177Speter       * gory details can be found in the Character Sets section of:
911289177Speter       * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt
912289177Speter       */
913289177Speter    case ASN1_T61_STRING:
914289177Speter      err = latin1_to_utf8(&utf8_string, src_string, result_pool);
915289177Speter      break;
916289177Speter
917289177Speter      /* This leaves two types out there in the wild.  PrintableString,
918289177Speter       * which is just a subset of ASCII and IA5 which is ASCII (though
919289177Speter       * 0x24 '$' and 0x23 '#' may be defined with differnet symbols
920289177Speter       * depending on the location, in practice it seems everyone just
921289177Speter       * treats it as ASCII).  Since these are just ASCII run through
922289177Speter       * the fuzzy_escape code to deal with anything that isn't actually
923289177Speter       * ASCII.  There shouldn't be any other types here but if we find
924289177Speter       * a cert with some other encoding, the best we can do is the
925289177Speter       * fuzzy_escape().  Note: Technically IA5 isn't valid in this
926289177Speter       * context, however in the real world it may pop up. */
927289177Speter    default:
928289177Speter      return fuzzy_escape(src_string, result_pool);
929289177Speter    }
930289177Speter
931289177Speter  if (err)
932289177Speter    {
933289177Speter      svn_error_clear(err);
934289177Speter      return fuzzy_escape(src_string, result_pool);
935289177Speter    }
936289177Speter
937289177Speter  return nul_escape(utf8_string, result_pool);
938289177Speter}
939289177Speter
940289177Speterstatic svn_error_t *
941289177Speterx509_name_to_certinfo(apr_array_header_t **result,
942289177Speter                      const x509_name *dn,
943289177Speter                      apr_pool_t *scratch_pool,
944289177Speter                      apr_pool_t *result_pool)
945289177Speter{
946289177Speter  const x509_name *name = dn;
947289177Speter
948289177Speter  *result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *));
949289177Speter
950289177Speter  while (name != NULL) {
951289177Speter    svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t));
952289177Speter
953289177Speter    attr->oid_len = name->oid.len;
954362181Sdim    attr->oid = apr_pmemdup(result_pool, name->oid.p, attr->oid_len);
955289177Speter    attr->utf8_value = x509name_to_utf8_string(name, result_pool);
956289177Speter    if (!attr->utf8_value)
957289177Speter      /* this should never happen */
958289177Speter      attr->utf8_value = apr_pstrdup(result_pool, "??");
959289177Speter    APR_ARRAY_PUSH(*result, const svn_x509_name_attr_t *) = attr;
960289177Speter
961289177Speter    name = name->next;
962289177Speter  }
963289177Speter
964289177Speter  return SVN_NO_ERROR;
965289177Speter}
966289177Speter
967289177Speterstatic svn_boolean_t
968289177Speteris_hostname(const char *str)
969289177Speter{
970289177Speter  apr_size_t i, len = strlen(str);
971289177Speter
972289177Speter  for (i = 0; i < len; i++)
973289177Speter    {
974289177Speter      char c = str[i];
975289177Speter
976289177Speter      /* '-' is only legal when not at the start or end of a label */
977289177Speter      if (c == '-')
978289177Speter        {
979289177Speter          if (i + 1 != len)
980289177Speter            {
981289177Speter              if (str[i + 1] == '.')
982289177Speter                return FALSE; /* '-' preceeds a '.' */
983289177Speter            }
984289177Speter          else
985289177Speter            return FALSE; /* '-' is at end of string */
986289177Speter
987289177Speter          /* determine the previous character. */
988289177Speter          if (i == 0)
989289177Speter            return FALSE; /* '-' is at start of string */
990289177Speter          else
991289177Speter            if (str[i - 1] == '.')
992289177Speter              return FALSE; /* '-' follows a '.' */
993289177Speter        }
994289177Speter      else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
995289177Speter        return FALSE; /* some character not allowed */
996289177Speter    }
997289177Speter
998289177Speter  return TRUE;
999289177Speter}
1000289177Speter
1001289177Speterstatic const char *
1002289177Speterx509parse_get_cn(apr_array_header_t *subject)
1003289177Speter{
1004289177Speter  int i;
1005289177Speter
1006289177Speter  for (i = 0; i < subject->nelts; ++i)
1007289177Speter    {
1008289177Speter      const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(subject, i, const svn_x509_name_attr_t *);
1009289177Speter      if (equal(attr->oid, attr->oid_len,
1010289177Speter                SVN_X509_OID_COMMON_NAME, sizeof(SVN_X509_OID_COMMON_NAME) - 1))
1011289177Speter        return attr->utf8_value;
1012289177Speter    }
1013289177Speter
1014289177Speter  return NULL;
1015289177Speter}
1016289177Speter
1017289177Speter
1018289177Speterstatic void
1019289177Speterx509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt,
1020289177Speter                        apr_pool_t *result_pool, apr_pool_t *scratch_pool)
1021289177Speter{
1022289177Speter  ci->hostnames = NULL;
1023289177Speter
1024289177Speter  if (crt->dnsnames->nelts > 0)
1025289177Speter    {
1026289177Speter      int i;
1027289177Speter
1028289177Speter      ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts,
1029289177Speter                                     sizeof(const char*));
1030289177Speter
1031289177Speter      /* Subject Alt Names take priority */
1032289177Speter      for (i = 0; i < crt->dnsnames->nelts; i++)
1033289177Speter        {
1034289177Speter          x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *);
1035289177Speter          const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p,
1036289177Speter                                                        dnsname->len,
1037289177Speter                                                        scratch_pool);
1038289177Speter
1039289177Speter          APR_ARRAY_PUSH(ci->hostnames, const char*)
1040289177Speter            = fuzzy_escape(temp, result_pool);
1041289177Speter        }
1042289177Speter    }
1043289177Speter  else
1044289177Speter    {
1045289177Speter      /* no SAN then get the hostname from the CommonName on the cert */
1046289177Speter      const char *utf8_value;
1047289177Speter
1048289177Speter      utf8_value = x509parse_get_cn(ci->subject);
1049289177Speter
1050289177Speter      if (utf8_value && is_hostname(utf8_value))
1051289177Speter        {
1052289177Speter          ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*));
1053289177Speter          APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value;
1054289177Speter        }
1055289177Speter    }
1056289177Speter}
1057289177Speter
1058289177Speter/*
1059289177Speter * Parse one certificate.
1060289177Speter */
1061289177Spetersvn_error_t *
1062289177Spetersvn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
1063289177Speter                    const char *buf,
1064289177Speter                    apr_size_t buflen,
1065289177Speter                    apr_pool_t *result_pool,
1066289177Speter                    apr_pool_t *scratch_pool)
1067289177Speter{
1068289177Speter  svn_error_t *err;
1069289177Speter  ptrdiff_t len;
1070289177Speter  const unsigned char *p;
1071289177Speter  const unsigned char *end;
1072289177Speter  x509_cert *crt;
1073289177Speter  svn_x509_certinfo_t *ci;
1074289177Speter
1075289177Speter  crt = apr_pcalloc(scratch_pool, sizeof(*crt));
1076289177Speter  p = (const unsigned char *)buf;
1077289177Speter  len = buflen;
1078289177Speter  end = p + len;
1079289177Speter
1080289177Speter  /*
1081289177Speter   * Certificate  ::=      SEQUENCE  {
1082289177Speter   *              tbsCertificate           TBSCertificate,
1083289177Speter   *              signatureAlgorithm       AlgorithmIdentifier,
1084289177Speter   *              signatureValue           BIT STRING      }
1085289177Speter   */
1086289177Speter  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1087289177Speter  if (err)
1088289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1089289177Speter
1090289177Speter  if (len != (end - p))
1091289177Speter    {
1092289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1093289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1094289177Speter    }
1095289177Speter
1096289177Speter  /*
1097289177Speter   * TBSCertificate  ::=  SEQUENCE  {
1098289177Speter   */
1099289177Speter  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1100289177Speter  if (err)
1101289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1102289177Speter
1103289177Speter  end = p + len;
1104289177Speter
1105289177Speter  /*
1106289177Speter   * Version      ::=      INTEGER  {      v1(0), v2(1), v3(2)  }
1107289177Speter   *
1108289177Speter   * CertificateSerialNumber      ::=      INTEGER
1109289177Speter   *
1110289177Speter   * signature                    AlgorithmIdentifier
1111289177Speter   */
1112289177Speter  SVN_ERR(x509_get_version(&p, end, &crt->version));
1113289177Speter  SVN_ERR(x509_get_serial(&p, end, &crt->serial));
1114289177Speter  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1));
1115289177Speter
1116289177Speter  crt->version++;
1117289177Speter
1118289177Speter  if (crt->version > 3)
1119289177Speter    return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
1120289177Speter
1121289177Speter  /*
1122289177Speter   * issuer                               Name
1123289177Speter   */
1124289177Speter  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1125289177Speter  if (err)
1126289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1127289177Speter
1128289177Speter  SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
1129289177Speter
1130289177Speter  /*
1131289177Speter   * Validity ::= SEQUENCE {
1132289177Speter   *              notBefore          Time,
1133289177Speter   *              notAfter           Time }
1134289177Speter   *
1135289177Speter   */
1136289177Speter  SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
1137289177Speter                         scratch_pool));
1138289177Speter
1139289177Speter  /*
1140289177Speter   * subject                              Name
1141289177Speter   */
1142289177Speter  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1143289177Speter  if (err)
1144289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1145289177Speter
1146289177Speter  SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
1147289177Speter
1148289177Speter  /*
1149289177Speter   * SubjectPublicKeyInfo  ::=  SEQUENCE
1150289177Speter   *              algorithm                        AlgorithmIdentifier,
1151289177Speter   *              subjectPublicKey         BIT STRING      }
1152289177Speter   */
1153289177Speter  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1154289177Speter  if (err)
1155289177Speter    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1156289177Speter
1157289177Speter  /* Skip pubkey. */
1158289177Speter  p += len;
1159289177Speter
1160289177Speter  /*
1161289177Speter   *      issuerUniqueID  [1]      IMPLICIT UniqueIdentifier OPTIONAL,
1162289177Speter   *                                               -- If present, version shall be v2 or v3
1163289177Speter   *      subjectUniqueID [2]      IMPLICIT UniqueIdentifier OPTIONAL,
1164289177Speter   *                                               -- If present, version shall be v2 or v3
1165289177Speter   *      extensions              [3]      EXPLICIT Extensions OPTIONAL
1166289177Speter   *                                               -- If present, version shall be v3
1167289177Speter   */
1168289177Speter  crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
1169289177Speter
1170289177Speter  /* Try to parse issuerUniqueID, subjectUniqueID and extensions for *every*
1171289177Speter   * version (X.509 v1, v2 and v3), not just v2 or v3.  If they aren't present,
1172289177Speter   * we are fine, but we don't want to throw an error if they are.  v1 and v2
1173289177Speter   * certificates with the corresponding extra fields are ill-formed per RFC
1174289177Speter   * 5280 s. 4.1, but we suspect they could exist in the real world.  Other
1175289177Speter   * X.509 parsers (e.g., within OpenSSL or Microsoft CryptoAPI) aren't picky
1176289177Speter   * about these certificates, and we also allow them. */
1177289177Speter  SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1));
1178289177Speter  SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2));
1179289177Speter  SVN_ERR(x509_get_ext(crt->dnsnames, &p, end));
1180289177Speter
1181289177Speter  if (p != end)
1182289177Speter    {
1183289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1184289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1185289177Speter    }
1186289177Speter
1187289177Speter  end = (const unsigned char*) buf + buflen;
1188289177Speter
1189289177Speter  /*
1190289177Speter   *      signatureAlgorithm       AlgorithmIdentifier,
1191289177Speter   *      signatureValue           BIT STRING
1192289177Speter   */
1193289177Speter  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
1194289177Speter
1195289177Speter  if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2))
1196289177Speter    return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
1197289177Speter
1198289177Speter  SVN_ERR(x509_get_sig(&p, end, &crt->sig));
1199289177Speter
1200289177Speter  if (p != end)
1201289177Speter    {
1202289177Speter      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1203289177Speter      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1204289177Speter    }
1205289177Speter
1206289177Speter  ci = apr_pcalloc(result_pool, sizeof(*ci));
1207289177Speter
1208289177Speter  /* Get the subject name */
1209289177Speter  SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject,
1210289177Speter                                scratch_pool, result_pool));
1211289177Speter
1212289177Speter  /* Get the issuer name */
1213289177Speter  SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer,
1214289177Speter                                scratch_pool, result_pool));
1215289177Speter
1216289177Speter  /* Copy the validity range */
1217289177Speter  ci->valid_from = crt->valid_from;
1218289177Speter  ci->valid_to = crt->valid_to;
1219289177Speter
1220289177Speter  /* Calculate the SHA1 digest of the certificate, otherwise known as
1221289177Speter    the fingerprint */
1222289177Speter  SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
1223289177Speter                       result_pool));
1224289177Speter
1225289177Speter  /* Construct the array of host names */
1226289177Speter  x509parse_get_hostnames(ci, crt, result_pool, scratch_pool);
1227289177Speter
1228289177Speter  *certinfo = ci;
1229289177Speter  return SVN_NO_ERROR;
1230289177Speter}
1231289177Speter
1232