xref: /NextBSD/contrib/subversion/subversion/libsvn_subr/x509parse.c (revision 84d351007654069f9643c8e4b4802a7f5f08ee42)
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   /*
267    * assume the algorithm parameters must be NULL
268    */
269   err = asn1_get_tag(p, end, &len, ASN1_NULL);
270   if (err)
271     return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
272 
273   if (*p != end)
274     {
275       err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
276       return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
277     }
278 
279   return SVN_NO_ERROR;
280 }
281 
282 /*
283  *  AttributeTypeAndValue ::= SEQUENCE {
284  *    type     AttributeType,
285  *    value     AttributeValue }
286  *
287  *  AttributeType ::= OBJECT IDENTIFIER
288  *
289  *  AttributeValue ::= ANY DEFINED BY AttributeType
290  */
291 static svn_error_t *
x509_get_attribute(const unsigned char ** p,const unsigned char * end,x509_name * cur,apr_pool_t * result_pool)292 x509_get_attribute(const unsigned char **p, const unsigned char *end,
293                    x509_name *cur, apr_pool_t *result_pool)
294 {
295   svn_error_t *err;
296   ptrdiff_t len;
297   x509_buf *oid;
298   x509_buf *val;
299 
300   err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
301   if (err)
302     return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
303 
304   end = *p + len;
305 
306   oid = &cur->oid;
307 
308   err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
309   if (err)
310     return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
311 
312   oid->tag = ASN1_OID;
313   oid->p = *p;
314   *p += oid->len;
315 
316   if ((end - *p) < 1)
317     {
318       err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
319       return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
320     }
321 
322   if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
323       **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
324       **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
325     {
326       err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
327       return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
328     }
329 
330   val = &cur->val;
331   val->tag = *(*p)++;
332 
333   err = asn1_get_len(p, end, &val->len);
334   if (err)
335     return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
336 
337   val->p = *p;
338   *p += val->len;
339 
340   cur->next = NULL;
341 
342   if (*p != end)
343     {
344       err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
345       return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
346     }
347 
348   return SVN_NO_ERROR;
349 }
350 
351 /*
352  *   RelativeDistinguishedName ::=
353  *   SET SIZE (1..MAX) OF AttributeTypeAndValue
354  */
355 static svn_error_t *
x509_get_name(const unsigned char ** p,const unsigned char * name_end,x509_name * name,apr_pool_t * result_pool)356 x509_get_name(const unsigned char **p, const unsigned char *name_end,
357               x509_name *name, apr_pool_t *result_pool)
358 {
359   svn_error_t *err;
360   ptrdiff_t len;
361   const unsigned char *set_end;
362   x509_name *cur = NULL;
363 
364   err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET);
365   if (err)
366     return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
367 
368   set_end = *p + len;
369 
370   /*
371    * iterate until the end of the SET is reached
372    */
373   while (*p < set_end)
374     {
375       if (!cur)
376         {
377           cur = name;
378         }
379       else
380         {
381           cur->next = apr_palloc(result_pool, sizeof(x509_name));
382           cur = cur->next;
383         }
384       SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool));
385     }
386 
387   /*
388    * recurse until end of SEQUENCE (name) is reached
389    */
390   if (*p == name_end)
391     return SVN_NO_ERROR;
392 
393   cur->next = apr_palloc(result_pool, sizeof(x509_name));
394 
395   return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool));
396 }
397 
398 /* Retrieve the date from the X.509 cert data between *P and END in either
399  * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
400  * 4.1.2.5.2 respectively) and place the result in WHEN using  SCRATCH_POOL
401  * for temporary allocations. */
402 static svn_error_t *
x509_get_date(apr_time_t * when,const unsigned char ** p,const unsigned char * end,apr_pool_t * scratch_pool)403 x509_get_date(apr_time_t *when,
404               const unsigned char **p,
405               const unsigned char *end,
406               apr_pool_t *scratch_pool)
407 {
408   svn_error_t *err;
409   apr_status_t ret;
410   int tag;
411   ptrdiff_t len;
412   char *date;
413   apr_time_exp_t xt = { 0 };
414   char tz;
415 
416   err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
417   if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
418     {
419       svn_error_clear(err);
420       err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
421       tag = ASN1_GENERALIZED_TIME;
422     }
423   else
424     {
425       tag = ASN1_UTC_TIME;
426     }
427   if (err)
428     return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
429 
430   date = apr_pstrndup(scratch_pool, (const char *) *p, len);
431   switch (tag)
432     {
433     case ASN1_UTC_TIME:
434       if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
435                  &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
436                  &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
437         return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
438 
439       /* UTCTime only provides a 2 digit year.  X.509 specifies that years
440        * greater than or equal to 50 must be interpreted as 19YY and years
441        * less than 50 be interpreted as 20YY.  This format is not used for
442        * years greater than 2049. apr_time_exp_t wants years as the number
443        * of years since 1900, so don't convert to 4 digits here. */
444       xt.tm_year += 100 * (xt.tm_year < 50);
445       break;
446 
447     case ASN1_GENERALIZED_TIME:
448       if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
449                  &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
450                  &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
451         return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
452 
453       /* GeneralizedTime has the full 4 digit year.  But apr_time_exp_t
454        * wants years as the number of years since 1900. */
455       xt.tm_year -= 1900;
456       break;
457 
458     default:
459       /* shouldn't ever get here because we should error out above in the
460        * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
461       return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
462       break;
463     }
464 
465   /* check that the timezone is GMT
466    * ASN.1 allows for the timezone to be specified but X.509 says it must
467    * always be GMT.  A little bit of extra paranoia here seems like a good
468    * idea. */
469   if (tz != 'Z')
470     return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
471 
472   /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
473   xt.tm_mon -= 1;
474 
475   ret = apr_time_exp_gmt_get(when, &xt);
476   if (ret)
477     return svn_error_wrap_apr(ret, NULL);
478 
479   *p += len;
480 
481   return SVN_NO_ERROR;
482 }
483 
484 /*
485  *  Validity ::= SEQUENCE {
486  *     notBefore    Time,
487  *     notAfter    Time }
488  *
489  *  Time ::= CHOICE {
490  *     utcTime    UTCTime,
491  *     generalTime  GeneralizedTime }
492  */
493 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)494 x509_get_dates(apr_time_t *from,
495                apr_time_t *to,
496                const unsigned char **p,
497                const unsigned char *end,
498                apr_pool_t *scratch_pool)
499 {
500   svn_error_t *err;
501   ptrdiff_t len;
502 
503   err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
504   if (err)
505     return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
506 
507   end = *p + len;
508 
509   SVN_ERR(x509_get_date(from, p, end, scratch_pool));
510 
511   SVN_ERR(x509_get_date(to, p, end, scratch_pool));
512 
513   if (*p != end)
514     {
515       err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
516       return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
517     }
518 
519   return SVN_NO_ERROR;
520 }
521 
522 static svn_error_t *
x509_get_sig(const unsigned char ** p,const unsigned char * end,x509_buf * sig)523 x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
524 {
525   svn_error_t *err;
526   ptrdiff_t len;
527 
528   err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
529   if (err)
530     return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
531 
532   sig->tag = ASN1_BIT_STRING;
533 
534   if (--len < 1 || *(*p)++ != 0)
535     return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
536 
537   sig->len = len;
538   sig->p = *p;
539 
540   *p += len;
541 
542   return SVN_NO_ERROR;
543 }
544 
545 /*
546  * X.509 v2/v3 unique identifier (not parsed)
547  */
548 static svn_error_t *
x509_get_uid(const unsigned char ** p,const unsigned char * end,x509_buf * uid,int n)549 x509_get_uid(const unsigned char **p,
550              const unsigned char *end, x509_buf * uid, int n)
551 {
552   svn_error_t *err;
553 
554   if (*p == end)
555     return SVN_NO_ERROR;
556 
557   err = asn1_get_tag(p, end, &uid->len,
558                      ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
559   if (err)
560     {
561       if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
562         {
563           svn_error_clear(err);
564           return SVN_NO_ERROR;
565         }
566 
567       return svn_error_trace(err);
568     }
569 
570   uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n;
571   uid->p = *p;
572   *p += uid->len;
573 
574   return SVN_NO_ERROR;
575 }
576 
577 /*
578  * X.509 v3 extensions (not parsed)
579  */
580 static svn_error_t *
x509_get_ext(apr_array_header_t * dnsnames,const unsigned char ** p,const unsigned char * end)581 x509_get_ext(apr_array_header_t *dnsnames,
582              const unsigned char **p,
583              const unsigned char *end)
584 {
585   svn_error_t *err;
586   ptrdiff_t len;
587 
588   if (*p == end)
589     return SVN_NO_ERROR;
590 
591   err = asn1_get_tag(p, end, &len,
592                      ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
593   if (err)
594     {
595       /* If there aren't extensions that's ok they aren't required */
596       if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
597         {
598           svn_error_clear(err);
599           return SVN_NO_ERROR;
600         }
601 
602       return svn_error_trace(err);
603     }
604 
605   end = *p + len;
606 
607   SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
608 
609   if (end != *p + len)
610     {
611       err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
612       return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
613     }
614 
615   while (*p < end)
616     {
617       ptrdiff_t ext_len;
618       const unsigned char *ext_start, *sna_end;
619       err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
620       if (err)
621         return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
622                                 NULL);
623       ext_start = *p;
624 
625       err = asn1_get_tag(p, end, &len, ASN1_OID);
626       if (err)
627         return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
628                                 NULL);
629 
630       /* skip all extensions except SubjectAltName */
631       if (!equal(*p, len,
632                  OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1))
633         {
634           *p += ext_len - (*p - ext_start);
635           continue;
636         }
637       *p += len;
638 
639       err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
640       if (err)
641         return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
642                                 NULL);
643 
644       /*   SubjectAltName ::= GeneralNames
645 
646            GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
647 
648            GeneralName ::= CHOICE {
649                 other Name                      [0]     OtherName,
650                 rfc822Name                      [1]     IA5String,
651                 dNSName                         [2]     IA5String,
652                 x400Address                     [3]     ORAddress,
653                 directoryName                   [4]     Name,
654                 ediPartyName                    [5]     EDIPartyName,
655                 uniformResourceIdentifier       [6]     IA5String,
656                 iPAddress                       [7]     OCTET STRING,
657                 registeredID                    [8]     OBJECT IDENTIFIER } */
658       sna_end = *p + len;
659 
660       err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
661       if (err)
662         return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
663                                 NULL);
664 
665       if (sna_end != *p + len)
666         {
667           err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
668           return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
669         }
670 
671       while (*p < sna_end)
672         {
673           err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
674                              ASN1_PRIMITIVE | 2);
675           if (err)
676             {
677               /* not not a dNSName */
678               if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
679                 {
680                   svn_error_clear(err);
681                   /* need to skip the tag and then find the length to
682                    * skip to ignore this SNA entry. */
683                   (*p)++;
684                   SVN_ERR(asn1_get_len(p, sna_end, &len));
685                   *p += len;
686                   continue;
687                 }
688 
689               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. */
716 static const char *
fuzzy_escape(const svn_string_t * src,apr_pool_t * result_pool)717 fuzzy_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. */
760 static const char *
nul_escape(const svn_string_t * src,apr_pool_t * result_pool)761 nul_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. */
802 static svn_error_t *
latin1_to_utf8(const svn_string_t ** result,const svn_string_t * src,apr_pool_t * result_pool)803 latin1_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. */
830 static const char *
x509name_to_utf8_string(const x509_name * name,apr_pool_t * result_pool)831 x509name_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 
908 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)909 x509_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 
936 static svn_boolean_t
is_hostname(const char * str)937 is_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 
970 static const char *
x509parse_get_cn(apr_array_header_t * subject)971 x509parse_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 
987 static void
x509parse_get_hostnames(svn_x509_certinfo_t * ci,x509_cert * crt,apr_pool_t * result_pool,apr_pool_t * scratch_pool)988 x509parse_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  */
1030 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)1031 svn_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