1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ 28 defined(USE_SCHANNEL) || defined(USE_SECTRANSP) 29 30#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL) 31#define WANT_PARSEX509 /* uses Curl_parseX509() */ 32#endif 33 34#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) 35#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ 36#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ 37#endif 38 39#include <curl/curl.h> 40#include "urldata.h" 41#include "strcase.h" 42#include "curl_ctype.h" 43#include "hostcheck.h" 44#include "vtls/vtls.h" 45#include "vtls/vtls_int.h" 46#include "sendf.h" 47#include "inet_pton.h" 48#include "curl_base64.h" 49#include "x509asn1.h" 50#include "dynbuf.h" 51 52/* The last 3 #include files should be in this order */ 53#include "curl_printf.h" 54#include "curl_memory.h" 55#include "memdebug.h" 56 57/* 58 * Constants. 59 */ 60 61/* Largest supported ASN.1 structure. */ 62#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ 63 64/* ASN.1 classes. */ 65#define CURL_ASN1_UNIVERSAL 0 66#define CURL_ASN1_APPLICATION 1 67#define CURL_ASN1_CONTEXT_SPECIFIC 2 68#define CURL_ASN1_PRIVATE 3 69 70/* ASN.1 types. */ 71#define CURL_ASN1_BOOLEAN 1 72#define CURL_ASN1_INTEGER 2 73#define CURL_ASN1_BIT_STRING 3 74#define CURL_ASN1_OCTET_STRING 4 75#define CURL_ASN1_NULL 5 76#define CURL_ASN1_OBJECT_IDENTIFIER 6 77#define CURL_ASN1_OBJECT_DESCRIPTOR 7 78#define CURL_ASN1_INSTANCE_OF 8 79#define CURL_ASN1_REAL 9 80#define CURL_ASN1_ENUMERATED 10 81#define CURL_ASN1_EMBEDDED 11 82#define CURL_ASN1_UTF8_STRING 12 83#define CURL_ASN1_RELATIVE_OID 13 84#define CURL_ASN1_SEQUENCE 16 85#define CURL_ASN1_SET 17 86#define CURL_ASN1_NUMERIC_STRING 18 87#define CURL_ASN1_PRINTABLE_STRING 19 88#define CURL_ASN1_TELETEX_STRING 20 89#define CURL_ASN1_VIDEOTEX_STRING 21 90#define CURL_ASN1_IA5_STRING 22 91#define CURL_ASN1_UTC_TIME 23 92#define CURL_ASN1_GENERALIZED_TIME 24 93#define CURL_ASN1_GRAPHIC_STRING 25 94#define CURL_ASN1_VISIBLE_STRING 26 95#define CURL_ASN1_GENERAL_STRING 27 96#define CURL_ASN1_UNIVERSAL_STRING 28 97#define CURL_ASN1_CHARACTER_STRING 29 98#define CURL_ASN1_BMP_STRING 30 99 100/* Max sixes */ 101 102#define MAX_X509_STR 10000 103#define MAX_X509_CERT 100000 104 105#ifdef WANT_EXTRACT_CERTINFO 106/* ASN.1 OID table entry. */ 107struct Curl_OID { 108 const char *numoid; /* Dotted-numeric OID. */ 109 const char *textoid; /* OID name. */ 110}; 111 112/* ASN.1 OIDs. */ 113static const char cnOID[] = "2.5.4.3"; /* Common name. */ 114static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ 115 116static const struct Curl_OID OIDtable[] = { 117 { "1.2.840.10040.4.1", "dsa" }, 118 { "1.2.840.10040.4.3", "dsa-with-sha1" }, 119 { "1.2.840.10045.2.1", "ecPublicKey" }, 120 { "1.2.840.10045.3.0.1", "c2pnb163v1" }, 121 { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, 122 { "1.2.840.10046.2.1", "dhpublicnumber" }, 123 { "1.2.840.113549.1.1.1", "rsaEncryption" }, 124 { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, 125 { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, 126 { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, 127 { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, 128 { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, 129 { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, 130 { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, 131 { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, 132 { "1.2.840.113549.2.2", "md2" }, 133 { "1.2.840.113549.2.5", "md5" }, 134 { "1.3.14.3.2.26", "sha1" }, 135 { cnOID, "CN" }, 136 { "2.5.4.4", "SN" }, 137 { "2.5.4.5", "serialNumber" }, 138 { "2.5.4.6", "C" }, 139 { "2.5.4.7", "L" }, 140 { "2.5.4.8", "ST" }, 141 { "2.5.4.9", "streetAddress" }, 142 { "2.5.4.10", "O" }, 143 { "2.5.4.11", "OU" }, 144 { "2.5.4.12", "title" }, 145 { "2.5.4.13", "description" }, 146 { "2.5.4.17", "postalCode" }, 147 { "2.5.4.41", "name" }, 148 { "2.5.4.42", "givenName" }, 149 { "2.5.4.43", "initials" }, 150 { "2.5.4.44", "generationQualifier" }, 151 { "2.5.4.45", "X500UniqueIdentifier" }, 152 { "2.5.4.46", "dnQualifier" }, 153 { "2.5.4.65", "pseudonym" }, 154 { "1.2.840.113549.1.9.1", "emailAddress" }, 155 { "2.5.4.72", "role" }, 156 { sanOID, "subjectAltName" }, 157 { "2.5.29.18", "issuerAltName" }, 158 { "2.5.29.19", "basicConstraints" }, 159 { "2.16.840.1.101.3.4.2.4", "sha224" }, 160 { "2.16.840.1.101.3.4.2.1", "sha256" }, 161 { "2.16.840.1.101.3.4.2.2", "sha384" }, 162 { "2.16.840.1.101.3.4.2.3", "sha512" }, 163 { (const char *) NULL, (const char *) NULL } 164}; 165 166#endif /* WANT_EXTRACT_CERTINFO */ 167 168/* 169 * Lightweight ASN.1 parser. 170 * In particular, it does not check for syntactic/lexical errors. 171 * It is intended to support certificate information gathering for SSL backends 172 * that offer a mean to get certificates as a whole, but do not supply 173 * entry points to get particular certificate sub-fields. 174 * Please note there is no pretension here to rewrite a full SSL library. 175 */ 176 177static const char *getASN1Element(struct Curl_asn1Element *elem, 178 const char *beg, const char *end) 179 WARN_UNUSED_RESULT; 180 181static const char *getASN1Element(struct Curl_asn1Element *elem, 182 const char *beg, const char *end) 183{ 184 unsigned char b; 185 size_t len; 186 struct Curl_asn1Element lelem; 187 188 /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' 189 ending at `end'. 190 Returns a pointer in source string after the parsed element, or NULL 191 if an error occurs. */ 192 if(!beg || !end || beg >= end || !*beg || 193 (size_t)(end - beg) > CURL_ASN1_MAX) 194 return NULL; 195 196 /* Process header byte. */ 197 elem->header = beg; 198 b = (unsigned char) *beg++; 199 elem->constructed = (b & 0x20) != 0; 200 elem->class = (b >> 6) & 3; 201 b &= 0x1F; 202 if(b == 0x1F) 203 return NULL; /* Long tag values not supported here. */ 204 elem->tag = b; 205 206 /* Process length. */ 207 if(beg >= end) 208 return NULL; 209 b = (unsigned char) *beg++; 210 if(!(b & 0x80)) 211 len = b; 212 else if(!(b &= 0x7F)) { 213 /* Unspecified length. Since we have all the data, we can determine the 214 effective length by skipping element until an end element is found. */ 215 if(!elem->constructed) 216 return NULL; 217 elem->beg = beg; 218 while(beg < end && *beg) { 219 beg = getASN1Element(&lelem, beg, end); 220 if(!beg) 221 return NULL; 222 } 223 if(beg >= end) 224 return NULL; 225 elem->end = beg; 226 return beg + 1; 227 } 228 else if((unsigned)b > (size_t)(end - beg)) 229 return NULL; /* Does not fit in source. */ 230 else { 231 /* Get long length. */ 232 len = 0; 233 do { 234 if(len & 0xFF000000L) 235 return NULL; /* Lengths > 32 bits are not supported. */ 236 len = (len << 8) | (unsigned char) *beg++; 237 } while(--b); 238 } 239 if(len > (size_t)(end - beg)) 240 return NULL; /* Element data does not fit in source. */ 241 elem->beg = beg; 242 elem->end = beg + len; 243 return elem->end; 244} 245 246#ifdef WANT_EXTRACT_CERTINFO 247 248/* 249 * Search the null terminated OID or OID identifier in local table. 250 * Return the table entry pointer or NULL if not found. 251 */ 252static const struct Curl_OID *searchOID(const char *oid) 253{ 254 const struct Curl_OID *op; 255 for(op = OIDtable; op->numoid; op++) 256 if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) 257 return op; 258 259 return NULL; 260} 261 262/* 263 * Convert an ASN.1 Boolean value into its string representation. 264 * 265 * Return error code. 266 */ 267 268static CURLcode bool2str(struct dynbuf *store, 269 const char *beg, const char *end) 270{ 271 if(end - beg != 1) 272 return CURLE_BAD_FUNCTION_ARGUMENT; 273 return Curl_dyn_add(store, *beg? "TRUE": "FALSE"); 274} 275 276/* 277 * Convert an ASN.1 octet string to a printable string. 278 * 279 * Return error code. 280 */ 281static CURLcode octet2str(struct dynbuf *store, 282 const char *beg, const char *end) 283{ 284 CURLcode result = CURLE_OK; 285 286 while(!result && beg < end) 287 result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++); 288 289 return result; 290} 291 292static CURLcode bit2str(struct dynbuf *store, 293 const char *beg, const char *end) 294{ 295 /* Convert an ASN.1 bit string to a printable string. */ 296 297 if(++beg > end) 298 return CURLE_BAD_FUNCTION_ARGUMENT; 299 return octet2str(store, beg, end); 300} 301 302/* 303 * Convert an ASN.1 integer value into its string representation. 304 * 305 * Returns error. 306 */ 307static CURLcode int2str(struct dynbuf *store, 308 const char *beg, const char *end) 309{ 310 unsigned int val = 0; 311 size_t n = end - beg; 312 313 if(!n) 314 return CURLE_BAD_FUNCTION_ARGUMENT; 315 316 if(n > 4) 317 return octet2str(store, beg, end); 318 319 /* Represent integers <= 32-bit as a single value. */ 320 if(*beg & 0x80) 321 val = ~val; 322 323 do 324 val = (val << 8) | *(const unsigned char *) beg++; 325 while(beg < end); 326 return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val); 327} 328 329/* 330 * Convert from an ASN.1 typed string to UTF8. 331 * 332 * The result is stored in a dynbuf that is inited by the user of this 333 * function. 334 * 335 * Returns error. 336 */ 337static CURLcode 338utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) 339{ 340 size_t inlength = end - from; 341 int size = 1; 342 CURLcode result = CURLE_OK; 343 344 switch(type) { 345 case CURL_ASN1_BMP_STRING: 346 size = 2; 347 break; 348 case CURL_ASN1_UNIVERSAL_STRING: 349 size = 4; 350 break; 351 case CURL_ASN1_NUMERIC_STRING: 352 case CURL_ASN1_PRINTABLE_STRING: 353 case CURL_ASN1_TELETEX_STRING: 354 case CURL_ASN1_IA5_STRING: 355 case CURL_ASN1_VISIBLE_STRING: 356 case CURL_ASN1_UTF8_STRING: 357 break; 358 default: 359 return CURLE_BAD_FUNCTION_ARGUMENT; /* Conversion not supported. */ 360 } 361 362 if(inlength % size) 363 /* Length inconsistent with character size. */ 364 return CURLE_BAD_FUNCTION_ARGUMENT; 365 366 if(type == CURL_ASN1_UTF8_STRING) { 367 /* Just copy. */ 368 if(inlength) 369 result = Curl_dyn_addn(to, from, inlength); 370 } 371 else { 372 while(!result && (from < end)) { 373 char buf[4]; /* decode buffer */ 374 int charsize = 1; 375 unsigned int wc = 0; 376 377 switch(size) { 378 case 4: 379 wc = (wc << 8) | *(const unsigned char *) from++; 380 wc = (wc << 8) | *(const unsigned char *) from++; 381 FALLTHROUGH(); 382 case 2: 383 wc = (wc << 8) | *(const unsigned char *) from++; 384 FALLTHROUGH(); 385 default: /* case 1: */ 386 wc = (wc << 8) | *(const unsigned char *) from++; 387 } 388 if(wc >= 0x00000080) { 389 if(wc >= 0x00000800) { 390 if(wc >= 0x00010000) { 391 if(wc >= 0x00200000) { 392 free(buf); 393 /* Invalid char. size for target encoding. */ 394 return CURLE_WEIRD_SERVER_REPLY; 395 } 396 buf[3] = (char) (0x80 | (wc & 0x3F)); 397 wc = (wc >> 6) | 0x00010000; 398 charsize++; 399 } 400 buf[2] = (char) (0x80 | (wc & 0x3F)); 401 wc = (wc >> 6) | 0x00000800; 402 charsize++; 403 } 404 buf[1] = (char) (0x80 | (wc & 0x3F)); 405 wc = (wc >> 6) | 0x000000C0; 406 charsize++; 407 } 408 buf[0] = (char) wc; 409 result = Curl_dyn_addn(to, buf, charsize); 410 } 411 } 412 return result; 413} 414 415/* 416 * Convert an ASN.1 OID into its dotted string representation. 417 * 418 * Return error code. 419 */ 420static CURLcode encodeOID(struct dynbuf *store, 421 const char *beg, const char *end) 422{ 423 unsigned int x; 424 unsigned int y; 425 CURLcode result = CURLE_OK; 426 427 /* Process the first two numbers. */ 428 y = *(const unsigned char *) beg++; 429 x = y / 40; 430 y -= x * 40; 431 432 result = Curl_dyn_addf(store, "%u.%u", x, y); 433 if(result) 434 return result; 435 436 /* Process the trailing numbers. */ 437 while(beg < end) { 438 x = 0; 439 do { 440 if(x & 0xFF000000) 441 return 0; 442 y = *(const unsigned char *) beg++; 443 x = (x << 7) | (y & 0x7F); 444 } while(y & 0x80); 445 result = Curl_dyn_addf(store, ".%u", x); 446 } 447 return result; 448} 449 450/* 451 * Convert an ASN.1 OID into its dotted or symbolic string representation. 452 * 453 * Return error code. 454 */ 455 456static CURLcode OID2str(struct dynbuf *store, 457 const char *beg, const char *end, bool symbolic) 458{ 459 CURLcode result = CURLE_OK; 460 if(beg < end) { 461 if(symbolic) { 462 struct dynbuf buf; 463 Curl_dyn_init(&buf, MAX_X509_STR); 464 result = encodeOID(&buf, beg, end); 465 466 if(!result) { 467 const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf)); 468 if(op) 469 result = Curl_dyn_add(store, op->textoid); 470 Curl_dyn_free(&buf); 471 } 472 } 473 else 474 result = encodeOID(store, beg, end); 475 } 476 return result; 477} 478 479static CURLcode GTime2str(struct dynbuf *store, 480 const char *beg, const char *end) 481{ 482 const char *tzp; 483 const char *fracp; 484 char sec1, sec2; 485 size_t fracl; 486 size_t tzl; 487 const char *sep = ""; 488 489 /* Convert an ASN.1 Generalized time to a printable string. 490 Return the dynamically allocated string, or NULL if an error occurs. */ 491 492 for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) 493 ; 494 495 /* Get seconds digits. */ 496 sec1 = '0'; 497 switch(fracp - beg - 12) { 498 case 0: 499 sec2 = '0'; 500 break; 501 case 2: 502 sec1 = fracp[-2]; 503 FALLTHROUGH(); 504 case 1: 505 sec2 = fracp[-1]; 506 break; 507 default: 508 return CURLE_BAD_FUNCTION_ARGUMENT; 509 } 510 511 /* Scan for timezone, measure fractional seconds. */ 512 tzp = fracp; 513 fracl = 0; 514 if(fracp < end && (*fracp == '.' || *fracp == ',')) { 515 fracp++; 516 do 517 tzp++; 518 while(tzp < end && *tzp >= '0' && *tzp <= '9'); 519 /* Strip leading zeroes in fractional seconds. */ 520 for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) 521 ; 522 } 523 524 /* Process timezone. */ 525 if(tzp >= end) 526 ; /* Nothing to do. */ 527 else if(*tzp == 'Z') { 528 tzp = " GMT"; 529 end = tzp + 4; 530 } 531 else { 532 sep = " "; 533 tzp++; 534 } 535 536 tzl = end - tzp; 537 return Curl_dyn_addf(store, 538 "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", 539 beg, beg + 4, beg + 6, 540 beg + 8, beg + 10, sec1, sec2, 541 fracl? ".": "", (int)fracl, fracp, 542 sep, (int)tzl, tzp); 543} 544 545/* 546 * Convert an ASN.1 UTC time to a printable string. 547 * 548 * Return error code. 549 */ 550static CURLcode UTime2str(struct dynbuf *store, 551 const char *beg, const char *end) 552{ 553 const char *tzp; 554 size_t tzl; 555 const char *sec; 556 557 for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) 558 ; 559 /* Get the seconds. */ 560 sec = beg + 10; 561 switch(tzp - sec) { 562 case 0: 563 sec = "00"; 564 FALLTHROUGH(); 565 case 2: 566 break; 567 default: 568 return CURLE_BAD_FUNCTION_ARGUMENT; 569 } 570 571 /* Process timezone. */ 572 if(tzp >= end) 573 return CURLE_BAD_FUNCTION_ARGUMENT; 574 if(*tzp == 'Z') { 575 tzp = "GMT"; 576 end = tzp + 3; 577 } 578 else 579 tzp++; 580 581 tzl = end - tzp; 582 return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", 583 20 - (*beg >= '5'), beg, beg + 2, beg + 4, 584 beg + 6, beg + 8, sec, 585 (int)tzl, tzp); 586} 587 588/* 589 * Convert an ASN.1 element to a printable string. 590 * 591 * Return error 592 */ 593static CURLcode ASN1tostr(struct dynbuf *store, 594 struct Curl_asn1Element *elem, int type) 595{ 596 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; 597 if(elem->constructed) 598 return CURLE_OK; /* No conversion of structured elements. */ 599 600 if(!type) 601 type = elem->tag; /* Type not forced: use element tag as type. */ 602 603 switch(type) { 604 case CURL_ASN1_BOOLEAN: 605 result = bool2str(store, elem->beg, elem->end); 606 break; 607 case CURL_ASN1_INTEGER: 608 case CURL_ASN1_ENUMERATED: 609 result = int2str(store, elem->beg, elem->end); 610 break; 611 case CURL_ASN1_BIT_STRING: 612 result = bit2str(store, elem->beg, elem->end); 613 break; 614 case CURL_ASN1_OCTET_STRING: 615 result = octet2str(store, elem->beg, elem->end); 616 break; 617 case CURL_ASN1_NULL: 618 result = Curl_dyn_addn(store, "", 1); 619 break; 620 case CURL_ASN1_OBJECT_IDENTIFIER: 621 result = OID2str(store, elem->beg, elem->end, TRUE); 622 break; 623 case CURL_ASN1_UTC_TIME: 624 result = UTime2str(store, elem->beg, elem->end); 625 break; 626 case CURL_ASN1_GENERALIZED_TIME: 627 result = GTime2str(store, elem->beg, elem->end); 628 break; 629 case CURL_ASN1_UTF8_STRING: 630 case CURL_ASN1_NUMERIC_STRING: 631 case CURL_ASN1_PRINTABLE_STRING: 632 case CURL_ASN1_TELETEX_STRING: 633 case CURL_ASN1_IA5_STRING: 634 case CURL_ASN1_VISIBLE_STRING: 635 case CURL_ASN1_UNIVERSAL_STRING: 636 case CURL_ASN1_BMP_STRING: 637 result = utf8asn1str(store, type, elem->beg, elem->end); 638 break; 639 } 640 641 return result; 642} 643 644/* 645 * ASCII encode distinguished name at `dn' into the store dynbuf. 646 * 647 * Returns error. 648 */ 649static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) 650{ 651 struct Curl_asn1Element rdn; 652 struct Curl_asn1Element atv; 653 struct Curl_asn1Element oid; 654 struct Curl_asn1Element value; 655 const char *p1; 656 const char *p2; 657 const char *p3; 658 const char *str; 659 CURLcode result = CURLE_OK; 660 bool added = FALSE; 661 struct dynbuf temp; 662 Curl_dyn_init(&temp, MAX_X509_STR); 663 664 for(p1 = dn->beg; p1 < dn->end;) { 665 p1 = getASN1Element(&rdn, p1, dn->end); 666 if(!p1) { 667 result = CURLE_BAD_FUNCTION_ARGUMENT; 668 goto error; 669 } 670 for(p2 = rdn.beg; p2 < rdn.end;) { 671 p2 = getASN1Element(&atv, p2, rdn.end); 672 if(!p2) { 673 result = CURLE_BAD_FUNCTION_ARGUMENT; 674 goto error; 675 } 676 p3 = getASN1Element(&oid, atv.beg, atv.end); 677 if(!p3) { 678 result = CURLE_BAD_FUNCTION_ARGUMENT; 679 goto error; 680 } 681 if(!getASN1Element(&value, p3, atv.end)) { 682 result = CURLE_BAD_FUNCTION_ARGUMENT; 683 goto error; 684 } 685 Curl_dyn_reset(&temp); 686 result = ASN1tostr(&temp, &oid, 0); 687 if(result) 688 goto error; 689 690 str = Curl_dyn_ptr(&temp); 691 692 /* Encode delimiter. 693 If attribute has a short uppercase name, delimiter is ", ". */ 694 for(p3 = str; ISUPPER(*p3); p3++) 695 ; 696 if(added) { 697 if(p3 - str > 2) 698 result = Curl_dyn_addn(store, "/", 1); 699 else 700 result = Curl_dyn_addn(store, ", ", 2); 701 if(result) 702 goto error; 703 } 704 705 /* Encode attribute name. */ 706 result = Curl_dyn_add(store, str); 707 if(result) 708 goto error; 709 710 /* Generate equal sign. */ 711 result = Curl_dyn_addn(store, "=", 1); 712 if(result) 713 goto error; 714 715 /* Generate value. */ 716 result = ASN1tostr(store, &value, 0); 717 if(result) 718 goto error; 719 Curl_dyn_reset(&temp); 720 added = TRUE; /* use separator for next */ 721 } 722 } 723error: 724 Curl_dyn_free(&temp); 725 726 return result; 727} 728 729#endif /* WANT_EXTRACT_CERTINFO */ 730 731#ifdef WANT_PARSEX509 732/* 733 * ASN.1 parse an X509 certificate into structure subfields. 734 * Syntax is assumed to have already been checked by the SSL backend. 735 * See RFC 5280. 736 */ 737int Curl_parseX509(struct Curl_X509certificate *cert, 738 const char *beg, const char *end) 739{ 740 struct Curl_asn1Element elem; 741 struct Curl_asn1Element tbsCertificate; 742 const char *ccp; 743 static const char defaultVersion = 0; /* v1. */ 744 745 cert->certificate.header = NULL; 746 cert->certificate.beg = beg; 747 cert->certificate.end = end; 748 749 /* Get the sequence content. */ 750 if(!getASN1Element(&elem, beg, end)) 751 return -1; /* Invalid bounds/size. */ 752 beg = elem.beg; 753 end = elem.end; 754 755 /* Get tbsCertificate. */ 756 beg = getASN1Element(&tbsCertificate, beg, end); 757 if(!beg) 758 return -1; 759 /* Skip the signatureAlgorithm. */ 760 beg = getASN1Element(&cert->signatureAlgorithm, beg, end); 761 if(!beg) 762 return -1; 763 /* Get the signatureValue. */ 764 if(!getASN1Element(&cert->signature, beg, end)) 765 return -1; 766 767 /* Parse TBSCertificate. */ 768 beg = tbsCertificate.beg; 769 end = tbsCertificate.end; 770 /* Get optional version, get serialNumber. */ 771 cert->version.header = NULL; 772 cert->version.beg = &defaultVersion; 773 cert->version.end = &defaultVersion + sizeof(defaultVersion); 774 beg = getASN1Element(&elem, beg, end); 775 if(!beg) 776 return -1; 777 if(elem.tag == 0) { 778 if(!getASN1Element(&cert->version, elem.beg, elem.end)) 779 return -1; 780 beg = getASN1Element(&elem, beg, end); 781 if(!beg) 782 return -1; 783 } 784 cert->serialNumber = elem; 785 /* Get signature algorithm. */ 786 beg = getASN1Element(&cert->signatureAlgorithm, beg, end); 787 /* Get issuer. */ 788 beg = getASN1Element(&cert->issuer, beg, end); 789 if(!beg) 790 return -1; 791 /* Get notBefore and notAfter. */ 792 beg = getASN1Element(&elem, beg, end); 793 if(!beg) 794 return -1; 795 ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); 796 if(!ccp) 797 return -1; 798 if(!getASN1Element(&cert->notAfter, ccp, elem.end)) 799 return -1; 800 /* Get subject. */ 801 beg = getASN1Element(&cert->subject, beg, end); 802 if(!beg) 803 return -1; 804 /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ 805 beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); 806 if(!beg) 807 return -1; 808 ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, 809 cert->subjectPublicKeyInfo.beg, 810 cert->subjectPublicKeyInfo.end); 811 if(!ccp) 812 return -1; 813 if(!getASN1Element(&cert->subjectPublicKey, ccp, 814 cert->subjectPublicKeyInfo.end)) 815 return -1; 816 /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ 817 cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; 818 cert->extensions.tag = elem.tag = 0; 819 cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; 820 cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; 821 cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; 822 cert->extensions.header = NULL; 823 cert->extensions.beg = cert->extensions.end = ""; 824 if(beg < end) { 825 beg = getASN1Element(&elem, beg, end); 826 if(!beg) 827 return -1; 828 } 829 if(elem.tag == 1) { 830 cert->issuerUniqueID = elem; 831 if(beg < end) { 832 beg = getASN1Element(&elem, beg, end); 833 if(!beg) 834 return -1; 835 } 836 } 837 if(elem.tag == 2) { 838 cert->subjectUniqueID = elem; 839 if(beg < end) { 840 beg = getASN1Element(&elem, beg, end); 841 if(!beg) 842 return -1; 843 } 844 } 845 if(elem.tag == 3) 846 if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) 847 return -1; 848 return 0; 849} 850 851#endif /* WANT_PARSEX509 */ 852 853#ifdef WANT_EXTRACT_CERTINFO 854 855static CURLcode dumpAlgo(struct dynbuf *store, 856 struct Curl_asn1Element *param, 857 const char *beg, const char *end) 858{ 859 struct Curl_asn1Element oid; 860 861 /* Get algorithm parameters and return algorithm name. */ 862 863 beg = getASN1Element(&oid, beg, end); 864 if(!beg) 865 return CURLE_BAD_FUNCTION_ARGUMENT; 866 param->header = NULL; 867 param->tag = 0; 868 param->beg = param->end = end; 869 if(beg < end) { 870 const char *p = getASN1Element(param, beg, end); 871 if(!p) 872 return CURLE_BAD_FUNCTION_ARGUMENT; 873 } 874 return OID2str(store, oid.beg, oid.end, TRUE); 875} 876 877/* 878 * This is a convenience function for push_certinfo_len that takes a zero 879 * terminated value. 880 */ 881static CURLcode ssl_push_certinfo(struct Curl_easy *data, 882 int certnum, 883 const char *label, 884 const char *value) 885{ 886 size_t valuelen = strlen(value); 887 888 return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); 889} 890 891/* 892 * This is a convenience function for push_certinfo_len that takes a 893 * dynbuf value. 894 * 895 * It also does the verbose output if !certnum. 896 */ 897static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data, 898 int certnum, 899 const char *label, 900 struct dynbuf *ptr) 901{ 902 size_t valuelen = Curl_dyn_len(ptr); 903 char *value = Curl_dyn_ptr(ptr); 904 905 CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label, 906 value, valuelen); 907 908 if(!certnum && !result) 909 infof(data, " %s: %s", label, value); 910 911 return result; 912} 913 914static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, 915 const char *label, 916 struct Curl_asn1Element *elem) 917{ 918 CURLcode result; 919 struct dynbuf out; 920 921 Curl_dyn_init(&out, MAX_X509_STR); 922 923 /* Generate a certificate information record for the public key. */ 924 925 result = ASN1tostr(&out, elem, 0); 926 if(!result) { 927 if(data->set.ssl.certinfo) 928 result = ssl_push_certinfo_dyn(data, certnum, label, &out); 929 Curl_dyn_free(&out); 930 } 931 return result; 932} 933 934/* return 0 on success, 1 on error */ 935static int do_pubkey(struct Curl_easy *data, int certnum, 936 const char *algo, struct Curl_asn1Element *param, 937 struct Curl_asn1Element *pubkey) 938{ 939 struct Curl_asn1Element elem; 940 struct Curl_asn1Element pk; 941 const char *p; 942 943 /* Generate all information records for the public key. */ 944 945 if(strcasecompare(algo, "ecPublicKey")) { 946 /* 947 * ECC public key is all the data, a value of type BIT STRING mapped to 948 * OCTET STRING and should not be parsed as an ASN.1 value. 949 */ 950 const size_t len = ((pubkey->end - pubkey->beg - 2) * 4); 951 if(!certnum) 952 infof(data, " ECC Public Key (%zu bits)", len); 953 if(data->set.ssl.certinfo) { 954 char q[sizeof(len) * 8 / 3 + 1]; 955 (void)msnprintf(q, sizeof(q), "%zu", len); 956 if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) 957 return 1; 958 } 959 return do_pubkey_field(data, certnum, "ecPublicKey", pubkey); 960 } 961 962 /* Get the public key (single element). */ 963 if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) 964 return 1; 965 966 if(strcasecompare(algo, "rsaEncryption")) { 967 const char *q; 968 size_t len; 969 970 p = getASN1Element(&elem, pk.beg, pk.end); 971 if(!p) 972 return 1; 973 974 /* Compute key length. */ 975 for(q = elem.beg; !*q && q < elem.end; q++) 976 ; 977 len = ((elem.end - q) * 8); 978 if(len) { 979 unsigned int i; 980 for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) 981 len--; 982 } 983 if(len > 32) 984 elem.beg = q; /* Strip leading zero bytes. */ 985 if(!certnum) 986 infof(data, " RSA Public Key (%zu bits)", len); 987 if(data->set.ssl.certinfo) { 988 char r[sizeof(len) * 8 / 3 + 1]; 989 msnprintf(r, sizeof(r), "%zu", len); 990 if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) 991 return 1; 992 } 993 /* Generate coefficients. */ 994 if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) 995 return 1; 996 if(!getASN1Element(&elem, p, pk.end)) 997 return 1; 998 if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) 999 return 1; 1000 } 1001 else if(strcasecompare(algo, "dsa")) { 1002 p = getASN1Element(&elem, param->beg, param->end); 1003 if(p) { 1004 if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) 1005 return 1; 1006 p = getASN1Element(&elem, p, param->end); 1007 if(p) { 1008 if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) 1009 return 1; 1010 if(getASN1Element(&elem, p, param->end)) { 1011 if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) 1012 return 1; 1013 if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) 1014 return 1; 1015 } 1016 } 1017 } 1018 } 1019 else if(strcasecompare(algo, "dhpublicnumber")) { 1020 p = getASN1Element(&elem, param->beg, param->end); 1021 if(p) { 1022 if(do_pubkey_field(data, certnum, "dh(p)", &elem)) 1023 return 1; 1024 if(getASN1Element(&elem, param->beg, param->end)) { 1025 if(do_pubkey_field(data, certnum, "dh(g)", &elem)) 1026 return 1; 1027 if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) 1028 return 1; 1029 } 1030 } 1031 } 1032 return 0; 1033} 1034 1035/* 1036 * Convert an ASN.1 distinguished name into a printable string. 1037 * Return error. 1038 */ 1039static CURLcode DNtostr(struct dynbuf *store, 1040 struct Curl_asn1Element *dn) 1041{ 1042 return encodeDN(store, dn); 1043} 1044 1045CURLcode Curl_extract_certinfo(struct Curl_easy *data, 1046 int certnum, 1047 const char *beg, 1048 const char *end) 1049{ 1050 struct Curl_X509certificate cert; 1051 struct Curl_asn1Element param; 1052 char *certptr; 1053 size_t clen; 1054 struct dynbuf out; 1055 CURLcode result = CURLE_OK; 1056 unsigned int version; 1057 const char *ptr; 1058 int rc; 1059 1060 if(!data->set.ssl.certinfo) 1061 if(certnum) 1062 return CURLE_OK; 1063 1064 Curl_dyn_init(&out, MAX_X509_STR); 1065 /* Prepare the certificate information for curl_easy_getinfo(). */ 1066 1067 /* Extract the certificate ASN.1 elements. */ 1068 if(Curl_parseX509(&cert, beg, end)) 1069 return CURLE_PEER_FAILED_VERIFICATION; 1070 1071 /* Subject. */ 1072 result = DNtostr(&out, &cert.subject); 1073 if(result) 1074 goto done; 1075 if(data->set.ssl.certinfo) { 1076 result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out); 1077 if(result) 1078 goto done; 1079 } 1080 Curl_dyn_reset(&out); 1081 1082 /* Issuer. */ 1083 result = DNtostr(&out, &cert.issuer); 1084 if(result) 1085 goto done; 1086 if(data->set.ssl.certinfo) { 1087 result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out); 1088 if(result) 1089 goto done; 1090 } 1091 Curl_dyn_reset(&out); 1092 1093 /* Version (always fits in less than 32 bits). */ 1094 version = 0; 1095 for(ptr = cert.version.beg; ptr < cert.version.end; ptr++) 1096 version = (version << 8) | *(const unsigned char *) ptr; 1097 if(data->set.ssl.certinfo) { 1098 result = Curl_dyn_addf(&out, "%x", version); 1099 if(result) 1100 goto done; 1101 result = ssl_push_certinfo_dyn(data, certnum, "Version", &out); 1102 if(result) 1103 goto done; 1104 Curl_dyn_reset(&out); 1105 } 1106 1107 /* Serial number. */ 1108 result = ASN1tostr(&out, &cert.serialNumber, 0); 1109 if(result) 1110 goto done; 1111 if(data->set.ssl.certinfo) { 1112 result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out); 1113 if(result) 1114 goto done; 1115 } 1116 Curl_dyn_reset(&out); 1117 1118 /* Signature algorithm .*/ 1119 result = dumpAlgo(&out, ¶m, cert.signatureAlgorithm.beg, 1120 cert.signatureAlgorithm.end); 1121 if(result) 1122 goto done; 1123 if(data->set.ssl.certinfo) { 1124 result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm", 1125 &out); 1126 if(result) 1127 goto done; 1128 } 1129 Curl_dyn_reset(&out); 1130 1131 /* Start Date. */ 1132 result = ASN1tostr(&out, &cert.notBefore, 0); 1133 if(result) 1134 goto done; 1135 if(data->set.ssl.certinfo) { 1136 result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out); 1137 if(result) 1138 goto done; 1139 } 1140 Curl_dyn_reset(&out); 1141 1142 /* Expire Date. */ 1143 result = ASN1tostr(&out, &cert.notAfter, 0); 1144 if(result) 1145 goto done; 1146 if(data->set.ssl.certinfo) { 1147 result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out); 1148 if(result) 1149 goto done; 1150 } 1151 Curl_dyn_reset(&out); 1152 1153 /* Public Key Algorithm. */ 1154 result = dumpAlgo(&out, ¶m, cert.subjectPublicKeyAlgorithm.beg, 1155 cert.subjectPublicKeyAlgorithm.end); 1156 if(result) 1157 goto done; 1158 if(data->set.ssl.certinfo) { 1159 result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm", 1160 &out); 1161 if(result) 1162 goto done; 1163 } 1164 1165 rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out), 1166 ¶m, &cert.subjectPublicKey); 1167 if(rc) { 1168 result = CURLE_OUT_OF_MEMORY; /* the most likely error */ 1169 goto done; 1170 } 1171 Curl_dyn_reset(&out); 1172 1173 /* Signature. */ 1174 result = ASN1tostr(&out, &cert.signature, 0); 1175 if(result) 1176 goto done; 1177 if(data->set.ssl.certinfo) { 1178 result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out); 1179 if(result) 1180 goto done; 1181 } 1182 Curl_dyn_reset(&out); 1183 1184 /* Generate PEM certificate. */ 1185 result = Curl_base64_encode(cert.certificate.beg, 1186 cert.certificate.end - cert.certificate.beg, 1187 &certptr, &clen); 1188 if(result) 1189 goto done; 1190 1191 /* Generate the final output certificate string. Format is: 1192 -----BEGIN CERTIFICATE-----\n 1193 <max 64 base64 characters>\n 1194 . 1195 . 1196 . 1197 -----END CERTIFICATE-----\n 1198 */ 1199 1200 Curl_dyn_reset(&out); 1201 1202 /* Build the certificate string. */ 1203 result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n"); 1204 if(!result) { 1205 size_t j = 0; 1206 1207 while(!result && (j < clen)) { 1208 size_t chunksize = (clen - j) > 64 ? 64 : (clen - j); 1209 result = Curl_dyn_addn(&out, &certptr[j], chunksize); 1210 if(!result) 1211 result = Curl_dyn_addn(&out, "\n", 1); 1212 j += chunksize; 1213 } 1214 if(!result) 1215 result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n"); 1216 } 1217 free(certptr); 1218 if(!result) 1219 if(data->set.ssl.certinfo) 1220 result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out); 1221 1222done: 1223 Curl_dyn_free(&out); 1224 return result; 1225} 1226 1227#endif /* WANT_EXTRACT_CERTINFO */ 1228 1229#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ 1230