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