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