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