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