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 /* 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 */ 291static svn_error_t * 292x509_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 */ 355static svn_error_t * 356x509_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. */ 402static svn_error_t * 403x509_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 */ 493static svn_error_t * 494x509_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 522static svn_error_t * 523x509_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 */ 548static svn_error_t * 549x509_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 */ 580static svn_error_t * 581x509_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. */ 716static const char * 717fuzzy_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. */ 760static const char * 761nul_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. */ 802static svn_error_t * 803latin1_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. */ 830static const char * 831x509name_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 908static svn_error_t * 909x509_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 936static svn_boolean_t 937is_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 970static const char * 971x509parse_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 987static void 988x509parse_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 */ 1030svn_error_t * 1031svn_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