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