x509parse.c revision 362181
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 */ 67static svn_error_t * 68asn1_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 106static svn_error_t * 107asn1_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 121static svn_error_t * 122asn1_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 144static svn_boolean_t 145equal(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 154static svn_boolean_t 155oids_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 */ 164static svn_error_t * 165x509_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 */ 207static svn_error_t * 208x509_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 */ 243static svn_error_t * 244x509_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 */ 312static svn_error_t * 313x509_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 */ 376static svn_error_t * 377x509_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. */ 423static svn_error_t * 424x509_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 */ 526static svn_error_t * 527x509_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 555static svn_error_t * 556x509_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 */ 581static svn_error_t * 582x509_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 */ 613static svn_error_t * 614x509_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. */ 748static const char * 749fuzzy_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. */ 792static const char * 793nul_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. */ 834static svn_error_t * 835latin1_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. */ 862static const char * 863x509name_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 940static svn_error_t * 941x509_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 967static svn_boolean_t 968is_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 1001static const char * 1002x509parse_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 1018static void 1019x509parse_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 */ 1061svn_error_t * 1062svn_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