1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 22 23 /* This file is for implementing all "generic" SSL functions that all libcurl 24 internals should use. It is then responsible for calling the proper 25 "backend" function. 26 27 SSL-functions in libcurl should call functions in this source file, and not 28 to any specific SSL-layer. 29 30 Curl_ssl_ - prefix for generic ones 31 Curl_ossl_ - prefix for OpenSSL ones 32 Curl_gtls_ - prefix for GnuTLS ones 33 Curl_nss_ - prefix for NSS ones 34 Curl_gskit_ - prefix for GSKit ones 35 Curl_polarssl_ - prefix for PolarSSL ones 36 Curl_cyassl_ - prefix for CyaSSL ones 37 Curl_schannel_ - prefix for Schannel SSPI ones 38 Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones 39 40 Note that this source code uses curlssl_* functions, and they are all 41 defines/macros #defined by the lib-specific header files. 42 43 "SSL/TLS Strong Encryption: An Introduction" 44 https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html 45 */ 46 47 #include "curl_setup.h" 48 49 #ifdef HAVE_SYS_TYPES_H 50 #include <sys/types.h> 51 #endif 52 #ifdef HAVE_SYS_STAT_H 53 #include <sys/stat.h> 54 #endif 55 #ifdef HAVE_FCNTL_H 56 #include <fcntl.h> 57 #endif 58 59 #include "urldata.h" 60 61 #include "vtls.h" /* generic SSL protos etc */ 62 #include "slist.h" 63 #include "sendf.h" 64 #include "strcase.h" 65 #include "url.h" 66 #include "progress.h" 67 #include "share.h" 68 #include "multiif.h" 69 #include "timeval.h" 70 #include "curl_md5.h" 71 #include "warnless.h" 72 #include "curl_base64.h" 73 #include "curl_printf.h" 74 75 /* The last #include files should be: */ 76 #include "curl_memory.h" 77 #include "memdebug.h" 78 79 /* convenience macro to check if this handle is using a shared SSL session */ 80 #define SSLSESSION_SHARED(data) (data->share && \ 81 (data->share->specifier & \ 82 (1<<CURL_LOCK_DATA_SSL_SESSION))) 83 84 #define CLONE_STRING(var) \ 85 if(source->var) { \ 86 dest->var = strdup(source->var); \ 87 if(!dest->var) \ 88 return FALSE; \ 89 } \ 90 else \ 91 dest->var = NULL; 92 93 bool 94 Curl_ssl_config_matches(struct ssl_primary_config* data, 95 struct ssl_primary_config* needle) 96 { 97 if((data->version == needle->version) && 98 (data->verifypeer == needle->verifypeer) && 99 (data->verifyhost == needle->verifyhost) && 100 Curl_safe_strcasecompare(data->CApath, needle->CApath) && 101 Curl_safe_strcasecompare(data->CAfile, needle->CAfile) && 102 Curl_safe_strcasecompare(data->clientcert, needle->clientcert) && 103 Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list)) 104 return TRUE; 105 106 return FALSE; 107 } 108 109 bool 110 Curl_clone_primary_ssl_config(struct ssl_primary_config *source, 111 struct ssl_primary_config *dest) 112 { 113 dest->verifyhost = source->verifyhost; 114 dest->verifypeer = source->verifypeer; 115 dest->version = source->version; 116 117 CLONE_STRING(CAfile); 118 CLONE_STRING(CApath); 119 CLONE_STRING(cipher_list); 120 CLONE_STRING(egdsocket); 121 CLONE_STRING(random_file); 122 CLONE_STRING(clientcert); 123 return TRUE; 124 } 125 126 void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc) 127 { 128 Curl_safefree(sslc->CAfile); 129 Curl_safefree(sslc->CApath); 130 Curl_safefree(sslc->cipher_list); 131 Curl_safefree(sslc->egdsocket); 132 Curl_safefree(sslc->random_file); 133 Curl_safefree(sslc->clientcert); 134 } 135 136 int Curl_ssl_backend(void) 137 { 138 return (int)CURL_SSL_BACKEND; 139 } 140 141 #ifdef USE_SSL 142 143 /* "global" init done? */ 144 static bool init_ssl=FALSE; 145 146 /** 147 * Global SSL init 148 * 149 * @retval 0 error initializing SSL 150 * @retval 1 SSL initialized successfully 151 */ 152 int Curl_ssl_init(void) 153 { 154 /* make sure this is only done once */ 155 if(init_ssl) 156 return 1; 157 init_ssl = TRUE; /* never again */ 158 159 return curlssl_init(); 160 } 161 162 163 /* Global cleanup */ 164 void Curl_ssl_cleanup(void) 165 { 166 if(init_ssl) { 167 /* only cleanup if we did a previous init */ 168 curlssl_cleanup(); 169 init_ssl = FALSE; 170 } 171 } 172 173 static bool ssl_prefs_check(struct Curl_easy *data) 174 { 175 /* check for CURLOPT_SSLVERSION invalid parameter value */ 176 if((data->set.ssl.primary.version < 0) 177 || (data->set.ssl.primary.version >= CURL_SSLVERSION_LAST)) { 178 failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); 179 return FALSE; 180 } 181 return TRUE; 182 } 183 184 static CURLcode 185 ssl_connect_init_proxy(struct connectdata *conn, int sockindex) 186 { 187 DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]); 188 if(ssl_connection_complete == conn->ssl[sockindex].state && 189 !conn->proxy_ssl[sockindex].use) { 190 #if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_NSS) || \ 191 defined(USE_GSKIT) 192 conn->proxy_ssl[sockindex] = conn->ssl[sockindex]; 193 memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex])); 194 #else 195 return CURLE_NOT_BUILT_IN; 196 #endif 197 } 198 return CURLE_OK; 199 } 200 201 CURLcode 202 Curl_ssl_connect(struct connectdata *conn, int sockindex) 203 { 204 CURLcode result; 205 206 if(conn->bits.proxy_ssl_connected[sockindex]) { 207 result = ssl_connect_init_proxy(conn, sockindex); 208 if(result) 209 return result; 210 } 211 212 if(!ssl_prefs_check(conn->data)) 213 return CURLE_SSL_CONNECT_ERROR; 214 215 /* mark this is being ssl-enabled from here on. */ 216 conn->ssl[sockindex].use = TRUE; 217 conn->ssl[sockindex].state = ssl_connection_negotiating; 218 219 result = curlssl_connect(conn, sockindex); 220 221 if(!result) 222 Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ 223 224 return result; 225 } 226 227 CURLcode 228 Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex, 229 bool *done) 230 { 231 CURLcode result; 232 if(conn->bits.proxy_ssl_connected[sockindex]) { 233 result = ssl_connect_init_proxy(conn, sockindex); 234 if(result) 235 return result; 236 } 237 238 if(!ssl_prefs_check(conn->data)) 239 return CURLE_SSL_CONNECT_ERROR; 240 241 /* mark this is being ssl requested from here on. */ 242 conn->ssl[sockindex].use = TRUE; 243 #ifdef curlssl_connect_nonblocking 244 result = curlssl_connect_nonblocking(conn, sockindex, done); 245 #else 246 *done = TRUE; /* fallback to BLOCKING */ 247 result = curlssl_connect(conn, sockindex); 248 #endif /* non-blocking connect support */ 249 if(!result && *done) 250 Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ 251 return result; 252 } 253 254 /* 255 * Lock shared SSL session data 256 */ 257 void Curl_ssl_sessionid_lock(struct connectdata *conn) 258 { 259 if(SSLSESSION_SHARED(conn->data)) 260 Curl_share_lock(conn->data, 261 CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); 262 } 263 264 /* 265 * Unlock shared SSL session data 266 */ 267 void Curl_ssl_sessionid_unlock(struct connectdata *conn) 268 { 269 if(SSLSESSION_SHARED(conn->data)) 270 Curl_share_unlock(conn->data, CURL_LOCK_DATA_SSL_SESSION); 271 } 272 273 /* 274 * Check if there's a session ID for the given connection in the cache, and if 275 * there's one suitable, it is provided. Returns TRUE when no entry matched. 276 */ 277 bool Curl_ssl_getsessionid(struct connectdata *conn, 278 void **ssl_sessionid, 279 size_t *idsize, /* set 0 if unknown */ 280 int sockindex) 281 { 282 struct curl_ssl_session *check; 283 struct Curl_easy *data = conn->data; 284 size_t i; 285 long *general_age; 286 bool no_match = TRUE; 287 288 const bool isProxy = CONNECT_PROXY_SSL(); 289 struct ssl_primary_config * const ssl_config = isProxy ? 290 &conn->proxy_ssl_config : 291 &conn->ssl_config; 292 const char * const name = isProxy ? conn->http_proxy.host.name : 293 conn->host.name; 294 int port = isProxy ? (int)conn->port : conn->remote_port; 295 *ssl_sessionid = NULL; 296 297 DEBUGASSERT(data->set.general_ssl.sessionid); 298 299 if(!data->set.general_ssl.sessionid) 300 /* session ID re-use is disabled */ 301 return TRUE; 302 303 /* Lock if shared */ 304 if(SSLSESSION_SHARED(data)) 305 general_age = &data->share->sessionage; 306 else 307 general_age = &data->state.sessionage; 308 309 for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { 310 check = &data->state.session[i]; 311 if(!check->sessionid) 312 /* not session ID means blank entry */ 313 continue; 314 if(strcasecompare(name, check->name) && 315 ((!conn->bits.conn_to_host && !check->conn_to_host) || 316 (conn->bits.conn_to_host && check->conn_to_host && 317 strcasecompare(conn->conn_to_host.name, check->conn_to_host))) && 318 ((!conn->bits.conn_to_port && check->conn_to_port == -1) || 319 (conn->bits.conn_to_port && check->conn_to_port != -1 && 320 conn->conn_to_port == check->conn_to_port)) && 321 (port == check->remote_port) && 322 strcasecompare(conn->handler->scheme, check->scheme) && 323 Curl_ssl_config_matches(ssl_config, &check->ssl_config)) { 324 /* yes, we have a session ID! */ 325 (*general_age)++; /* increase general age */ 326 check->age = *general_age; /* set this as used in this age */ 327 *ssl_sessionid = check->sessionid; 328 if(idsize) 329 *idsize = check->idsize; 330 no_match = FALSE; 331 break; 332 } 333 } 334 335 return no_match; 336 } 337 338 /* 339 * Kill a single session ID entry in the cache. 340 */ 341 void Curl_ssl_kill_session(struct curl_ssl_session *session) 342 { 343 if(session->sessionid) { 344 /* defensive check */ 345 346 /* free the ID the SSL-layer specific way */ 347 curlssl_session_free(session->sessionid); 348 349 session->sessionid = NULL; 350 session->age = 0; /* fresh */ 351 352 Curl_free_primary_ssl_config(&session->ssl_config); 353 354 Curl_safefree(session->name); 355 Curl_safefree(session->conn_to_host); 356 } 357 } 358 359 /* 360 * Delete the given session ID from the cache. 361 */ 362 void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid) 363 { 364 size_t i; 365 struct Curl_easy *data=conn->data; 366 367 for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { 368 struct curl_ssl_session *check = &data->state.session[i]; 369 370 if(check->sessionid == ssl_sessionid) { 371 Curl_ssl_kill_session(check); 372 break; 373 } 374 } 375 } 376 377 /* 378 * Store session id in the session cache. The ID passed on to this function 379 * must already have been extracted and allocated the proper way for the SSL 380 * layer. Curl_XXXX_session_free() will be called to free/kill the session ID 381 * later on. 382 */ 383 CURLcode Curl_ssl_addsessionid(struct connectdata *conn, 384 void *ssl_sessionid, 385 size_t idsize, 386 int sockindex) 387 { 388 size_t i; 389 struct Curl_easy *data=conn->data; /* the mother of all structs */ 390 struct curl_ssl_session *store = &data->state.session[0]; 391 long oldest_age=data->state.session[0].age; /* zero if unused */ 392 char *clone_host; 393 char *clone_conn_to_host; 394 int conn_to_port; 395 long *general_age; 396 const bool isProxy = CONNECT_PROXY_SSL(); 397 struct ssl_primary_config * const ssl_config = isProxy ? 398 &conn->proxy_ssl_config : 399 &conn->ssl_config; 400 401 DEBUGASSERT(data->set.general_ssl.sessionid); 402 403 clone_host = strdup(isProxy ? conn->http_proxy.host.name : conn->host.name); 404 if(!clone_host) 405 return CURLE_OUT_OF_MEMORY; /* bail out */ 406 407 if(conn->bits.conn_to_host) { 408 clone_conn_to_host = strdup(conn->conn_to_host.name); 409 if(!clone_conn_to_host) { 410 free(clone_host); 411 return CURLE_OUT_OF_MEMORY; /* bail out */ 412 } 413 } 414 else 415 clone_conn_to_host = NULL; 416 417 if(conn->bits.conn_to_port) 418 conn_to_port = conn->conn_to_port; 419 else 420 conn_to_port = -1; 421 422 /* Now we should add the session ID and the host name to the cache, (remove 423 the oldest if necessary) */ 424 425 /* If using shared SSL session, lock! */ 426 if(SSLSESSION_SHARED(data)) { 427 general_age = &data->share->sessionage; 428 } 429 else { 430 general_age = &data->state.sessionage; 431 } 432 433 /* find an empty slot for us, or find the oldest */ 434 for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) && 435 data->state.session[i].sessionid; i++) { 436 if(data->state.session[i].age < oldest_age) { 437 oldest_age = data->state.session[i].age; 438 store = &data->state.session[i]; 439 } 440 } 441 if(i == data->set.general_ssl.max_ssl_sessions) 442 /* cache is full, we must "kill" the oldest entry! */ 443 Curl_ssl_kill_session(store); 444 else 445 store = &data->state.session[i]; /* use this slot */ 446 447 /* now init the session struct wisely */ 448 store->sessionid = ssl_sessionid; 449 store->idsize = idsize; 450 store->age = *general_age; /* set current age */ 451 /* free it if there's one already present */ 452 free(store->name); 453 free(store->conn_to_host); 454 store->name = clone_host; /* clone host name */ 455 store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ 456 store->conn_to_port = conn_to_port; /* connect to port number */ 457 /* port number */ 458 store->remote_port = isProxy ? (int)conn->port : conn->remote_port; 459 store->scheme = conn->handler->scheme; 460 461 if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) { 462 store->sessionid = NULL; /* let caller free sessionid */ 463 free(clone_host); 464 free(clone_conn_to_host); 465 return CURLE_OUT_OF_MEMORY; 466 } 467 468 return CURLE_OK; 469 } 470 471 472 void Curl_ssl_close_all(struct Curl_easy *data) 473 { 474 size_t i; 475 /* kill the session ID cache if not shared */ 476 if(data->state.session && !SSLSESSION_SHARED(data)) { 477 for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) 478 /* the single-killer function handles empty table slots */ 479 Curl_ssl_kill_session(&data->state.session[i]); 480 481 /* free the cache data */ 482 Curl_safefree(data->state.session); 483 } 484 485 curlssl_close_all(data); 486 } 487 488 #if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \ 489 defined(USE_DARWINSSL) || defined(USE_NSS) 490 /* This function is for OpenSSL, GnuTLS, darwinssl, and schannel only. */ 491 int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks, 492 int numsocks) 493 { 494 struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; 495 496 if(!numsocks) 497 return GETSOCK_BLANK; 498 499 if(connssl->connecting_state == ssl_connect_2_writing) { 500 /* write mode */ 501 socks[0] = conn->sock[FIRSTSOCKET]; 502 return GETSOCK_WRITESOCK(0); 503 } 504 else if(connssl->connecting_state == ssl_connect_2_reading) { 505 /* read mode */ 506 socks[0] = conn->sock[FIRSTSOCKET]; 507 return GETSOCK_READSOCK(0); 508 } 509 510 return GETSOCK_BLANK; 511 } 512 #else 513 int Curl_ssl_getsock(struct connectdata *conn, 514 curl_socket_t *socks, 515 int numsocks) 516 { 517 (void)conn; 518 (void)socks; 519 (void)numsocks; 520 return GETSOCK_BLANK; 521 } 522 /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL || USE_DARWINSSL || USE_NSS */ 523 #endif 524 525 void Curl_ssl_close(struct connectdata *conn, int sockindex) 526 { 527 DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); 528 curlssl_close(conn, sockindex); 529 } 530 531 CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex) 532 { 533 if(curlssl_shutdown(conn, sockindex)) 534 return CURLE_SSL_SHUTDOWN_FAILED; 535 536 conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */ 537 conn->ssl[sockindex].state = ssl_connection_none; 538 539 conn->recv[sockindex] = Curl_recv_plain; 540 conn->send[sockindex] = Curl_send_plain; 541 542 return CURLE_OK; 543 } 544 545 /* Selects an SSL crypto engine 546 */ 547 CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine) 548 { 549 return curlssl_set_engine(data, engine); 550 } 551 552 /* Selects the default SSL crypto engine 553 */ 554 CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data) 555 { 556 return curlssl_set_engine_default(data); 557 } 558 559 /* Return list of OpenSSL crypto engine names. */ 560 struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data) 561 { 562 return curlssl_engines_list(data); 563 } 564 565 /* 566 * This sets up a session ID cache to the specified size. Make sure this code 567 * is agnostic to what underlying SSL technology we use. 568 */ 569 CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount) 570 { 571 struct curl_ssl_session *session; 572 573 if(data->state.session) 574 /* this is just a precaution to prevent multiple inits */ 575 return CURLE_OK; 576 577 session = calloc(amount, sizeof(struct curl_ssl_session)); 578 if(!session) 579 return CURLE_OUT_OF_MEMORY; 580 581 /* store the info in the SSL section */ 582 data->set.general_ssl.max_ssl_sessions = amount; 583 data->state.session = session; 584 data->state.sessionage = 1; /* this is brand new */ 585 return CURLE_OK; 586 } 587 588 size_t Curl_ssl_version(char *buffer, size_t size) 589 { 590 return curlssl_version(buffer, size); 591 } 592 593 /* 594 * This function tries to determine connection status. 595 * 596 * Return codes: 597 * 1 means the connection is still in place 598 * 0 means the connection has been closed 599 * -1 means the connection status is unknown 600 */ 601 int Curl_ssl_check_cxn(struct connectdata *conn) 602 { 603 return curlssl_check_cxn(conn); 604 } 605 606 bool Curl_ssl_data_pending(const struct connectdata *conn, 607 int connindex) 608 { 609 return curlssl_data_pending(conn, connindex); 610 } 611 612 void Curl_ssl_free_certinfo(struct Curl_easy *data) 613 { 614 int i; 615 struct curl_certinfo *ci = &data->info.certs; 616 617 if(ci->num_of_certs) { 618 /* free all individual lists used */ 619 for(i=0; i<ci->num_of_certs; i++) { 620 curl_slist_free_all(ci->certinfo[i]); 621 ci->certinfo[i] = NULL; 622 } 623 624 free(ci->certinfo); /* free the actual array too */ 625 ci->certinfo = NULL; 626 ci->num_of_certs = 0; 627 } 628 } 629 630 CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num) 631 { 632 struct curl_certinfo *ci = &data->info.certs; 633 struct curl_slist **table; 634 635 /* Free any previous certificate information structures */ 636 Curl_ssl_free_certinfo(data); 637 638 /* Allocate the required certificate information structures */ 639 table = calloc((size_t) num, sizeof(struct curl_slist *)); 640 if(!table) 641 return CURLE_OUT_OF_MEMORY; 642 643 ci->num_of_certs = num; 644 ci->certinfo = table; 645 646 return CURLE_OK; 647 } 648 649 /* 650 * 'value' is NOT a zero terminated string 651 */ 652 CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, 653 int certnum, 654 const char *label, 655 const char *value, 656 size_t valuelen) 657 { 658 struct curl_certinfo *ci = &data->info.certs; 659 char *output; 660 struct curl_slist *nl; 661 CURLcode result = CURLE_OK; 662 size_t labellen = strlen(label); 663 size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */ 664 665 output = malloc(outlen); 666 if(!output) 667 return CURLE_OUT_OF_MEMORY; 668 669 /* sprintf the label and colon */ 670 snprintf(output, outlen, "%s:", label); 671 672 /* memcpy the value (it might not be zero terminated) */ 673 memcpy(&output[labellen+1], value, valuelen); 674 675 /* zero terminate the output */ 676 output[labellen + 1 + valuelen] = 0; 677 678 nl = Curl_slist_append_nodup(ci->certinfo[certnum], output); 679 if(!nl) { 680 free(output); 681 curl_slist_free_all(ci->certinfo[certnum]); 682 result = CURLE_OUT_OF_MEMORY; 683 } 684 685 ci->certinfo[certnum] = nl; 686 return result; 687 } 688 689 /* 690 * This is a convenience function for push_certinfo_len that takes a zero 691 * terminated value. 692 */ 693 CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, 694 int certnum, 695 const char *label, 696 const char *value) 697 { 698 size_t valuelen = strlen(value); 699 700 return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); 701 } 702 703 CURLcode Curl_ssl_random(struct Curl_easy *data, 704 unsigned char *entropy, 705 size_t length) 706 { 707 int rc = curlssl_random(data, entropy, length); 708 if(rc) { 709 failf(data, "PRNG seeding failed"); 710 return CURLE_FAILED_INIT; /* possibly weird return code */ 711 } 712 return CURLE_OK; 713 } 714 715 /* 716 * Public key pem to der conversion 717 */ 718 719 static CURLcode pubkey_pem_to_der(const char *pem, 720 unsigned char **der, size_t *der_len) 721 { 722 char *stripped_pem, *begin_pos, *end_pos; 723 size_t pem_count, stripped_pem_count = 0, pem_len; 724 CURLcode result; 725 726 /* if no pem, exit. */ 727 if(!pem) 728 return CURLE_BAD_CONTENT_ENCODING; 729 730 begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); 731 if(!begin_pos) 732 return CURLE_BAD_CONTENT_ENCODING; 733 734 pem_count = begin_pos - pem; 735 /* Invalid if not at beginning AND not directly following \n */ 736 if(0 != pem_count && '\n' != pem[pem_count - 1]) 737 return CURLE_BAD_CONTENT_ENCODING; 738 739 /* 26 is length of "-----BEGIN PUBLIC KEY-----" */ 740 pem_count += 26; 741 742 /* Invalid if not directly following \n */ 743 end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----"); 744 if(!end_pos) 745 return CURLE_BAD_CONTENT_ENCODING; 746 747 pem_len = end_pos - pem; 748 749 stripped_pem = malloc(pem_len - pem_count + 1); 750 if(!stripped_pem) 751 return CURLE_OUT_OF_MEMORY; 752 753 /* 754 * Here we loop through the pem array one character at a time between the 755 * correct indices, and place each character that is not '\n' or '\r' 756 * into the stripped_pem array, which should represent the raw base64 string 757 */ 758 while(pem_count < pem_len) { 759 if('\n' != pem[pem_count] && '\r' != pem[pem_count]) 760 stripped_pem[stripped_pem_count++] = pem[pem_count]; 761 ++pem_count; 762 } 763 /* Place the null terminator in the correct place */ 764 stripped_pem[stripped_pem_count] = '\0'; 765 766 result = Curl_base64_decode(stripped_pem, der, der_len); 767 768 Curl_safefree(stripped_pem); 769 770 return result; 771 } 772 773 /* 774 * Generic pinned public key check. 775 */ 776 777 CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, 778 const char *pinnedpubkey, 779 const unsigned char *pubkey, size_t pubkeylen) 780 { 781 FILE *fp; 782 unsigned char *buf = NULL, *pem_ptr = NULL; 783 long filesize; 784 size_t size, pem_len; 785 CURLcode pem_read; 786 CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; 787 #ifdef curlssl_sha256sum 788 CURLcode encode; 789 size_t encodedlen, pinkeylen; 790 char *encoded, *pinkeycopy, *begin_pos, *end_pos; 791 unsigned char *sha256sumdigest = NULL; 792 #endif 793 794 /* if a path wasn't specified, don't pin */ 795 if(!pinnedpubkey) 796 return CURLE_OK; 797 if(!pubkey || !pubkeylen) 798 return result; 799 800 /* only do this if pinnedpubkey starts with "sha256//", length 8 */ 801 if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { 802 #ifdef curlssl_sha256sum 803 /* compute sha256sum of public key */ 804 sha256sumdigest = malloc(SHA256_DIGEST_LENGTH); 805 if(!sha256sumdigest) 806 return CURLE_OUT_OF_MEMORY; 807 curlssl_sha256sum(pubkey, pubkeylen, 808 sha256sumdigest, SHA256_DIGEST_LENGTH); 809 encode = Curl_base64_encode(data, (char *)sha256sumdigest, 810 SHA256_DIGEST_LENGTH, &encoded, &encodedlen); 811 Curl_safefree(sha256sumdigest); 812 813 if(encode) 814 return encode; 815 816 infof(data, "\t public key hash: sha256//%s\n", encoded); 817 818 /* it starts with sha256//, copy so we can modify it */ 819 pinkeylen = strlen(pinnedpubkey) + 1; 820 pinkeycopy = malloc(pinkeylen); 821 if(!pinkeycopy) { 822 Curl_safefree(encoded); 823 return CURLE_OUT_OF_MEMORY; 824 } 825 memcpy(pinkeycopy, pinnedpubkey, pinkeylen); 826 /* point begin_pos to the copy, and start extracting keys */ 827 begin_pos = pinkeycopy; 828 do { 829 end_pos = strstr(begin_pos, ";sha256//"); 830 /* 831 * if there is an end_pos, null terminate, 832 * otherwise it'll go to the end of the original string 833 */ 834 if(end_pos) 835 end_pos[0] = '\0'; 836 837 /* compare base64 sha256 digests, 8 is the length of "sha256//" */ 838 if(encodedlen == strlen(begin_pos + 8) && 839 !memcmp(encoded, begin_pos + 8, encodedlen)) { 840 result = CURLE_OK; 841 break; 842 } 843 844 /* 845 * change back the null-terminator we changed earlier, 846 * and look for next begin 847 */ 848 if(end_pos) { 849 end_pos[0] = ';'; 850 begin_pos = strstr(end_pos, "sha256//"); 851 } 852 } while(end_pos && begin_pos); 853 Curl_safefree(encoded); 854 Curl_safefree(pinkeycopy); 855 #else 856 /* without sha256 support, this cannot match */ 857 (void)data; 858 #endif 859 return result; 860 } 861 862 fp = fopen(pinnedpubkey, "rb"); 863 if(!fp) 864 return result; 865 866 do { 867 /* Determine the file's size */ 868 if(fseek(fp, 0, SEEK_END)) 869 break; 870 filesize = ftell(fp); 871 if(fseek(fp, 0, SEEK_SET)) 872 break; 873 if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) 874 break; 875 876 /* 877 * if the size of our certificate is bigger than the file 878 * size then it can't match 879 */ 880 size = curlx_sotouz((curl_off_t) filesize); 881 if(pubkeylen > size) 882 break; 883 884 /* 885 * Allocate buffer for the pinned key 886 * With 1 additional byte for null terminator in case of PEM key 887 */ 888 buf = malloc(size + 1); 889 if(!buf) 890 break; 891 892 /* Returns number of elements read, which should be 1 */ 893 if((int) fread(buf, size, 1, fp) != 1) 894 break; 895 896 /* If the sizes are the same, it can't be base64 encoded, must be der */ 897 if(pubkeylen == size) { 898 if(!memcmp(pubkey, buf, pubkeylen)) 899 result = CURLE_OK; 900 break; 901 } 902 903 /* 904 * Otherwise we will assume it's PEM and try to decode it 905 * after placing null terminator 906 */ 907 buf[size] = '\0'; 908 pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); 909 /* if it wasn't read successfully, exit */ 910 if(pem_read) 911 break; 912 913 /* 914 * if the size of our certificate doesn't match the size of 915 * the decoded file, they can't be the same, otherwise compare 916 */ 917 if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) 918 result = CURLE_OK; 919 } while(0); 920 921 Curl_safefree(buf); 922 Curl_safefree(pem_ptr); 923 fclose(fp); 924 925 return result; 926 } 927 928 #ifndef CURL_DISABLE_CRYPTO_AUTH 929 CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */ 930 size_t tmplen, 931 unsigned char *md5sum, /* output */ 932 size_t md5len) 933 { 934 #ifdef curlssl_md5sum 935 curlssl_md5sum(tmp, tmplen, md5sum, md5len); 936 #else 937 MD5_context *MD5pw; 938 939 (void) md5len; 940 941 MD5pw = Curl_MD5_init(Curl_DIGEST_MD5); 942 if(!MD5pw) 943 return CURLE_OUT_OF_MEMORY; 944 Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen)); 945 Curl_MD5_final(MD5pw, md5sum); 946 #endif 947 return CURLE_OK; 948 } 949 #endif 950 951 /* 952 * Check whether the SSL backend supports the status_request extension. 953 */ 954 bool Curl_ssl_cert_status_request(void) 955 { 956 #ifdef curlssl_cert_status_request 957 return curlssl_cert_status_request(); 958 #else 959 return FALSE; 960 #endif 961 } 962 963 /* 964 * Check whether the SSL backend supports false start. 965 */ 966 bool Curl_ssl_false_start(void) 967 { 968 #ifdef curlssl_false_start 969 return curlssl_false_start(); 970 #else 971 return FALSE; 972 #endif 973 } 974 975 #endif /* USE_SSL */ 976