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