x509parse.c revision 299742
1232809Sjmallett/*
2232809Sjmallett *  X.509 certificate and private key decoding
3232809Sjmallett *
4232809Sjmallett *  Based on XySSL: Copyright (C) 2006-2008   Christophe Devine
5232809Sjmallett *
6232809Sjmallett *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
7232809Sjmallett *
8232809Sjmallett *  All rights reserved.
9232809Sjmallett *
10232809Sjmallett *  Redistribution and use in source and binary forms, with or without
11232809Sjmallett *  modification, are permitted provided that the following conditions
12232809Sjmallett *  are met:
13232809Sjmallett *
14232809Sjmallett *    * Redistributions of source code must retain the above copyright
15232809Sjmallett *    notice, this list of conditions and the following disclaimer.
16232809Sjmallett *    * Redistributions in binary form must reproduce the above copyright
17232809Sjmallett *    notice, this list of conditions and the following disclaimer in the
18232809Sjmallett *    documentation and/or other materials provided with the distribution.
19232809Sjmallett *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
20232809Sjmallett *    may be used to endorse or promote products derived from this software
21232809Sjmallett *    without specific prior written permission.
22232809Sjmallett *
23232809Sjmallett *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24232809Sjmallett *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25232809Sjmallett *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26232809Sjmallett *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27232809Sjmallett *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28232809Sjmallett *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29232809Sjmallett *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30232809Sjmallett *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31232809Sjmallett *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32232809Sjmallett *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33232809Sjmallett *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34232809Sjmallett */
35232809Sjmallett/*
36232809Sjmallett *  The ITU-T X.509 standard defines a certificate format for PKI.
37232809Sjmallett *
38232809Sjmallett *  http://www.ietf.org/rfc/rfc5280.txt
39232809Sjmallett *  http://www.ietf.org/rfc/rfc3279.txt
40232809Sjmallett *  http://www.ietf.org/rfc/rfc6818.txt
41232809Sjmallett *
42232809Sjmallett *  ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
43232809Sjmallett *
44232809Sjmallett *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
45232809Sjmallett *  http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
46232809Sjmallett */
47232809Sjmallett
48232809Sjmallett#include <apr_pools.h>
49232809Sjmallett#include <apr_tables.h>
50232809Sjmallett#include "svn_hash.h"
51232809Sjmallett#include "svn_string.h"
52232809Sjmallett#include "svn_time.h"
53232809Sjmallett#include "svn_checksum.h"
54232809Sjmallett#include "svn_utf.h"
55232809Sjmallett#include "svn_ctype.h"
56232809Sjmallett#include "private/svn_utf_private.h"
57232809Sjmallett#include "private/svn_string_private.h"
58232809Sjmallett
59232809Sjmallett#include "x509.h"
60232809Sjmallett
61232809Sjmallett#include <string.h>
62232809Sjmallett#include <stdio.h>
63232809Sjmallett
64232809Sjmallett/*
65232809Sjmallett * ASN.1 DER decoding routines
66232809Sjmallett */
67232809Sjmallettstatic svn_error_t *
68232809Sjmallettasn1_get_len(const unsigned char **p, const unsigned char *end,
69232809Sjmallett             ptrdiff_t *len)
70232809Sjmallett{
71232809Sjmallett  if ((end - *p) < 1)
72232809Sjmallett    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
73232809Sjmallett
74232809Sjmallett  if ((**p & 0x80) == 0)
75232809Sjmallett    *len = *(*p)++;
76232809Sjmallett  else
77232809Sjmallett    switch (**p & 0x7F)
78232809Sjmallett      {
79232809Sjmallett      case 1:
80232809Sjmallett        if ((end - *p) < 2)
81232809Sjmallett          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
82232809Sjmallett
83232809Sjmallett        *len = (*p)[1];
84232809Sjmallett        (*p) += 2;
85232809Sjmallett        break;
86232809Sjmallett
87232809Sjmallett      case 2:
88232809Sjmallett        if ((end - *p) < 3)
89232809Sjmallett          return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
90232809Sjmallett
91232809Sjmallett        *len = ((*p)[1] << 8) | (*p)[2];
92232809Sjmallett        (*p) += 3;
93232809Sjmallett        break;
94232809Sjmallett
95232809Sjmallett      default:
96232809Sjmallett        return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
97232809Sjmallett        break;
98232809Sjmallett      }
99232809Sjmallett
100232809Sjmallett  if (*len > (end - *p))
101232809Sjmallett    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
102232809Sjmallett
103232809Sjmallett  return SVN_NO_ERROR;
104232809Sjmallett}
105232809Sjmallett
106232809Sjmallettstatic svn_error_t *
107232809Sjmallettasn1_get_tag(const unsigned char **p,
108232809Sjmallett             const unsigned char *end, ptrdiff_t *len, int tag)
109232809Sjmallett{
110232809Sjmallett  if ((end - *p) < 1)
111232809Sjmallett    return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
112232809Sjmallett
113232809Sjmallett  if (**p != tag)
114232809Sjmallett    return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
115232809Sjmallett
116232809Sjmallett  (*p)++;
117232809Sjmallett
118232809Sjmallett  return svn_error_trace(asn1_get_len(p, end, len));
119232809Sjmallett}
120232809Sjmallett
121232809Sjmallettstatic svn_error_t *
122232809Sjmallettasn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
123232809Sjmallett{
124232809Sjmallett  ptrdiff_t len;
125232809Sjmallett
126232809Sjmallett  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
127232809Sjmallett
128232809Sjmallett  /* Reject bit patterns that would overflow the output and those that
129232809Sjmallett     represent negative values. */
130232809Sjmallett  if (len > (int)sizeof(int) || (**p & 0x80) != 0)
131232809Sjmallett    return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
132232809Sjmallett
133232809Sjmallett  *val = 0;
134232809Sjmallett
135232809Sjmallett  while (len-- > 0) {
136232809Sjmallett    /* This would be undefined for bit-patterns of negative values. */
137232809Sjmallett    *val = (*val << 8) | **p;
138232809Sjmallett    (*p)++;
139232809Sjmallett  }
140232809Sjmallett
141232809Sjmallett  return SVN_NO_ERROR;
142232809Sjmallett}
143232809Sjmallett
144232809Sjmallettstatic svn_boolean_t
145232809Sjmallettequal(const void *left, apr_size_t left_len,
146232809Sjmallett      const void *right, apr_size_t right_len)
147232809Sjmallett{
148232809Sjmallett  if (left_len != right_len)
149232809Sjmallett    return FALSE;
150232809Sjmallett
151232809Sjmallett  return memcmp(left, right, right_len) == 0;
152232809Sjmallett}
153232809Sjmallett
154232809Sjmallettstatic svn_boolean_t
155232809Sjmallettoids_equal(x509_buf *left, x509_buf *right)
156232809Sjmallett{
157232809Sjmallett  return equal(left->p, left->len,
158232809Sjmallett               right->p, right->len);
159232809Sjmallett}
160232809Sjmallett
161232809Sjmallett/*
162232809Sjmallett *  Version   ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
163232809Sjmallett */
164232809Sjmallettstatic svn_error_t *
165232809Sjmallettx509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
166232809Sjmallett{
167232809Sjmallett  svn_error_t *err;
168232809Sjmallett  ptrdiff_t len;
169232809Sjmallett
170232809Sjmallett  /*
171232809Sjmallett   * As defined in the Basic Certificate fields:
172232809Sjmallett   *   version         [0]  EXPLICIT Version DEFAULT v1,
173232809Sjmallett   * the version is the context specific tag 0.
174232809Sjmallett   */
175232809Sjmallett  err = asn1_get_tag(p, end, &len,
176232809Sjmallett                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
177232809Sjmallett  if (err)
178232809Sjmallett    {
179232809Sjmallett      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
180232809Sjmallett        {
181232809Sjmallett          svn_error_clear(err);
182232809Sjmallett          *ver = 0;
183232809Sjmallett          return SVN_NO_ERROR;
184232809Sjmallett        }
185232809Sjmallett
186232809Sjmallett      return svn_error_trace(err);
187232809Sjmallett    }
188232809Sjmallett
189232809Sjmallett  end = *p + len;
190232809Sjmallett
191232809Sjmallett  err = asn1_get_int(p, end, ver);
192232809Sjmallett  if (err)
193232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
194232809Sjmallett
195232809Sjmallett  if (*p != end)
196232809Sjmallett    {
197232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
198232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
199232809Sjmallett    }
200232809Sjmallett
201232809Sjmallett  return SVN_NO_ERROR;
202232809Sjmallett}
203232809Sjmallett
204232809Sjmallett/*
205232809Sjmallett *  CertificateSerialNumber   ::=  INTEGER
206232809Sjmallett */
207232809Sjmallettstatic svn_error_t *
208232809Sjmallettx509_get_serial(const unsigned char **p,
209232809Sjmallett                const unsigned char *end, x509_buf * serial)
210232809Sjmallett{
211232809Sjmallett  svn_error_t *err;
212232809Sjmallett
213232809Sjmallett  if ((end - *p) < 1)
214232809Sjmallett    {
215232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
216232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
217232809Sjmallett    }
218232809Sjmallett
219232809Sjmallett  if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
220232809Sjmallett      **p != ASN1_INTEGER)
221232809Sjmallett    {
222232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
223232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
224232809Sjmallett    }
225232809Sjmallett
226232809Sjmallett  serial->tag = *(*p)++;
227232809Sjmallett
228232809Sjmallett  err = asn1_get_len(p, end, &serial->len);
229232809Sjmallett  if (err)
230232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
231232809Sjmallett
232232809Sjmallett  serial->p = *p;
233232809Sjmallett  *p += serial->len;
234232809Sjmallett
235232809Sjmallett  return SVN_NO_ERROR;
236232809Sjmallett}
237232809Sjmallett
238232809Sjmallett/*
239232809Sjmallett *  AlgorithmIdentifier   ::=  SEQUENCE  {
240232809Sjmallett *     algorithm         OBJECT IDENTIFIER,
241232809Sjmallett *     parameters        ANY DEFINED BY algorithm OPTIONAL  }
242232809Sjmallett */
243232809Sjmallettstatic svn_error_t *
244232809Sjmallettx509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
245232809Sjmallett{
246232809Sjmallett  svn_error_t *err;
247232809Sjmallett  ptrdiff_t len;
248232809Sjmallett
249232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
250232809Sjmallett  if (err)
251232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
252232809Sjmallett
253232809Sjmallett  end = *p + len;
254232809Sjmallett  alg->tag = **p;
255232809Sjmallett
256232809Sjmallett  err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
257232809Sjmallett  if (err)
258232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
259232809Sjmallett
260232809Sjmallett  alg->p = *p;
261232809Sjmallett  *p += alg->len;
262232809Sjmallett
263232809Sjmallett  if (*p == end)
264232809Sjmallett    return SVN_NO_ERROR;
265232809Sjmallett
266232809Sjmallett  /*
267232809Sjmallett   * assume the algorithm parameters must be NULL
268232809Sjmallett   */
269232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_NULL);
270232809Sjmallett  if (err)
271232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
272232809Sjmallett
273232809Sjmallett  if (*p != end)
274232809Sjmallett    {
275232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
276232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
277232809Sjmallett    }
278232809Sjmallett
279232809Sjmallett  return SVN_NO_ERROR;
280232809Sjmallett}
281232809Sjmallett
282232809Sjmallett/*
283232809Sjmallett *  AttributeTypeAndValue ::= SEQUENCE {
284232809Sjmallett *    type     AttributeType,
285232809Sjmallett *    value     AttributeValue }
286232809Sjmallett *
287232809Sjmallett *  AttributeType ::= OBJECT IDENTIFIER
288232809Sjmallett *
289232809Sjmallett *  AttributeValue ::= ANY DEFINED BY AttributeType
290232809Sjmallett */
291232809Sjmallettstatic svn_error_t *
292232809Sjmallettx509_get_attribute(const unsigned char **p, const unsigned char *end,
293232809Sjmallett                   x509_name *cur, apr_pool_t *result_pool)
294232809Sjmallett{
295232809Sjmallett  svn_error_t *err;
296232809Sjmallett  ptrdiff_t len;
297232809Sjmallett  x509_buf *oid;
298232809Sjmallett  x509_buf *val;
299232809Sjmallett
300232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
301232809Sjmallett  if (err)
302232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
303232809Sjmallett
304232809Sjmallett  end = *p + len;
305232809Sjmallett
306232809Sjmallett  oid = &cur->oid;
307232809Sjmallett
308232809Sjmallett  err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
309232809Sjmallett  if (err)
310232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
311232809Sjmallett
312232809Sjmallett  oid->tag = ASN1_OID;
313232809Sjmallett  oid->p = *p;
314232809Sjmallett  *p += oid->len;
315232809Sjmallett
316232809Sjmallett  if ((end - *p) < 1)
317232809Sjmallett    {
318232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
319232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
320232809Sjmallett    }
321232809Sjmallett
322232809Sjmallett  if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
323232809Sjmallett      **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
324232809Sjmallett      **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
325232809Sjmallett    {
326232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
327232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
328232809Sjmallett    }
329232809Sjmallett
330232809Sjmallett  val = &cur->val;
331232809Sjmallett  val->tag = *(*p)++;
332232809Sjmallett
333232809Sjmallett  err = asn1_get_len(p, end, &val->len);
334232809Sjmallett  if (err)
335232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
336232809Sjmallett
337232809Sjmallett  val->p = *p;
338232809Sjmallett  *p += val->len;
339232809Sjmallett
340232809Sjmallett  cur->next = NULL;
341232809Sjmallett
342232809Sjmallett  if (*p != end)
343232809Sjmallett    {
344232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
345232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
346232809Sjmallett    }
347232809Sjmallett
348232809Sjmallett  return SVN_NO_ERROR;
349232809Sjmallett}
350232809Sjmallett
351232809Sjmallett/*
352232809Sjmallett *   RelativeDistinguishedName ::=
353232809Sjmallett *   SET SIZE (1..MAX) OF AttributeTypeAndValue
354232809Sjmallett */
355232809Sjmallettstatic svn_error_t *
356232809Sjmallettx509_get_name(const unsigned char **p, const unsigned char *name_end,
357232809Sjmallett              x509_name *name, apr_pool_t *result_pool)
358232809Sjmallett{
359232809Sjmallett  svn_error_t *err;
360232809Sjmallett  ptrdiff_t len;
361232809Sjmallett  const unsigned char *set_end;
362232809Sjmallett  x509_name *cur = NULL;
363232809Sjmallett
364232809Sjmallett  err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET);
365232809Sjmallett  if (err)
366232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
367232809Sjmallett
368232809Sjmallett  set_end = *p + len;
369232809Sjmallett
370232809Sjmallett  /*
371232809Sjmallett   * iterate until the end of the SET is reached
372232809Sjmallett   */
373232809Sjmallett  while (*p < set_end)
374232809Sjmallett    {
375232809Sjmallett      if (!cur)
376232809Sjmallett        {
377232809Sjmallett          cur = name;
378232809Sjmallett        }
379232809Sjmallett      else
380232809Sjmallett        {
381232809Sjmallett          cur->next = apr_palloc(result_pool, sizeof(x509_name));
382232809Sjmallett          cur = cur->next;
383232809Sjmallett        }
384232809Sjmallett      SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool));
385232809Sjmallett    }
386232809Sjmallett
387232809Sjmallett  /*
388232809Sjmallett   * recurse until end of SEQUENCE (name) is reached
389232809Sjmallett   */
390232809Sjmallett  if (*p == name_end)
391232809Sjmallett    return SVN_NO_ERROR;
392232809Sjmallett
393232809Sjmallett  cur->next = apr_palloc(result_pool, sizeof(x509_name));
394232809Sjmallett
395232809Sjmallett  return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool));
396232809Sjmallett}
397232809Sjmallett
398232809Sjmallett/* Retrieve the date from the X.509 cert data between *P and END in either
399232809Sjmallett * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
400232809Sjmallett * 4.1.2.5.2 respectively) and place the result in WHEN using  SCRATCH_POOL
401232809Sjmallett * for temporary allocations. */
402232809Sjmallettstatic svn_error_t *
403232809Sjmallettx509_get_date(apr_time_t *when,
404232809Sjmallett              const unsigned char **p,
405232809Sjmallett              const unsigned char *end,
406232809Sjmallett              apr_pool_t *scratch_pool)
407232809Sjmallett{
408232809Sjmallett  svn_error_t *err;
409232809Sjmallett  apr_status_t ret;
410232809Sjmallett  int tag;
411232809Sjmallett  ptrdiff_t len;
412232809Sjmallett  char *date;
413232809Sjmallett  apr_time_exp_t xt = { 0 };
414232809Sjmallett  char tz;
415232809Sjmallett
416232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
417232809Sjmallett  if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
418232809Sjmallett    {
419232809Sjmallett      svn_error_clear(err);
420232809Sjmallett      err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
421232809Sjmallett      tag = ASN1_GENERALIZED_TIME;
422232809Sjmallett    }
423232809Sjmallett  else
424232809Sjmallett    {
425232809Sjmallett      tag = ASN1_UTC_TIME;
426232809Sjmallett    }
427232809Sjmallett  if (err)
428232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
429232809Sjmallett
430232809Sjmallett  date = apr_pstrndup(scratch_pool, (const char *) *p, len);
431232809Sjmallett  switch (tag)
432232809Sjmallett    {
433232809Sjmallett    case ASN1_UTC_TIME:
434232809Sjmallett      if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
435232809Sjmallett                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
436232809Sjmallett                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
437232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
438232809Sjmallett
439232809Sjmallett      /* UTCTime only provides a 2 digit year.  X.509 specifies that years
440232809Sjmallett       * greater than or equal to 50 must be interpreted as 19YY and years
441232809Sjmallett       * less than 50 be interpreted as 20YY.  This format is not used for
442232809Sjmallett       * years greater than 2049. apr_time_exp_t wants years as the number
443232809Sjmallett       * of years since 1900, so don't convert to 4 digits here. */
444232809Sjmallett      xt.tm_year += 100 * (xt.tm_year < 50);
445232809Sjmallett      break;
446232809Sjmallett
447232809Sjmallett    case ASN1_GENERALIZED_TIME:
448232809Sjmallett      if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
449232809Sjmallett                 &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
450232809Sjmallett                 &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
451232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
452232809Sjmallett
453232809Sjmallett      /* GeneralizedTime has the full 4 digit year.  But apr_time_exp_t
454232809Sjmallett       * wants years as the number of years since 1900. */
455232809Sjmallett      xt.tm_year -= 1900;
456232809Sjmallett      break;
457232809Sjmallett
458232809Sjmallett    default:
459232809Sjmallett      /* shouldn't ever get here because we should error out above in the
460232809Sjmallett       * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
461232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
462232809Sjmallett      break;
463232809Sjmallett    }
464232809Sjmallett
465232809Sjmallett  /* check that the timezone is GMT
466232809Sjmallett   * ASN.1 allows for the timezone to be specified but X.509 says it must
467232809Sjmallett   * always be GMT.  A little bit of extra paranoia here seems like a good
468232809Sjmallett   * idea. */
469232809Sjmallett  if (tz != 'Z')
470232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
471232809Sjmallett
472232809Sjmallett  /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
473232809Sjmallett  xt.tm_mon -= 1;
474232809Sjmallett
475232809Sjmallett  ret = apr_time_exp_gmt_get(when, &xt);
476232809Sjmallett  if (ret)
477232809Sjmallett    return svn_error_wrap_apr(ret, NULL);
478232809Sjmallett
479232809Sjmallett  *p += len;
480232809Sjmallett
481232809Sjmallett  return SVN_NO_ERROR;
482232809Sjmallett}
483232809Sjmallett
484232809Sjmallett/*
485232809Sjmallett *  Validity ::= SEQUENCE {
486232809Sjmallett *     notBefore    Time,
487232809Sjmallett *     notAfter    Time }
488232809Sjmallett *
489232809Sjmallett *  Time ::= CHOICE {
490232809Sjmallett *     utcTime    UTCTime,
491232809Sjmallett *     generalTime  GeneralizedTime }
492232809Sjmallett */
493232809Sjmallettstatic svn_error_t *
494232809Sjmallettx509_get_dates(apr_time_t *from,
495232809Sjmallett               apr_time_t *to,
496232809Sjmallett               const unsigned char **p,
497232809Sjmallett               const unsigned char *end,
498232809Sjmallett               apr_pool_t *scratch_pool)
499232809Sjmallett{
500232809Sjmallett  svn_error_t *err;
501232809Sjmallett  ptrdiff_t len;
502232809Sjmallett
503232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
504232809Sjmallett  if (err)
505232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
506232809Sjmallett
507232809Sjmallett  end = *p + len;
508232809Sjmallett
509232809Sjmallett  SVN_ERR(x509_get_date(from, p, end, scratch_pool));
510232809Sjmallett
511232809Sjmallett  SVN_ERR(x509_get_date(to, p, end, scratch_pool));
512232809Sjmallett
513232809Sjmallett  if (*p != end)
514232809Sjmallett    {
515232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
516232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
517232809Sjmallett    }
518232809Sjmallett
519232809Sjmallett  return SVN_NO_ERROR;
520232809Sjmallett}
521232809Sjmallett
522232809Sjmallettstatic svn_error_t *
523232809Sjmallettx509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
524232809Sjmallett{
525232809Sjmallett  svn_error_t *err;
526232809Sjmallett  ptrdiff_t len;
527232809Sjmallett
528232809Sjmallett  err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
529232809Sjmallett  if (err)
530232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
531232809Sjmallett
532232809Sjmallett  sig->tag = ASN1_BIT_STRING;
533232809Sjmallett
534232809Sjmallett  if (--len < 1 || *(*p)++ != 0)
535232809Sjmallett    return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
536232809Sjmallett
537232809Sjmallett  sig->len = len;
538232809Sjmallett  sig->p = *p;
539232809Sjmallett
540232809Sjmallett  *p += len;
541232809Sjmallett
542232809Sjmallett  return SVN_NO_ERROR;
543232809Sjmallett}
544232809Sjmallett
545232809Sjmallett/*
546232809Sjmallett * X.509 v2/v3 unique identifier (not parsed)
547232809Sjmallett */
548232809Sjmallettstatic svn_error_t *
549232809Sjmallettx509_get_uid(const unsigned char **p,
550232809Sjmallett             const unsigned char *end, x509_buf * uid, int n)
551232809Sjmallett{
552232809Sjmallett  svn_error_t *err;
553232809Sjmallett
554232809Sjmallett  if (*p == end)
555232809Sjmallett    return SVN_NO_ERROR;
556232809Sjmallett
557232809Sjmallett  err = asn1_get_tag(p, end, &uid->len,
558232809Sjmallett                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
559232809Sjmallett  if (err)
560232809Sjmallett    {
561232809Sjmallett      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
562232809Sjmallett        {
563232809Sjmallett          svn_error_clear(err);
564232809Sjmallett          return SVN_NO_ERROR;
565232809Sjmallett        }
566232809Sjmallett
567232809Sjmallett      return svn_error_trace(err);
568232809Sjmallett    }
569232809Sjmallett
570232809Sjmallett  uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n;
571232809Sjmallett  uid->p = *p;
572232809Sjmallett  *p += uid->len;
573232809Sjmallett
574232809Sjmallett  return SVN_NO_ERROR;
575232809Sjmallett}
576232809Sjmallett
577232809Sjmallett/*
578232809Sjmallett * X.509 v3 extensions (not parsed)
579232809Sjmallett */
580232809Sjmallettstatic svn_error_t *
581232809Sjmallettx509_get_ext(apr_array_header_t *dnsnames,
582232809Sjmallett             const unsigned char **p,
583232809Sjmallett             const unsigned char *end)
584232809Sjmallett{
585232809Sjmallett  svn_error_t *err;
586232809Sjmallett  ptrdiff_t len;
587232809Sjmallett
588232809Sjmallett  if (*p == end)
589232809Sjmallett    return SVN_NO_ERROR;
590232809Sjmallett
591232809Sjmallett  err = asn1_get_tag(p, end, &len,
592232809Sjmallett                     ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
593232809Sjmallett  if (err)
594232809Sjmallett    {
595232809Sjmallett      /* If there aren't extensions that's ok they aren't required */
596232809Sjmallett      if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
597232809Sjmallett        {
598232809Sjmallett          svn_error_clear(err);
599232809Sjmallett          return SVN_NO_ERROR;
600232809Sjmallett        }
601232809Sjmallett
602232809Sjmallett      return svn_error_trace(err);
603232809Sjmallett    }
604232809Sjmallett
605232809Sjmallett  end = *p + len;
606232809Sjmallett
607232809Sjmallett  SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
608232809Sjmallett
609232809Sjmallett  if (end != *p + len)
610232809Sjmallett    {
611232809Sjmallett      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
612232809Sjmallett      return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
613232809Sjmallett    }
614232809Sjmallett
615232809Sjmallett  while (*p < end)
616232809Sjmallett    {
617232809Sjmallett      ptrdiff_t ext_len;
618232809Sjmallett      const unsigned char *ext_start, *sna_end;
619232809Sjmallett      err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
620232809Sjmallett      if (err)
621232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
622232809Sjmallett                                NULL);
623232809Sjmallett      ext_start = *p;
624232809Sjmallett
625232809Sjmallett      err = asn1_get_tag(p, end, &len, ASN1_OID);
626232809Sjmallett      if (err)
627232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
628232809Sjmallett                                NULL);
629232809Sjmallett
630232809Sjmallett      /* skip all extensions except SubjectAltName */
631232809Sjmallett      if (!equal(*p, len,
632232809Sjmallett                 OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1))
633232809Sjmallett        {
634232809Sjmallett          *p += ext_len - (*p - ext_start);
635232809Sjmallett          continue;
636232809Sjmallett        }
637232809Sjmallett      *p += len;
638232809Sjmallett
639232809Sjmallett      err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
640232809Sjmallett      if (err)
641232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
642232809Sjmallett                                NULL);
643232809Sjmallett
644232809Sjmallett      /*   SubjectAltName ::= GeneralNames
645232809Sjmallett
646232809Sjmallett           GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
647232809Sjmallett
648232809Sjmallett           GeneralName ::= CHOICE {
649232809Sjmallett                other Name                      [0]     OtherName,
650232809Sjmallett                rfc822Name                      [1]     IA5String,
651232809Sjmallett                dNSName                         [2]     IA5String,
652232809Sjmallett                x400Address                     [3]     ORAddress,
653232809Sjmallett                directoryName                   [4]     Name,
654232809Sjmallett                ediPartyName                    [5]     EDIPartyName,
655232809Sjmallett                uniformResourceIdentifier       [6]     IA5String,
656232809Sjmallett                iPAddress                       [7]     OCTET STRING,
657232809Sjmallett                registeredID                    [8]     OBJECT IDENTIFIER } */
658232809Sjmallett      sna_end = *p + len;
659232809Sjmallett
660232809Sjmallett      err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
661232809Sjmallett      if (err)
662232809Sjmallett        return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
663232809Sjmallett                                NULL);
664232809Sjmallett
665232809Sjmallett      if (sna_end != *p + len)
666232809Sjmallett        {
667232809Sjmallett          err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
668232809Sjmallett          return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
669232809Sjmallett        }
670232809Sjmallett
671232809Sjmallett      while (*p < sna_end)
672232809Sjmallett        {
673232809Sjmallett          err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
674232809Sjmallett                             ASN1_PRIMITIVE | 2);
675232809Sjmallett          if (err)
676232809Sjmallett            {
677232809Sjmallett              /* not not a dNSName */
678232809Sjmallett              if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
679232809Sjmallett                {
680232809Sjmallett                  svn_error_clear(err);
681232809Sjmallett                  /* need to skip the tag and then find the length to
682232809Sjmallett                   * skip to ignore this SNA entry. */
683232809Sjmallett                  (*p)++;
684232809Sjmallett                  SVN_ERR(asn1_get_len(p, sna_end, &len));
685232809Sjmallett                  *p += len;
686232809Sjmallett                  continue;
687232809Sjmallett                }
688232809Sjmallett
689232809Sjmallett              return svn_error_trace(err);
690            }
691          else
692            {
693              /* We found a dNSName entry */
694              x509_buf *dnsname = apr_palloc(dnsnames->pool,
695                                             sizeof(x509_buf));
696              dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
697              dnsname->len = len;
698              dnsname->p = *p;
699              APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
700            }
701
702          *p += len;
703        }
704
705    }
706
707  return SVN_NO_ERROR;
708}
709
710/* Escape all non-ascii or control characters similar to
711 * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy().
712 * All of the encoding formats somewhat overlap with ascii (BMPString
713 * and UniversalString are actually always wider so you'll end up
714 * with a bunch of escaped nul bytes, but ideally we don't get here
715 * for those).  The result is always a nul-terminated C string. */
716static const char *
717fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
718{
719  const char *end = src->data + src->len;
720  const char *p = src->data, *q;
721  svn_stringbuf_t *outstr;
722  char escaped_char[6]; /* ? \ u u u \0 */
723
724  for (q = p; q < end; q++)
725    {
726      if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
727        break;
728    }
729
730  if (q == end)
731    return src->data;
732
733  outstr = svn_stringbuf_create_empty(result_pool);
734  while (1)
735    {
736      q = p;
737
738      /* Traverse till either unsafe character or eos. */
739      while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
740        q++;
741
742      /* copy chunk before marker */
743      svn_stringbuf_appendbytes(outstr, p, q - p);
744
745      if (q == end)
746        break;
747
748      apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
749                   (unsigned char) *q);
750      svn_stringbuf_appendcstr(outstr, escaped_char);
751
752      p = q + 1;
753    }
754
755  return outstr->data;
756}
757
758/* Escape only NUL characters from a string that is presumed to
759 * be UTF-8 encoded and return a nul-terminated C string. */
760static const char *
761nul_escape(const svn_string_t *src, apr_pool_t *result_pool)
762{
763  const char *end = src->data + src->len;
764  const char *p = src->data, *q;
765  svn_stringbuf_t *outstr;
766
767  for (q = p; q < end; q++)
768    {
769      if (*q == '\0')
770        break;
771    }
772
773  if (q == end)
774    return src->data;
775
776  outstr = svn_stringbuf_create_empty(result_pool);
777  while (1)
778    {
779      q = p;
780
781      /* Traverse till either unsafe character or eos. */
782      while (q < end && *q != '\0')
783        q++;
784
785      /* copy chunk before marker */
786      svn_stringbuf_appendbytes(outstr, p, q - p);
787
788      if (q == end)
789        break;
790
791      svn_stringbuf_appendcstr(outstr, "?\\000");
792
793      p = q + 1;
794    }
795
796  return outstr->data;
797}
798
799
800/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
801   ISO-8859-1 is a strict subset of Unicode. */
802static svn_error_t *
803latin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
804               apr_pool_t *result_pool)
805{
806  apr_int32_t *ucs4buf;
807  svn_membuf_t resultbuf;
808  apr_size_t length;
809  apr_size_t i;
810  svn_string_t *res;
811
812  ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf));
813  for (i = 0; i < src->len; ++i)
814    ucs4buf[i] = (unsigned char)(src->data[i]);
815
816  svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
817  SVN_ERR(svn_utf__encode_ucs4_string(
818              &resultbuf, ucs4buf, src->len, &length));
819
820  res = apr_palloc(result_pool, sizeof(*res));
821  res->data = resultbuf.data;
822  res->len = length;
823  *result = res;
824  return SVN_NO_ERROR;
825}
826
827/* Make a best effort to convert a X.509 name to a UTF-8 encoded
828 * string and return it.  If we can't properly convert just do a
829 * fuzzy conversion so we have something to display. */
830static const char *
831x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
832{
833  const svn_string_t *src_string;
834  const svn_string_t *utf8_string;
835  svn_error_t *err;
836
837  src_string = svn_string_ncreate((const char *)name->val.p,
838                                  name->val.len,
839                                  result_pool);
840  switch (name->val.tag)
841    {
842    case ASN1_UTF8_STRING:
843      if (svn_utf__is_valid(src_string->data, src_string->len))
844        return nul_escape(src_string, result_pool);
845      else
846        /* not a valid UTF-8 string, who knows what it is,
847         * so run it through the fuzzy_escape code.  */
848        return fuzzy_escape(src_string, result_pool);
849      break;
850
851      /* Both BMP and UNIVERSAL should always be in Big Endian (aka
852       * network byte order).  But rumor has it that there are certs
853       * out there with other endianess and even Byte Order Marks.
854       * If we actually run into these, we might need to do something
855       * about it. */
856
857    case ASN1_BMP_STRING:
858      if (0 != src_string->len % sizeof(apr_uint16_t))
859          return fuzzy_escape(src_string, result_pool);
860      err = svn_utf__utf16_to_utf8(&utf8_string,
861                                   (const void*)(src_string->data),
862                                   src_string->len / sizeof(apr_uint16_t),
863                                   TRUE, result_pool, result_pool);
864      break;
865
866    case ASN1_UNIVERSAL_STRING:
867      if (0 != src_string->len % sizeof(apr_int32_t))
868          return fuzzy_escape(src_string, result_pool);
869      err = svn_utf__utf32_to_utf8(&utf8_string,
870                                   (const void*)(src_string->data),
871                                   src_string->len / sizeof(apr_int32_t),
872                                   TRUE, result_pool, result_pool);
873      break;
874
875      /* Despite what all the IETF, ISO, ITU bits say everything out
876       * on the Internet that I can find treats this as ISO-8859-1.
877       * Even the name is misleading, it's not actually T.61.  All the
878       * gory details can be found in the Character Sets section of:
879       * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt
880       */
881    case ASN1_T61_STRING:
882      err = latin1_to_utf8(&utf8_string, src_string, result_pool);
883      break;
884
885      /* This leaves two types out there in the wild.  PrintableString,
886       * which is just a subset of ASCII and IA5 which is ASCII (though
887       * 0x24 '$' and 0x23 '#' may be defined with differnet symbols
888       * depending on the location, in practice it seems everyone just
889       * treats it as ASCII).  Since these are just ASCII run through
890       * the fuzzy_escape code to deal with anything that isn't actually
891       * ASCII.  There shouldn't be any other types here but if we find
892       * a cert with some other encoding, the best we can do is the
893       * fuzzy_escape().  Note: Technically IA5 isn't valid in this
894       * context, however in the real world it may pop up. */
895    default:
896      return fuzzy_escape(src_string, result_pool);
897    }
898
899  if (err)
900    {
901      svn_error_clear(err);
902      return fuzzy_escape(src_string, result_pool);
903    }
904
905  return nul_escape(utf8_string, result_pool);
906}
907
908static svn_error_t *
909x509_name_to_certinfo(apr_array_header_t **result,
910                      const x509_name *dn,
911                      apr_pool_t *scratch_pool,
912                      apr_pool_t *result_pool)
913{
914  const x509_name *name = dn;
915
916  *result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *));
917
918  while (name != NULL) {
919    svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t));
920
921    attr->oid_len = name->oid.len;
922    attr->oid = apr_palloc(result_pool, attr->oid_len);
923    memcpy(attr->oid, name->oid.p, attr->oid_len);
924    attr->utf8_value = x509name_to_utf8_string(name, result_pool);
925    if (!attr->utf8_value)
926      /* this should never happen */
927      attr->utf8_value = apr_pstrdup(result_pool, "??");
928    APR_ARRAY_PUSH(*result, const svn_x509_name_attr_t *) = attr;
929
930    name = name->next;
931  }
932
933  return SVN_NO_ERROR;
934}
935
936static svn_boolean_t
937is_hostname(const char *str)
938{
939  apr_size_t i, len = strlen(str);
940
941  for (i = 0; i < len; i++)
942    {
943      char c = str[i];
944
945      /* '-' is only legal when not at the start or end of a label */
946      if (c == '-')
947        {
948          if (i + 1 != len)
949            {
950              if (str[i + 1] == '.')
951                return FALSE; /* '-' preceeds a '.' */
952            }
953          else
954            return FALSE; /* '-' is at end of string */
955
956          /* determine the previous character. */
957          if (i == 0)
958            return FALSE; /* '-' is at start of string */
959          else
960            if (str[i - 1] == '.')
961              return FALSE; /* '-' follows a '.' */
962        }
963      else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
964        return FALSE; /* some character not allowed */
965    }
966
967  return TRUE;
968}
969
970static const char *
971x509parse_get_cn(apr_array_header_t *subject)
972{
973  int i;
974
975  for (i = 0; i < subject->nelts; ++i)
976    {
977      const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(subject, i, const svn_x509_name_attr_t *);
978      if (equal(attr->oid, attr->oid_len,
979                SVN_X509_OID_COMMON_NAME, sizeof(SVN_X509_OID_COMMON_NAME) - 1))
980        return attr->utf8_value;
981    }
982
983  return NULL;
984}
985
986
987static void
988x509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt,
989                        apr_pool_t *result_pool, apr_pool_t *scratch_pool)
990{
991  ci->hostnames = NULL;
992
993  if (crt->dnsnames->nelts > 0)
994    {
995      int i;
996
997      ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts,
998                                     sizeof(const char*));
999
1000      /* Subject Alt Names take priority */
1001      for (i = 0; i < crt->dnsnames->nelts; i++)
1002        {
1003          x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *);
1004          const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p,
1005                                                        dnsname->len,
1006                                                        scratch_pool);
1007
1008          APR_ARRAY_PUSH(ci->hostnames, const char*)
1009            = fuzzy_escape(temp, result_pool);
1010        }
1011    }
1012  else
1013    {
1014      /* no SAN then get the hostname from the CommonName on the cert */
1015      const char *utf8_value;
1016
1017      utf8_value = x509parse_get_cn(ci->subject);
1018
1019      if (utf8_value && is_hostname(utf8_value))
1020        {
1021          ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*));
1022          APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value;
1023        }
1024    }
1025}
1026
1027/*
1028 * Parse one certificate.
1029 */
1030svn_error_t *
1031svn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
1032                    const char *buf,
1033                    apr_size_t buflen,
1034                    apr_pool_t *result_pool,
1035                    apr_pool_t *scratch_pool)
1036{
1037  svn_error_t *err;
1038  ptrdiff_t len;
1039  const unsigned char *p;
1040  const unsigned char *end;
1041  x509_cert *crt;
1042  svn_x509_certinfo_t *ci;
1043
1044  crt = apr_pcalloc(scratch_pool, sizeof(*crt));
1045  p = (const unsigned char *)buf;
1046  len = buflen;
1047  end = p + len;
1048
1049  /*
1050   * Certificate  ::=      SEQUENCE  {
1051   *              tbsCertificate           TBSCertificate,
1052   *              signatureAlgorithm       AlgorithmIdentifier,
1053   *              signatureValue           BIT STRING      }
1054   */
1055  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1056  if (err)
1057    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1058
1059  if (len != (end - p))
1060    {
1061      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1062      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1063    }
1064
1065  /*
1066   * TBSCertificate  ::=  SEQUENCE  {
1067   */
1068  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1069  if (err)
1070    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1071
1072  end = p + len;
1073
1074  /*
1075   * Version      ::=      INTEGER  {      v1(0), v2(1), v3(2)  }
1076   *
1077   * CertificateSerialNumber      ::=      INTEGER
1078   *
1079   * signature                    AlgorithmIdentifier
1080   */
1081  SVN_ERR(x509_get_version(&p, end, &crt->version));
1082  SVN_ERR(x509_get_serial(&p, end, &crt->serial));
1083  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1));
1084
1085  crt->version++;
1086
1087  if (crt->version > 3)
1088    return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
1089
1090  /*
1091   * issuer                               Name
1092   */
1093  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1094  if (err)
1095    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1096
1097  SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
1098
1099  /*
1100   * Validity ::= SEQUENCE {
1101   *              notBefore          Time,
1102   *              notAfter           Time }
1103   *
1104   */
1105  SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
1106                         scratch_pool));
1107
1108  /*
1109   * subject                              Name
1110   */
1111  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1112  if (err)
1113    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1114
1115  SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
1116
1117  /*
1118   * SubjectPublicKeyInfo  ::=  SEQUENCE
1119   *              algorithm                        AlgorithmIdentifier,
1120   *              subjectPublicKey         BIT STRING      }
1121   */
1122  err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1123  if (err)
1124    return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1125
1126  /* Skip pubkey. */
1127  p += len;
1128
1129  /*
1130   *      issuerUniqueID  [1]      IMPLICIT UniqueIdentifier OPTIONAL,
1131   *                                               -- If present, version shall be v2 or v3
1132   *      subjectUniqueID [2]      IMPLICIT UniqueIdentifier OPTIONAL,
1133   *                                               -- If present, version shall be v2 or v3
1134   *      extensions              [3]      EXPLICIT Extensions OPTIONAL
1135   *                                               -- If present, version shall be v3
1136   */
1137  crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
1138
1139  /* Try to parse issuerUniqueID, subjectUniqueID and extensions for *every*
1140   * version (X.509 v1, v2 and v3), not just v2 or v3.  If they aren't present,
1141   * we are fine, but we don't want to throw an error if they are.  v1 and v2
1142   * certificates with the corresponding extra fields are ill-formed per RFC
1143   * 5280 s. 4.1, but we suspect they could exist in the real world.  Other
1144   * X.509 parsers (e.g., within OpenSSL or Microsoft CryptoAPI) aren't picky
1145   * about these certificates, and we also allow them. */
1146  SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1));
1147  SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2));
1148  SVN_ERR(x509_get_ext(crt->dnsnames, &p, end));
1149
1150  if (p != end)
1151    {
1152      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1153      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1154    }
1155
1156  end = (const unsigned char*) buf + buflen;
1157
1158  /*
1159   *      signatureAlgorithm       AlgorithmIdentifier,
1160   *      signatureValue           BIT STRING
1161   */
1162  SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
1163
1164  if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2))
1165    return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
1166
1167  SVN_ERR(x509_get_sig(&p, end, &crt->sig));
1168
1169  if (p != end)
1170    {
1171      err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
1172      return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1173    }
1174
1175  ci = apr_pcalloc(result_pool, sizeof(*ci));
1176
1177  /* Get the subject name */
1178  SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject,
1179                                scratch_pool, result_pool));
1180
1181  /* Get the issuer name */
1182  SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer,
1183                                scratch_pool, result_pool));
1184
1185  /* Copy the validity range */
1186  ci->valid_from = crt->valid_from;
1187  ci->valid_to = crt->valid_to;
1188
1189  /* Calculate the SHA1 digest of the certificate, otherwise known as
1190    the fingerprint */
1191  SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
1192                       result_pool));
1193
1194  /* Construct the array of host names */
1195  x509parse_get_hostnames(ci, crt, result_pool, scratch_pool);
1196
1197  *certinfo = ci;
1198  return SVN_NO_ERROR;
1199}
1200
1201