1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, 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 http://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_polarssl_ - prefix for PolarSSL ones 35 Curl_cyassl_ - prefix for CyaSSL ones 36 37 Note that this source code uses curlssl_* functions, and they are all 38 defines/macros #defined by the lib-specific header files. 39 40 "SSL/TLS Strong Encryption: An Introduction" 41 http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html 42*/ 43 44#include "setup.h" 45 46#include <string.h> 47#include <stdlib.h> 48#include <ctype.h> 49#ifdef HAVE_SYS_SOCKET_H 50#include <sys/socket.h> 51#endif 52 53#include "urldata.h" 54#define SSLGEN_C 55#include "sslgen.h" /* generic SSL protos etc */ 56#include "ssluse.h" /* OpenSSL versions */ 57#include "gtls.h" /* GnuTLS versions */ 58#include "nssg.h" /* NSS versions */ 59#include "qssl.h" /* QSOSSL versions */ 60#include "polarssl.h" /* PolarSSL versions */ 61#include "axtls.h" /* axTLS versions */ 62#include "cyassl.h" /* CyaSSL versions */ 63#include "sendf.h" 64#include "rawstr.h" 65#include "url.h" 66#include "curl_memory.h" 67#include "progress.h" 68/* The last #include file should be: */ 69#include "memdebug.h" 70 71static bool safe_strequal(char* str1, char* str2) 72{ 73 if(str1 && str2) 74 /* both pointers point to something then compare them */ 75 return (bool)(0 != Curl_raw_equal(str1, str2)); 76 else 77 /* if both pointers are NULL then treat them as equal */ 78 return (bool)(!str1 && !str2); 79} 80 81bool 82Curl_ssl_config_matches(struct ssl_config_data* data, 83 struct ssl_config_data* needle) 84{ 85 if((data->version == needle->version) && 86 (data->verifypeer == needle->verifypeer) && 87 (data->verifyhost == needle->verifyhost) && 88 safe_strequal(data->CApath, needle->CApath) && 89 safe_strequal(data->CAfile, needle->CAfile) && 90 safe_strequal(data->random_file, needle->random_file) && 91 safe_strequal(data->egdsocket, needle->egdsocket) && 92 safe_strequal(data->cipher_list, needle->cipher_list)) 93 return TRUE; 94 95 return FALSE; 96} 97 98bool 99Curl_clone_ssl_config(struct ssl_config_data *source, 100 struct ssl_config_data *dest) 101{ 102 dest->sessionid = source->sessionid; 103 dest->verifyhost = source->verifyhost; 104 dest->verifypeer = source->verifypeer; 105 dest->version = source->version; 106 107 if(source->CAfile) { 108 dest->CAfile = strdup(source->CAfile); 109 if(!dest->CAfile) 110 return FALSE; 111 } 112 else 113 dest->CAfile = NULL; 114 115 if(source->CApath) { 116 dest->CApath = strdup(source->CApath); 117 if(!dest->CApath) 118 return FALSE; 119 } 120 else 121 dest->CApath = NULL; 122 123 if(source->cipher_list) { 124 dest->cipher_list = strdup(source->cipher_list); 125 if(!dest->cipher_list) 126 return FALSE; 127 } 128 else 129 dest->cipher_list = NULL; 130 131 if(source->egdsocket) { 132 dest->egdsocket = strdup(source->egdsocket); 133 if(!dest->egdsocket) 134 return FALSE; 135 } 136 else 137 dest->egdsocket = NULL; 138 139 if(source->random_file) { 140 dest->random_file = strdup(source->random_file); 141 if(!dest->random_file) 142 return FALSE; 143 } 144 else 145 dest->random_file = NULL; 146 147 return TRUE; 148} 149 150void Curl_free_ssl_config(struct ssl_config_data* sslc) 151{ 152 Curl_safefree(sslc->CAfile); 153 Curl_safefree(sslc->CApath); 154 Curl_safefree(sslc->cipher_list); 155 Curl_safefree(sslc->egdsocket); 156 Curl_safefree(sslc->random_file); 157} 158 159#ifdef USE_SSL 160 161/* "global" init done? */ 162static bool init_ssl=FALSE; 163 164/** 165 * Global SSL init 166 * 167 * @retval 0 error initializing SSL 168 * @retval 1 SSL initialized successfully 169 */ 170int Curl_ssl_init(void) 171{ 172 /* make sure this is only done once */ 173 if(init_ssl) 174 return 1; 175 init_ssl = TRUE; /* never again */ 176 177 return curlssl_init(); 178} 179 180 181/* Global cleanup */ 182void Curl_ssl_cleanup(void) 183{ 184 if(init_ssl) { 185 /* only cleanup if we did a previous init */ 186 curlssl_cleanup(); 187 init_ssl = FALSE; 188 } 189} 190 191CURLcode 192Curl_ssl_connect(struct connectdata *conn, int sockindex) 193{ 194 CURLcode res; 195 /* mark this is being ssl-enabled from here on. */ 196 conn->ssl[sockindex].use = TRUE; 197 conn->ssl[sockindex].state = ssl_connection_negotiating; 198 199 res = curlssl_connect(conn, sockindex); 200 201 if(!res) 202 Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ 203 204 return res; 205} 206 207CURLcode 208Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex, 209 bool *done) 210{ 211#ifdef curlssl_connect_nonblocking 212 CURLcode res; 213 /* mark this is being ssl requested from here on. */ 214 conn->ssl[sockindex].use = TRUE; 215 res = curlssl_connect_nonblocking(conn, sockindex, done); 216 if(!res && *done == TRUE) 217 Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ 218 return res; 219#else 220 *done = TRUE; /* fallback to BLOCKING */ 221 conn->ssl[sockindex].use = TRUE; 222 return curlssl_connect(conn, sockindex); 223#endif /* non-blocking connect support */ 224} 225 226/* 227 * Check if there's a session ID for the given connection in the cache, and if 228 * there's one suitable, it is provided. Returns TRUE when no entry matched. 229 */ 230int Curl_ssl_getsessionid(struct connectdata *conn, 231 void **ssl_sessionid, 232 size_t *idsize) /* set 0 if unknown */ 233{ 234 struct curl_ssl_session *check; 235 struct SessionHandle *data = conn->data; 236 long i; 237 238 if(!conn->ssl_config.sessionid) 239 /* session ID re-use is disabled */ 240 return TRUE; 241 242 for(i=0; i< data->set.ssl.numsessions; i++) { 243 check = &data->state.session[i]; 244 if(!check->sessionid) 245 /* not session ID means blank entry */ 246 continue; 247 if(Curl_raw_equal(conn->host.name, check->name) && 248 (conn->remote_port == check->remote_port) && 249 Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { 250 /* yes, we have a session ID! */ 251 data->state.sessionage++; /* increase general age */ 252 check->age = data->state.sessionage; /* set this as used in this age */ 253 *ssl_sessionid = check->sessionid; 254 if(idsize) 255 *idsize = check->idsize; 256 return FALSE; 257 } 258 } 259 *ssl_sessionid = NULL; 260 return TRUE; 261} 262 263/* 264 * Kill a single session ID entry in the cache. 265 */ 266static int kill_session(struct curl_ssl_session *session) 267{ 268 if(session->sessionid) { 269 /* defensive check */ 270 271 /* free the ID the SSL-layer specific way */ 272 curlssl_session_free(session->sessionid); 273 274 session->sessionid=NULL; 275 session->age = 0; /* fresh */ 276 277 Curl_free_ssl_config(&session->ssl_config); 278 279 Curl_safefree(session->name); 280 session->name = NULL; /* no name */ 281 282 return 0; /* ok */ 283 } 284 else 285 return 1; 286} 287 288/* 289 * Delete the given session ID from the cache. 290 */ 291void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid) 292{ 293 int i; 294 for(i=0; i< conn->data->set.ssl.numsessions; i++) { 295 struct curl_ssl_session *check = &conn->data->state.session[i]; 296 297 if(check->sessionid == ssl_sessionid) { 298 kill_session(check); 299 break; 300 } 301 } 302} 303 304/* 305 * Store session id in the session cache. The ID passed on to this function 306 * must already have been extracted and allocated the proper way for the SSL 307 * layer. Curl_XXXX_session_free() will be called to free/kill the session ID 308 * later on. 309 */ 310CURLcode Curl_ssl_addsessionid(struct connectdata *conn, 311 void *ssl_sessionid, 312 size_t idsize) 313{ 314 long i; 315 struct SessionHandle *data=conn->data; /* the mother of all structs */ 316 struct curl_ssl_session *store = &data->state.session[0]; 317 long oldest_age=data->state.session[0].age; /* zero if unused */ 318 char *clone_host; 319 320 /* Even though session ID re-use might be disabled, that only disables USING 321 IT. We still store it here in case the re-using is again enabled for an 322 upcoming transfer */ 323 324 clone_host = strdup(conn->host.name); 325 if(!clone_host) 326 return CURLE_OUT_OF_MEMORY; /* bail out */ 327 328 /* Now we should add the session ID and the host name to the cache, (remove 329 the oldest if necessary) */ 330 331 /* find an empty slot for us, or find the oldest */ 332 for(i=1; (i<data->set.ssl.numsessions) && 333 data->state.session[i].sessionid; i++) { 334 if(data->state.session[i].age < oldest_age) { 335 oldest_age = data->state.session[i].age; 336 store = &data->state.session[i]; 337 } 338 } 339 if(i == data->set.ssl.numsessions) 340 /* cache is full, we must "kill" the oldest entry! */ 341 kill_session(store); 342 else 343 store = &data->state.session[i]; /* use this slot */ 344 345 /* now init the session struct wisely */ 346 store->sessionid = ssl_sessionid; 347 store->idsize = idsize; 348 store->age = data->state.sessionage; /* set current age */ 349 if(store->name) 350 /* free it if there's one already present */ 351 free(store->name); 352 store->name = clone_host; /* clone host name */ 353 store->remote_port = conn->remote_port; /* port number */ 354 355 if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) { 356 store->sessionid = NULL; /* let caller free sessionid */ 357 free(clone_host); 358 return CURLE_OUT_OF_MEMORY; 359 } 360 361 return CURLE_OK; 362} 363 364 365void Curl_ssl_close_all(struct SessionHandle *data) 366{ 367 long i; 368 /* kill the session ID cache */ 369 if(data->state.session) { 370 for(i=0; i< data->set.ssl.numsessions; i++) 371 /* the single-killer function handles empty table slots */ 372 kill_session(&data->state.session[i]); 373 374 /* free the cache data */ 375 free(data->state.session); 376 data->state.session = NULL; 377 } 378 379 curlssl_close_all(data); 380} 381 382void Curl_ssl_close(struct connectdata *conn, int sockindex) 383{ 384 DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); 385 curlssl_close(conn, sockindex); 386} 387 388CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex) 389{ 390 if(curlssl_shutdown(conn, sockindex)) 391 return CURLE_SSL_SHUTDOWN_FAILED; 392 393 conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */ 394 conn->ssl[sockindex].state = ssl_connection_none; 395 396 conn->recv[sockindex] = Curl_recv_plain; 397 conn->send[sockindex] = Curl_send_plain; 398 399 return CURLE_OK; 400} 401 402/* Selects an SSL crypto engine 403 */ 404CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine) 405{ 406 return curlssl_set_engine(data, engine); 407} 408 409/* Selects the default SSL crypto engine 410 */ 411CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data) 412{ 413 return curlssl_set_engine_default(data); 414} 415 416/* Return list of OpenSSL crypto engine names. */ 417struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data) 418{ 419 return curlssl_engines_list(data); 420} 421 422/* 423 * This sets up a session ID cache to the specified size. Make sure this code 424 * is agnostic to what underlying SSL technology we use. 425 */ 426CURLcode Curl_ssl_initsessions(struct SessionHandle *data, long amount) 427{ 428 struct curl_ssl_session *session; 429 430 if(data->state.session) 431 /* this is just a precaution to prevent multiple inits */ 432 return CURLE_OK; 433 434 session = calloc(amount, sizeof(struct curl_ssl_session)); 435 if(!session) 436 return CURLE_OUT_OF_MEMORY; 437 438 /* store the info in the SSL section */ 439 data->set.ssl.numsessions = amount; 440 data->state.session = session; 441 data->state.sessionage = 1; /* this is brand new */ 442 return CURLE_OK; 443} 444 445size_t Curl_ssl_version(char *buffer, size_t size) 446{ 447 return curlssl_version(buffer, size); 448} 449 450/* 451 * This function tries to determine connection status. 452 * 453 * Return codes: 454 * 1 means the connection is still in place 455 * 0 means the connection has been closed 456 * -1 means the connection status is unknown 457 */ 458int Curl_ssl_check_cxn(struct connectdata *conn) 459{ 460 return curlssl_check_cxn(conn); 461} 462 463bool Curl_ssl_data_pending(const struct connectdata *conn, 464 int connindex) 465{ 466 return curlssl_data_pending(conn, connindex); 467} 468 469void Curl_ssl_free_certinfo(struct SessionHandle *data) 470{ 471 int i; 472 struct curl_certinfo *ci = &data->info.certs; 473 if(ci->num_of_certs) { 474 /* free all individual lists used */ 475 for(i=0; i<ci->num_of_certs; i++) 476 curl_slist_free_all(ci->certinfo[i]); 477 free(ci->certinfo); /* free the actual array too */ 478 ci->num_of_certs = 0; 479 } 480} 481#endif /* USE_SSL */ 482