1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2009, 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 * $Id: gtls.c,v 1.55 2009-02-25 12:51:17 bagder Exp $
22 ***************************************************************************/
23
24 /*
25 * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code
26 * but sslgen.c should ever call or use these functions.
27 *
28 * Note: don't use the GnuTLS' *_t variable type names in this source code,
29 * since they were not present in 1.0.X.
30 */
31
32 #include "setup.h"
33 #ifdef USE_GNUTLS
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
36 #include <gcrypt.h>
37
38 #include <string.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #ifdef HAVE_SYS_SOCKET_H
42 #include <sys/socket.h>
43 #endif
44
45 #include "urldata.h"
46 #include "sendf.h"
47 #include "inet_pton.h"
48 #include "gtls.h"
49 #include "sslgen.h"
50 #include "parsedate.h"
51 #include "connect.h" /* for the connect timeout */
52 #include "select.h"
53 #include "rawstr.h"
54
55 #define _MPRINTF_REPLACE /* use our functions only */
56 #include <curl/mprintf.h>
57 #include "memory.h"
58 /* The last #include file should be: */
59 #include "memdebug.h"
60
61 /* Enable GnuTLS debugging by defining GTLSDEBUG */
62 /*#define GTLSDEBUG */
63
64 #ifdef GTLSDEBUG
tls_log_func(int level,const char * str)65 static void tls_log_func(int level, const char *str)
66 {
67 fprintf(stderr, "|<%d>| %s", level, str);
68 }
69 #endif
70 static bool gtls_inited = FALSE;
71 /*
72 * Custom push and pull callback functions used by GNU TLS to read and write
73 * to the socket. These functions are simple wrappers to send() and recv()
74 * (although here using the sread/swrite macros as defined by setup_once.h).
75 * We use custom functions rather than the GNU TLS defaults because it allows
76 * us to get specific about the fourth "flags" argument, and to use arbitrary
77 * private data with gnutls_transport_set_ptr if we wish.
78 */
Curl_gtls_push(void * s,const void * buf,size_t len)79 static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len)
80 {
81 return swrite(s, buf, len);
82 }
83
Curl_gtls_pull(void * s,void * buf,size_t len)84 static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len)
85 {
86 return sread(s, buf, len);
87 }
88
89 /* Curl_gtls_init()
90 *
91 * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that
92 * are not thread-safe and thus this function itself is not thread-safe and
93 * must only be called from within curl_global_init() to keep the thread
94 * situation under control!
95 */
Curl_gtls_init(void)96 int Curl_gtls_init(void)
97 {
98 int ret = 1;
99 if(!gtls_inited) {
100 ret = gnutls_global_init()?0:1;
101 #ifdef GTLSDEBUG
102 gnutls_global_set_log_function(tls_log_func);
103 gnutls_global_set_log_level(2);
104 #endif
105 gtls_inited = TRUE;
106 }
107 return ret;
108 }
109
Curl_gtls_cleanup(void)110 int Curl_gtls_cleanup(void)
111 {
112 if(gtls_inited) {
113 gnutls_global_deinit();
114 gtls_inited = FALSE;
115 }
116 return 1;
117 }
118
showtime(struct SessionHandle * data,const char * text,time_t stamp)119 static void showtime(struct SessionHandle *data,
120 const char *text,
121 time_t stamp)
122 {
123 struct tm *tm;
124 #ifdef HAVE_GMTIME_R
125 struct tm buffer;
126 tm = (struct tm *)gmtime_r(&stamp, &buffer);
127 #else
128 tm = gmtime(&stamp);
129 #endif
130 snprintf(data->state.buffer,
131 BUFSIZE,
132 "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
133 text,
134 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
135 tm->tm_mday,
136 Curl_month[tm->tm_mon],
137 tm->tm_year + 1900,
138 tm->tm_hour,
139 tm->tm_min,
140 tm->tm_sec);
141 infof(data, "%s", data->state.buffer);
142 }
143
load_file(const char * file)144 static gnutls_datum load_file (const char *file)
145 {
146 FILE *f;
147 gnutls_datum loaded_file = { NULL, 0 };
148 long filelen;
149 void *ptr;
150
151 if (!(f = fopen(file, "r"))
152 || fseek(f, 0, SEEK_END) != 0
153 || (filelen = ftell(f)) < 0
154 || fseek(f, 0, SEEK_SET) != 0
155 || !(ptr = malloc((size_t)filelen))
156 || fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
157 return loaded_file;
158 }
159
160 loaded_file.data = ptr;
161 loaded_file.size = (unsigned int)filelen;
162 return loaded_file;
163 }
164
unload_file(gnutls_datum data)165 static void unload_file(gnutls_datum data) {
166 free(data.data);
167 }
168
169
170 /* this function does a BLOCKING SSL/TLS (re-)handshake */
handshake(struct connectdata * conn,gnutls_session session,int sockindex,bool duringconnect)171 static CURLcode handshake(struct connectdata *conn,
172 gnutls_session session,
173 int sockindex,
174 bool duringconnect)
175 {
176 struct SessionHandle *data = conn->data;
177 int rc;
178 if(!gtls_inited)
179 Curl_gtls_init();
180 do {
181 rc = gnutls_handshake(session);
182
183 if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
184 long timeout_ms = Curl_timeleft(conn, NULL, duringconnect);
185
186 if(timeout_ms < 0) {
187 /* a precaution, no need to continue if time already is up */
188 failf(data, "SSL connection timeout");
189 return CURLE_OPERATION_TIMEDOUT;
190 }
191
192 rc = Curl_socket_ready(conn->sock[sockindex],
193 conn->sock[sockindex], (int)timeout_ms);
194 if(rc > 0)
195 /* reabable or writable, go loop*/
196 continue;
197 else if(0 == rc) {
198 /* timeout */
199 failf(data, "SSL connection timeout");
200 return CURLE_OPERATION_TIMEDOUT;
201 }
202 else {
203 /* anything that gets here is fatally bad */
204 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
205 return CURLE_SSL_CONNECT_ERROR;
206 }
207 }
208 else
209 break;
210 } while(1);
211
212 if(rc < 0) {
213 failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc));
214 return CURLE_SSL_CONNECT_ERROR;
215 }
216
217 return CURLE_OK;
218 }
219
do_file_type(const char * type)220 static gnutls_x509_crt_fmt do_file_type(const char *type)
221 {
222 if(!type || !type[0])
223 return GNUTLS_X509_FMT_PEM;
224 if(Curl_raw_equal(type, "PEM"))
225 return GNUTLS_X509_FMT_PEM;
226 if(Curl_raw_equal(type, "DER"))
227 return GNUTLS_X509_FMT_DER;
228 return -1;
229 }
230
231
232 /*
233 * This function is called after the TCP connect has completed. Setup the TLS
234 * layer and do all necessary magic.
235 */
236 CURLcode
Curl_gtls_connect(struct connectdata * conn,int sockindex)237 Curl_gtls_connect(struct connectdata *conn,
238 int sockindex)
239
240 {
241 static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
242 struct SessionHandle *data = conn->data;
243 gnutls_session session;
244 int rc;
245 unsigned int cert_list_size;
246 const gnutls_datum *chainp;
247 unsigned int verify_status;
248 gnutls_x509_crt x509_cert,x509_issuer;
249 gnutls_datum issuerp;
250 char certbuf[256]; /* big enough? */
251 size_t size;
252 unsigned int algo;
253 unsigned int bits;
254 time_t certclock;
255 const char *ptr;
256 void *ssl_sessionid;
257 size_t ssl_idsize;
258 #ifdef ENABLE_IPV6
259 struct in6_addr addr;
260 #else
261 struct in_addr addr;
262 #endif
263
264 if(conn->ssl[sockindex].state == ssl_connection_complete)
265 /* to make us tolerant against being called more than once for the
266 same connection */
267 return CURLE_OK;
268
269 if(!gtls_inited)
270 Curl_gtls_init();
271
272 /* GnuTLS only supports SSLv3 and TLSv1 */
273 if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
274 failf(data, "GnuTLS does not support SSLv2");
275 return CURLE_SSL_CONNECT_ERROR;
276 }
277
278 /* allocate a cred struct */
279 rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
280 if(rc < 0) {
281 failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
282 return CURLE_SSL_CONNECT_ERROR;
283 }
284
285 if(data->set.ssl.CAfile) {
286 /* set the trusted CA cert bundle file */
287 gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
288 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
289
290 rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
291 data->set.ssl.CAfile,
292 GNUTLS_X509_FMT_PEM);
293 if(rc < 0) {
294 infof(data, "error reading ca cert file %s (%s)\n",
295 data->set.ssl.CAfile, gnutls_strerror(rc));
296 if(data->set.ssl.verifypeer)
297 return CURLE_SSL_CACERT_BADFILE;
298 }
299 else
300 infof(data, "found %d certificates in %s\n",
301 rc, data->set.ssl.CAfile);
302 }
303
304 if(data->set.ssl.CRLfile) {
305 /* set the CRL list file */
306 rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
307 data->set.ssl.CRLfile,
308 GNUTLS_X509_FMT_PEM);
309 if(rc < 0) {
310 failf(data, "error reading crl file %s (%s)\n",
311 data->set.ssl.CRLfile, gnutls_strerror(rc));
312 return CURLE_SSL_CRL_BADFILE;
313 }
314 else
315 infof(data, "found %d CRL in %s\n",
316 rc, data->set.ssl.CRLfile);
317 }
318
319 /* Initialize TLS session as a client */
320 rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
321 if(rc) {
322 failf(data, "gnutls_init() failed: %d", rc);
323 return CURLE_SSL_CONNECT_ERROR;
324 }
325
326 /* convenient assign */
327 session = conn->ssl[sockindex].session;
328
329 if ((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
330 #ifdef ENABLE_IPV6
331 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
332 #endif
333 (gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name,
334 strlen(conn->host.name)) < 0))
335 infof(data, "WARNING: failed to configure server name indication (SNI) "
336 "TLS extension\n");
337
338 /* Use default priorities */
339 rc = gnutls_set_default_priority(session);
340 if(rc < 0)
341 return CURLE_SSL_CONNECT_ERROR;
342
343 if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) {
344 static const int protocol_priority[] = { GNUTLS_SSL3, 0 };
345 gnutls_protocol_set_priority(session, protocol_priority);
346 if(rc < 0)
347 return CURLE_SSL_CONNECT_ERROR;
348 }
349
350 /* Sets the priority on the certificate types supported by gnutls. Priority
351 is higher for types specified before others. After specifying the types
352 you want, you must append a 0. */
353 rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
354 if(rc < 0)
355 return CURLE_SSL_CONNECT_ERROR;
356
357 if(data->set.str[STRING_CERT]) {
358 if( gnutls_certificate_set_x509_key_file(
359 conn->ssl[sockindex].cred,
360 data->set.str[STRING_CERT],
361 data->set.str[STRING_KEY] ?
362 data->set.str[STRING_KEY] : data->set.str[STRING_CERT],
363 do_file_type(data->set.str[STRING_CERT_TYPE]) ) ) {
364 failf(data, "error reading X.509 key or certificate file");
365 return CURLE_SSL_CONNECT_ERROR;
366 }
367 }
368
369 /* put the credentials to the current session */
370 rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
371 conn->ssl[sockindex].cred);
372
373 /* set the connection handle (file descriptor for the socket) */
374 gnutls_transport_set_ptr(session,
375 (gnutls_transport_ptr)conn->sock[sockindex]);
376
377 /* register callback functions to send and receive data. */
378 gnutls_transport_set_push_function(session, Curl_gtls_push);
379 gnutls_transport_set_pull_function(session, Curl_gtls_pull);
380
381 /* lowat must be set to zero when using custom push and pull functions. */
382 gnutls_transport_set_lowat(session, 0);
383
384 /* This might be a reconnect, so we check for a session ID in the cache
385 to speed up things */
386
387 if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
388 /* we got a session id, use it! */
389 gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
390
391 /* Informational message */
392 infof (data, "SSL re-using session ID\n");
393 }
394
395 rc = handshake(conn, session, sockindex, TRUE);
396 if(rc)
397 /* handshake() sets its own error message with failf() */
398 return rc;
399
400 /* This function will return the peer's raw certificate (chain) as sent by
401 the peer. These certificates are in raw format (DER encoded for
402 X.509). In case of a X.509 then a certificate list may be present. The
403 first certificate in the list is the peer's certificate, following the
404 issuer's certificate, then the issuer's issuer etc. */
405
406 chainp = gnutls_certificate_get_peers(session, &cert_list_size);
407 if(!chainp) {
408 if(data->set.ssl.verifypeer ||
409 data->set.ssl.verifyhost ||
410 data->set.ssl.issuercert) {
411 failf(data, "failed to get server cert");
412 return CURLE_PEER_FAILED_VERIFICATION;
413 }
414 infof(data, "\t common name: WARNING couldn't obtain\n");
415 }
416
417 if(data->set.ssl.verifypeer) {
418 /* This function will try to verify the peer's certificate and return its
419 status (trusted, invalid etc.). The value of status should be one or
420 more of the gnutls_certificate_status_t enumerated elements bitwise
421 or'd. To avoid denial of service attacks some default upper limits
422 regarding the certificate key size and chain size are set. To override
423 them use gnutls_certificate_set_verify_limits(). */
424
425 rc = gnutls_certificate_verify_peers2(session, &verify_status);
426 if(rc < 0) {
427 failf(data, "server cert verify failed: %d", rc);
428 return CURLE_SSL_CONNECT_ERROR;
429 }
430
431 /* verify_status is a bitmask of gnutls_certificate_status bits */
432 if(verify_status & GNUTLS_CERT_INVALID) {
433 if(data->set.ssl.verifypeer) {
434 failf(data, "server certificate verification failed. CAfile: %s "
435 "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
436 data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
437 return CURLE_SSL_CACERT;
438 }
439 else
440 infof(data, "\t server certificate verification FAILED\n");
441 }
442 else
443 infof(data, "\t server certificate verification OK\n");
444 }
445 else
446 infof(data, "\t server certificate verification SKIPPED\n");
447
448 /* initialize an X.509 certificate structure. */
449 gnutls_x509_crt_init(&x509_cert);
450
451 /* convert the given DER or PEM encoded Certificate to the native
452 gnutls_x509_crt_t format */
453 gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
454
455 if (data->set.ssl.issuercert) {
456 gnutls_x509_crt_init(&x509_issuer);
457 issuerp = load_file(data->set.ssl.issuercert);
458 gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
459 rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
460 unload_file(issuerp);
461 if (rc <= 0) {
462 failf(data, "server certificate issuer check failed (IssuerCert: %s)",
463 data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
464 return CURLE_SSL_ISSUER_ERROR;
465 }
466 infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
467 data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
468 }
469
470 size=sizeof(certbuf);
471 rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
472 0, /* the first and only one */
473 FALSE,
474 certbuf,
475 &size);
476 if(rc) {
477 infof(data, "error fetching CN from cert:%s\n",
478 gnutls_strerror(rc));
479 }
480
481 /* This function will check if the given certificate's subject matches the
482 given hostname. This is a basic implementation of the matching described
483 in RFC2818 (HTTPS), which takes into account wildcards, and the subject
484 alternative name PKIX extension. Returns non zero on success, and zero on
485 failure. */
486 rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
487
488 if(!rc) {
489 if(data->set.ssl.verifyhost > 1) {
490 failf(data, "SSL: certificate subject name (%s) does not match "
491 "target host name '%s'", certbuf, conn->host.dispname);
492 gnutls_x509_crt_deinit(x509_cert);
493 return CURLE_PEER_FAILED_VERIFICATION;
494 }
495 else
496 infof(data, "\t common name: %s (does not match '%s')\n",
497 certbuf, conn->host.dispname);
498 }
499 else
500 infof(data, "\t common name: %s (matched)\n", certbuf);
501
502 /* Check for time-based validity */
503 certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
504
505 if(certclock == (time_t)-1) {
506 failf(data, "server cert expiration date verify failed");
507 return CURLE_SSL_CONNECT_ERROR;
508 }
509
510 if(certclock < time(NULL)) {
511 if(data->set.ssl.verifypeer) {
512 failf(data, "server certificate expiration date has passed.");
513 return CURLE_PEER_FAILED_VERIFICATION;
514 }
515 else
516 infof(data, "\t server certificate expiration date FAILED\n");
517 }
518 else
519 infof(data, "\t server certificate expiration date OK\n");
520
521 certclock = gnutls_x509_crt_get_activation_time(x509_cert);
522
523 if(certclock == (time_t)-1) {
524 failf(data, "server cert activation date verify failed");
525 return CURLE_SSL_CONNECT_ERROR;
526 }
527
528 if(certclock > time(NULL)) {
529 if(data->set.ssl.verifypeer) {
530 failf(data, "server certificate not activated yet.");
531 return CURLE_PEER_FAILED_VERIFICATION;
532 }
533 else
534 infof(data, "\t server certificate activation date FAILED\n");
535 }
536 else
537 infof(data, "\t server certificate activation date OK\n");
538
539 /* Show:
540
541 - ciphers used
542 - subject
543 - start date
544 - expire date
545 - common name
546 - issuer
547
548 */
549
550 /* public key algorithm's parameters */
551 algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
552 infof(data, "\t certificate public key: %s\n",
553 gnutls_pk_algorithm_get_name(algo));
554
555 /* version of the X.509 certificate. */
556 infof(data, "\t certificate version: #%d\n",
557 gnutls_x509_crt_get_version(x509_cert));
558
559
560 size = sizeof(certbuf);
561 gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
562 infof(data, "\t subject: %s\n", certbuf);
563
564 certclock = gnutls_x509_crt_get_activation_time(x509_cert);
565 showtime(data, "start date", certclock);
566
567 certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
568 showtime(data, "expire date", certclock);
569
570 size = sizeof(certbuf);
571 gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
572 infof(data, "\t issuer: %s\n", certbuf);
573
574 gnutls_x509_crt_deinit(x509_cert);
575
576 /* compression algorithm (if any) */
577 ptr = gnutls_compression_get_name(gnutls_compression_get(session));
578 /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
579 infof(data, "\t compression: %s\n", ptr);
580
581 /* the name of the cipher used. ie 3DES. */
582 ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
583 infof(data, "\t cipher: %s\n", ptr);
584
585 /* the MAC algorithms name. ie SHA1 */
586 ptr = gnutls_mac_get_name(gnutls_mac_get(session));
587 infof(data, "\t MAC: %s\n", ptr);
588
589 conn->ssl[sockindex].state = ssl_connection_complete;
590
591 if(!ssl_sessionid) {
592 /* this session was not previously in the cache, add it now */
593
594 /* get the session ID data size */
595 gnutls_session_get_data(session, NULL, &ssl_idsize);
596 ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */
597
598 if(ssl_sessionid) {
599 /* extract session ID to the allocated buffer */
600 gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize);
601
602 /* store this session id */
603 return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize);
604 }
605 }
606
607 return CURLE_OK;
608 }
609
610
611 /* return number of sent (non-SSL) bytes */
Curl_gtls_send(struct connectdata * conn,int sockindex,const void * mem,size_t len)612 ssize_t Curl_gtls_send(struct connectdata *conn,
613 int sockindex,
614 const void *mem,
615 size_t len)
616 {
617 ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
618
619 if(rc < 0 ) {
620 if(rc == GNUTLS_E_AGAIN)
621 return 0; /* EWOULDBLOCK equivalent */
622 rc = -1; /* generic error code for send failure */
623 }
624
625 return rc;
626 }
627
Curl_gtls_close_all(struct SessionHandle * data)628 void Curl_gtls_close_all(struct SessionHandle *data)
629 {
630 /* FIX: make the OpenSSL code more generic and use parts of it here */
631 (void)data;
632 }
633
close_one(struct connectdata * conn,int idx)634 static void close_one(struct connectdata *conn,
635 int idx)
636 {
637 if(conn->ssl[idx].session) {
638 gnutls_bye(conn->ssl[idx].session, GNUTLS_SHUT_RDWR);
639 gnutls_deinit(conn->ssl[idx].session);
640 conn->ssl[idx].session = NULL;
641 }
642 if(conn->ssl[idx].cred) {
643 gnutls_certificate_free_credentials(conn->ssl[idx].cred);
644 conn->ssl[idx].cred = NULL;
645 }
646 }
647
Curl_gtls_close(struct connectdata * conn,int sockindex)648 void Curl_gtls_close(struct connectdata *conn, int sockindex)
649 {
650 close_one(conn, sockindex);
651 }
652
653 /*
654 * This function is called to shut down the SSL layer but keep the
655 * socket open (CCC - Clear Command Channel)
656 */
Curl_gtls_shutdown(struct connectdata * conn,int sockindex)657 int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
658 {
659 int result;
660 int retval = 0;
661 struct SessionHandle *data = conn->data;
662 int done = 0;
663 char buf[120];
664
665 /* This has only been tested on the proftpd server, and the mod_tls code
666 sends a close notify alert without waiting for a close notify alert in
667 response. Thus we wait for a close notify alert from the server, but
668 we do not send one. Let's hope other servers do the same... */
669
670 if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
671 gnutls_bye(conn->ssl[sockindex].session, GNUTLS_SHUT_WR);
672
673 if(conn->ssl[sockindex].session) {
674 while(!done) {
675 int what = Curl_socket_ready(conn->sock[sockindex],
676 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
677 if(what > 0) {
678 /* Something to read, let's do it and hope that it is the close
679 notify alert from the server */
680 result = gnutls_record_recv(conn->ssl[sockindex].session,
681 buf, sizeof(buf));
682 switch(result) {
683 case 0:
684 /* This is the expected response. There was no data but only
685 the close notify alert */
686 done = 1;
687 break;
688 case GNUTLS_E_AGAIN:
689 case GNUTLS_E_INTERRUPTED:
690 infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
691 break;
692 default:
693 retval = -1;
694 done = 1;
695 break;
696 }
697 }
698 else if(0 == what) {
699 /* timeout */
700 failf(data, "SSL shutdown timeout");
701 done = 1;
702 break;
703 }
704 else {
705 /* anything that gets here is fatally bad */
706 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
707 retval = -1;
708 done = 1;
709 }
710 }
711 gnutls_deinit(conn->ssl[sockindex].session);
712 }
713 gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
714
715 conn->ssl[sockindex].cred = NULL;
716 conn->ssl[sockindex].session = NULL;
717
718 return retval;
719 }
720
721 /*
722 * If the read would block we return -1 and set 'wouldblock' to TRUE.
723 * Otherwise we return the amount of data read. Other errors should return -1
724 * and set 'wouldblock' to FALSE.
725 */
Curl_gtls_recv(struct connectdata * conn,int num,char * buf,size_t buffersize,bool * wouldblock)726 ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
727 int num, /* socketindex */
728 char *buf, /* store read data here */
729 size_t buffersize, /* max amount to read */
730 bool *wouldblock)
731 {
732 ssize_t ret;
733
734 ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
735 if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
736 *wouldblock = TRUE;
737 return -1;
738 }
739
740 if(ret == GNUTLS_E_REHANDSHAKE) {
741 /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
742 proper way" takes a whole lot of work. */
743 CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE);
744 if(rc)
745 /* handshake() writes error message on its own */
746 return rc;
747 *wouldblock = TRUE; /* then return as if this was a wouldblock */
748 return -1;
749 }
750
751 *wouldblock = FALSE;
752 if(!ret) {
753 failf(conn->data, "Peer closed the TLS connection");
754 return -1;
755 }
756
757 if(ret < 0) {
758 failf(conn->data, "GnuTLS recv error (%d): %s",
759 (int)ret, gnutls_strerror(ret));
760 return -1;
761 }
762
763 return ret;
764 }
765
Curl_gtls_session_free(void * ptr)766 void Curl_gtls_session_free(void *ptr)
767 {
768 free(ptr);
769 }
770
Curl_gtls_version(char * buffer,size_t size)771 size_t Curl_gtls_version(char *buffer, size_t size)
772 {
773 return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
774 }
775
Curl_gtls_seed(struct SessionHandle * data)776 int Curl_gtls_seed(struct SessionHandle *data)
777 {
778 /* we have the "SSL is seeded" boolean static to prevent multiple
779 time-consuming seedings in vain */
780 static bool ssl_seeded = FALSE;
781
782 /* Quickly add a bit of entropy */
783 gcry_fast_random_poll();
784
785 if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
786 data->set.str[STRING_SSL_EGDSOCKET]) {
787
788 /* TODO: to a good job seeding the RNG
789 This may involve the gcry_control function and these options:
790 GCRYCTL_SET_RANDOM_SEED_FILE
791 GCRYCTL_SET_RNDEGD_SOCKET
792 */
793 ssl_seeded = TRUE;
794 }
795 return 0;
796 }
797
798 #endif /* USE_GNUTLS */
799