1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: gtls.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
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.
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.
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
46 #include "inet_pton.h"
49 #include "parsedate.h"
50 #include "connect.h" /* for the connect timeout */
52 #define _MPRINTF_REPLACE /* use our functions only */
53 #include <curl/mprintf.h>
55 /* The last #include file should be: */
58 /* Enable GnuTLS debugging by defining GTLSDEBUG */
59 /*#define GTLSDEBUG */
62 static void tls_log_func(int level
, const char *str
)
64 fprintf(stderr
, "|<%d>| %s", level
, str
);
67 static bool gtls_inited
= FALSE
;
69 * Custom push and pull callback functions used by GNU TLS to read and write
70 * to the socket. These functions are simple wrappers to send() and recv()
71 * (although here using the sread/swrite macros as defined by setup_once.h).
72 * We use custom functions rather than the GNU TLS defaults because it allows
73 * us to get specific about the fourth "flags" argument, and to use arbitrary
74 * private data with gnutls_transport_set_ptr if we wish.
76 static ssize_t
Curl_gtls_push(void *s
, const void *buf
, size_t len
)
78 return swrite(s
, buf
, len
);
81 static ssize_t
Curl_gtls_pull(void *s
, void *buf
, size_t len
)
83 return sread(s
, buf
, len
);
86 /* Global GnuTLS init, called from Curl_ssl_init() */
87 int Curl_gtls_init(void)
89 /* Unfortunately we can not init here, things like curl --version will
90 * fail to work if there is no egd socket available because libgcrypt
91 * will EXIT the application!!
92 * By doing the actual init later (before actually trying to use GnuTLS),
93 * we can at least provide basic info etc.
98 static int _Curl_gtls_init(void)
102 ret
= gnutls_global_init()?0:1;
104 gnutls_global_set_log_function(tls_log_func
);
105 gnutls_global_set_log_level(2);
112 int Curl_gtls_cleanup(void)
115 gnutls_global_deinit();
121 static void showtime(struct SessionHandle
*data
,
128 tm
= (struct tm
*)gmtime_r(&stamp
, &buffer
);
132 snprintf(data
->state
.buffer
,
134 "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
136 Curl_wkday
[tm
->tm_wday
?tm
->tm_wday
-1:6],
138 Curl_month
[tm
->tm_mon
],
143 infof(data
, "%s", data
->state
.buffer
);
146 static gnutls_datum
load_file (const char *file
)
149 gnutls_datum loaded_file
= { NULL
, 0 };
153 if (!(f
= fopen(file
, "r"))
154 || fseek(f
, 0, SEEK_END
) != 0
155 || (filelen
= ftell(f
)) < 0
156 || fseek(f
, 0, SEEK_SET
) != 0
157 || !(ptr
= malloc((size_t)filelen
))
158 || fread(ptr
, 1, (size_t)filelen
, f
) < (size_t)filelen
) {
162 loaded_file
.data
= ptr
;
163 loaded_file
.size
= (unsigned int)filelen
;
167 static void unload_file(gnutls_datum data
) {
172 /* this function does a BLOCKING SSL/TLS (re-)handshake */
173 static CURLcode
handshake(struct connectdata
*conn
,
174 gnutls_session session
,
178 struct SessionHandle
*data
= conn
->data
;
183 rc
= gnutls_handshake(session
);
185 if((rc
== GNUTLS_E_AGAIN
) || (rc
== GNUTLS_E_INTERRUPTED
)) {
186 long timeout_ms
= Curl_timeleft(conn
, NULL
, duringconnect
);
189 /* a precaution, no need to continue if time already is up */
190 failf(data
, "SSL connection timeout");
191 return CURLE_OPERATION_TIMEDOUT
;
194 rc
= Curl_socket_ready(conn
->sock
[sockindex
],
195 conn
->sock
[sockindex
], (int)timeout_ms
);
197 /* reabable or writable, go loop*/
201 failf(data
, "SSL connection timeout");
202 return CURLE_OPERATION_TIMEDOUT
;
205 /* anything that gets here is fatally bad */
206 failf(data
, "select/poll on SSL socket, errno: %d", SOCKERRNO
);
207 return CURLE_SSL_CONNECT_ERROR
;
215 failf(data
, "gnutls_handshake() failed: %s", gnutls_strerror(rc
));
216 return CURLE_SSL_CONNECT_ERROR
;
222 static gnutls_x509_crt_fmt
do_file_type(const char *type
)
224 if(!type
|| !type
[0])
225 return GNUTLS_X509_FMT_PEM
;
226 if(curl_strequal(type
, "PEM"))
227 return GNUTLS_X509_FMT_PEM
;
228 if(curl_strequal(type
, "DER"))
229 return GNUTLS_X509_FMT_DER
;
235 * This function is called after the TCP connect has completed. Setup the TLS
236 * layer and do all necessary magic.
239 Curl_gtls_connect(struct connectdata
*conn
,
243 static const int cert_type_priority
[] = { GNUTLS_CRT_X509
, 0 };
244 struct SessionHandle
*data
= conn
->data
;
245 gnutls_session session
;
247 unsigned int cert_list_size
;
248 const gnutls_datum
*chainp
;
249 unsigned int verify_status
;
250 gnutls_x509_crt x509_cert
,x509_issuer
;
251 gnutls_datum issuerp
;
252 char certbuf
[256]; /* big enough? */
261 struct in6_addr addr
;
269 /* GnuTLS only supports SSLv3 and TLSv1 */
270 if(data
->set
.ssl
.version
== CURL_SSLVERSION_SSLv2
) {
271 failf(data
, "GnuTLS does not support SSLv2");
272 return CURLE_SSL_CONNECT_ERROR
;
275 /* allocate a cred struct */
276 rc
= gnutls_certificate_allocate_credentials(&conn
->ssl
[sockindex
].cred
);
278 failf(data
, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc
));
279 return CURLE_SSL_CONNECT_ERROR
;
282 if(data
->set
.ssl
.CAfile
) {
283 /* set the trusted CA cert bundle file */
284 gnutls_certificate_set_verify_flags(conn
->ssl
[sockindex
].cred
,
285 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
);
287 rc
= gnutls_certificate_set_x509_trust_file(conn
->ssl
[sockindex
].cred
,
288 data
->set
.ssl
.CAfile
,
289 GNUTLS_X509_FMT_PEM
);
291 infof(data
, "error reading ca cert file %s (%s)\n",
292 data
->set
.ssl
.CAfile
, gnutls_strerror(rc
));
293 if(data
->set
.ssl
.verifypeer
)
294 return CURLE_SSL_CACERT_BADFILE
;
297 infof(data
, "found %d certificates in %s\n",
298 rc
, data
->set
.ssl
.CAfile
);
301 if(data
->set
.ssl
.CRLfile
) {
302 /* set the CRL list file */
303 rc
= gnutls_certificate_set_x509_crl_file(conn
->ssl
[sockindex
].cred
,
304 data
->set
.ssl
.CRLfile
,
305 GNUTLS_X509_FMT_PEM
);
307 failf(data
, "error reading crl file %s (%s)\n",
308 data
->set
.ssl
.CRLfile
, gnutls_strerror(rc
));
309 return CURLE_SSL_CRL_BADFILE
;
312 infof(data
, "found %d CRL in %s\n",
313 rc
, data
->set
.ssl
.CRLfile
);
316 /* Initialize TLS session as a client */
317 rc
= gnutls_init(&conn
->ssl
[sockindex
].session
, GNUTLS_CLIENT
);
319 failf(data
, "gnutls_init() failed: %d", rc
);
320 return CURLE_SSL_CONNECT_ERROR
;
323 /* convenient assign */
324 session
= conn
->ssl
[sockindex
].session
;
326 if ((0 == Curl_inet_pton(AF_INET
, conn
->host
.name
, &addr
)) &&
328 (0 == Curl_inet_pton(AF_INET6
, conn
->host
.name
, &addr
)) &&
330 (gnutls_server_name_set(session
, GNUTLS_NAME_DNS
, conn
->host
.name
,
331 strlen(conn
->host
.name
)) < 0))
332 infof(data
, "WARNING: failed to configure server name indication (SNI) "
335 /* Use default priorities */
336 rc
= gnutls_set_default_priority(session
);
338 return CURLE_SSL_CONNECT_ERROR
;
340 if(data
->set
.ssl
.version
== CURL_SSLVERSION_SSLv3
) {
341 int protocol_priority
[] = { GNUTLS_SSL3
, 0 };
342 gnutls_protocol_set_priority(session
, protocol_priority
);
344 return CURLE_SSL_CONNECT_ERROR
;
347 /* Sets the priority on the certificate types supported by gnutls. Priority
348 is higher for types specified before others. After specifying the types
349 you want, you must append a 0. */
350 rc
= gnutls_certificate_type_set_priority(session
, cert_type_priority
);
352 return CURLE_SSL_CONNECT_ERROR
;
354 if(data
->set
.str
[STRING_CERT
]) {
355 if( gnutls_certificate_set_x509_key_file(
356 conn
->ssl
[sockindex
].cred
,
357 data
->set
.str
[STRING_CERT
],
358 data
->set
.str
[STRING_KEY
] ?
359 data
->set
.str
[STRING_KEY
] : data
->set
.str
[STRING_CERT
],
360 do_file_type(data
->set
.str
[STRING_CERT_TYPE
]) ) ) {
361 failf(data
, "error reading X.509 key or certificate file");
362 return CURLE_SSL_CONNECT_ERROR
;
366 /* put the credentials to the current session */
367 rc
= gnutls_credentials_set(session
, GNUTLS_CRD_CERTIFICATE
,
368 conn
->ssl
[sockindex
].cred
);
370 /* set the connection handle (file descriptor for the socket) */
371 gnutls_transport_set_ptr(session
,
372 (gnutls_transport_ptr
)conn
->sock
[sockindex
]);
374 /* register callback functions to send and receive data. */
375 gnutls_transport_set_push_function(session
, Curl_gtls_push
);
376 gnutls_transport_set_pull_function(session
, Curl_gtls_pull
);
378 /* lowat must be set to zero when using custom push and pull functions. */
379 gnutls_transport_set_lowat(session
, 0);
381 /* This might be a reconnect, so we check for a session ID in the cache
382 to speed up things */
384 if(!Curl_ssl_getsessionid(conn
, &ssl_sessionid
, &ssl_idsize
)) {
385 /* we got a session id, use it! */
386 gnutls_session_set_data(session
, ssl_sessionid
, ssl_idsize
);
388 /* Informational message */
389 infof (data
, "SSL re-using session ID\n");
392 rc
= handshake(conn
, session
, sockindex
, TRUE
);
394 /* handshake() sets its own error message with failf() */
397 /* This function will return the peer's raw certificate (chain) as sent by
398 the peer. These certificates are in raw format (DER encoded for
399 X.509). In case of a X.509 then a certificate list may be present. The
400 first certificate in the list is the peer's certificate, following the
401 issuer's certificate, then the issuer's issuer etc. */
403 chainp
= gnutls_certificate_get_peers(session
, &cert_list_size
);
405 if(data
->set
.ssl
.verifypeer
||
406 data
->set
.ssl
.verifyhost
||
407 data
->set
.ssl
.issuercert
) {
408 failf(data
, "failed to get server cert");
409 return CURLE_PEER_FAILED_VERIFICATION
;
411 infof(data
, "\t common name: WARNING couldn't obtain\n");
414 if(data
->set
.ssl
.verifypeer
) {
415 /* This function will try to verify the peer's certificate and return its
416 status (trusted, invalid etc.). The value of status should be one or
417 more of the gnutls_certificate_status_t enumerated elements bitwise
418 or'd. To avoid denial of service attacks some default upper limits
419 regarding the certificate key size and chain size are set. To override
420 them use gnutls_certificate_set_verify_limits(). */
422 rc
= gnutls_certificate_verify_peers2(session
, &verify_status
);
424 failf(data
, "server cert verify failed: %d", rc
);
425 return CURLE_SSL_CONNECT_ERROR
;
428 /* verify_status is a bitmask of gnutls_certificate_status bits */
429 if(verify_status
& GNUTLS_CERT_INVALID
) {
430 if(data
->set
.ssl
.verifypeer
) {
431 failf(data
, "server certificate verification failed. CAfile: %s "
432 "CRLfile: %s", data
->set
.ssl
.CAfile
?data
->set
.ssl
.CAfile
:"none",
433 data
->set
.ssl
.CRLfile
?data
->set
.ssl
.CRLfile
:"none");
434 return CURLE_SSL_CACERT
;
437 infof(data
, "\t server certificate verification FAILED\n");
440 infof(data
, "\t server certificate verification OK\n");
443 infof(data
, "\t server certificate verification SKIPPED\n");
445 /* initialize an X.509 certificate structure. */
446 gnutls_x509_crt_init(&x509_cert
);
448 /* convert the given DER or PEM encoded Certificate to the native
449 gnutls_x509_crt_t format */
450 gnutls_x509_crt_import(x509_cert
, chainp
, GNUTLS_X509_FMT_DER
);
452 if (data
->set
.ssl
.issuercert
) {
453 gnutls_x509_crt_init(&x509_issuer
);
454 issuerp
= load_file(data
->set
.ssl
.issuercert
);
455 gnutls_x509_crt_import(x509_issuer
, &issuerp
, GNUTLS_X509_FMT_PEM
);
456 rc
= gnutls_x509_crt_check_issuer(x509_cert
,x509_issuer
);
457 unload_file(issuerp
);
459 failf(data
, "server certificate issuer check failed (IssuerCert: %s)",
460 data
->set
.ssl
.issuercert
?data
->set
.ssl
.issuercert
:"none");
461 return CURLE_SSL_ISSUER_ERROR
;
463 infof(data
,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
464 data
->set
.ssl
.issuercert
?data
->set
.ssl
.issuercert
:"none");
467 size
=sizeof(certbuf
);
468 rc
= gnutls_x509_crt_get_dn_by_oid(x509_cert
, GNUTLS_OID_X520_COMMON_NAME
,
469 0, /* the first and only one */
474 infof(data
, "error fetching CN from cert:%s\n",
475 gnutls_strerror(rc
));
478 /* This function will check if the given certificate's subject matches the
479 given hostname. This is a basic implementation of the matching described
480 in RFC2818 (HTTPS), which takes into account wildcards, and the subject
481 alternative name PKIX extension. Returns non zero on success, and zero on
483 rc
= gnutls_x509_crt_check_hostname(x509_cert
, conn
->host
.name
);
486 if(data
->set
.ssl
.verifyhost
> 1) {
487 failf(data
, "SSL: certificate subject name (%s) does not match "
488 "target host name '%s'", certbuf
, conn
->host
.dispname
);
489 gnutls_x509_crt_deinit(x509_cert
);
490 return CURLE_PEER_FAILED_VERIFICATION
;
493 infof(data
, "\t common name: %s (does not match '%s')\n",
494 certbuf
, conn
->host
.dispname
);
497 infof(data
, "\t common name: %s (matched)\n", certbuf
);
499 /* Check for time-based validity */
500 certclock
= gnutls_x509_crt_get_expiration_time(x509_cert
);
502 if(certclock
== (time_t)-1) {
503 failf(data
, "server cert expiration date verify failed");
504 return CURLE_SSL_CONNECT_ERROR
;
507 if(certclock
< time(NULL
)) {
508 if(data
->set
.ssl
.verifypeer
) {
509 failf(data
, "server certificate expiration date has passed.");
510 return CURLE_PEER_FAILED_VERIFICATION
;
513 infof(data
, "\t server certificate expiration date FAILED\n");
516 infof(data
, "\t server certificate expiration date OK\n");
518 certclock
= gnutls_x509_crt_get_activation_time(x509_cert
);
520 if(certclock
== (time_t)-1) {
521 failf(data
, "server cert activation date verify failed");
522 return CURLE_SSL_CONNECT_ERROR
;
525 if(certclock
> time(NULL
)) {
526 if(data
->set
.ssl
.verifypeer
) {
527 failf(data
, "server certificate not activated yet.");
528 return CURLE_PEER_FAILED_VERIFICATION
;
531 infof(data
, "\t server certificate activation date FAILED\n");
534 infof(data
, "\t server certificate activation date OK\n");
547 /* public key algorithm's parameters */
548 algo
= gnutls_x509_crt_get_pk_algorithm(x509_cert
, &bits
);
549 infof(data
, "\t certificate public key: %s\n",
550 gnutls_pk_algorithm_get_name(algo
));
552 /* version of the X.509 certificate. */
553 infof(data
, "\t certificate version: #%d\n",
554 gnutls_x509_crt_get_version(x509_cert
));
557 size
= sizeof(certbuf
);
558 gnutls_x509_crt_get_dn(x509_cert
, certbuf
, &size
);
559 infof(data
, "\t subject: %s\n", certbuf
);
561 certclock
= gnutls_x509_crt_get_activation_time(x509_cert
);
562 showtime(data
, "start date", certclock
);
564 certclock
= gnutls_x509_crt_get_expiration_time(x509_cert
);
565 showtime(data
, "expire date", certclock
);
567 size
= sizeof(certbuf
);
568 gnutls_x509_crt_get_issuer_dn(x509_cert
, certbuf
, &size
);
569 infof(data
, "\t issuer: %s\n", certbuf
);
571 gnutls_x509_crt_deinit(x509_cert
);
573 /* compression algorithm (if any) */
574 ptr
= gnutls_compression_get_name(gnutls_compression_get(session
));
575 /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
576 infof(data
, "\t compression: %s\n", ptr
);
578 /* the name of the cipher used. ie 3DES. */
579 ptr
= gnutls_cipher_get_name(gnutls_cipher_get(session
));
580 infof(data
, "\t cipher: %s\n", ptr
);
582 /* the MAC algorithms name. ie SHA1 */
583 ptr
= gnutls_mac_get_name(gnutls_mac_get(session
));
584 infof(data
, "\t MAC: %s\n", ptr
);
586 conn
->ssl
[sockindex
].state
= ssl_connection_complete
;
589 /* this session was not previously in the cache, add it now */
591 /* get the session ID data size */
592 gnutls_session_get_data(session
, NULL
, &ssl_idsize
);
593 ssl_sessionid
= malloc(ssl_idsize
); /* get a buffer for it */
596 /* extract session ID to the allocated buffer */
597 gnutls_session_get_data(session
, ssl_sessionid
, &ssl_idsize
);
599 /* store this session id */
600 return Curl_ssl_addsessionid(conn
, ssl_sessionid
, ssl_idsize
);
608 /* return number of sent (non-SSL) bytes */
609 ssize_t
Curl_gtls_send(struct connectdata
*conn
,
614 ssize_t rc
= gnutls_record_send(conn
->ssl
[sockindex
].session
, mem
, len
);
617 if(rc
== GNUTLS_E_AGAIN
)
618 return 0; /* EWOULDBLOCK equivalent */
619 rc
= -1; /* generic error code for send failure */
625 void Curl_gtls_close_all(struct SessionHandle
*data
)
627 /* FIX: make the OpenSSL code more generic and use parts of it here */
631 static void close_one(struct connectdata
*conn
,
634 if(conn
->ssl
[idx
].session
) {
635 gnutls_bye(conn
->ssl
[idx
].session
, GNUTLS_SHUT_RDWR
);
636 gnutls_deinit(conn
->ssl
[idx
].session
);
637 conn
->ssl
[idx
].session
= NULL
;
639 if(conn
->ssl
[idx
].cred
) {
640 gnutls_certificate_free_credentials(conn
->ssl
[idx
].cred
);
641 conn
->ssl
[idx
].cred
= NULL
;
645 void Curl_gtls_close(struct connectdata
*conn
, int sockindex
)
647 close_one(conn
, sockindex
);
651 * This function is called to shut down the SSL layer but keep the
652 * socket open (CCC - Clear Command Channel)
654 int Curl_gtls_shutdown(struct connectdata
*conn
, int sockindex
)
658 struct SessionHandle
*data
= conn
->data
;
662 /* This has only been tested on the proftpd server, and the mod_tls code
663 sends a close notify alert without waiting for a close notify alert in
664 response. Thus we wait for a close notify alert from the server, but
665 we do not send one. Let's hope other servers do the same... */
667 if(data
->set
.ftp_ccc
== CURLFTPSSL_CCC_ACTIVE
)
668 gnutls_bye(conn
->ssl
[sockindex
].session
, GNUTLS_SHUT_WR
);
670 if(conn
->ssl
[sockindex
].session
) {
672 int what
= Curl_socket_ready(conn
->sock
[sockindex
],
673 CURL_SOCKET_BAD
, SSL_SHUTDOWN_TIMEOUT
);
675 /* Something to read, let's do it and hope that it is the close
676 notify alert from the server */
677 result
= gnutls_record_recv(conn
->ssl
[sockindex
].session
,
681 /* This is the expected response. There was no data but only
682 the close notify alert */
686 case GNUTLS_E_INTERRUPTED
:
687 infof(data
, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
697 failf(data
, "SSL shutdown timeout");
702 /* anything that gets here is fatally bad */
703 failf(data
, "select/poll on SSL socket, errno: %d", SOCKERRNO
);
708 gnutls_deinit(conn
->ssl
[sockindex
].session
);
710 gnutls_certificate_free_credentials(conn
->ssl
[sockindex
].cred
);
712 conn
->ssl
[sockindex
].cred
= NULL
;
713 conn
->ssl
[sockindex
].session
= NULL
;
719 * If the read would block we return -1 and set 'wouldblock' to TRUE.
720 * Otherwise we return the amount of data read. Other errors should return -1
721 * and set 'wouldblock' to FALSE.
723 ssize_t
Curl_gtls_recv(struct connectdata
*conn
, /* connection data */
724 int num
, /* socketindex */
725 char *buf
, /* store read data here */
726 size_t buffersize
, /* max amount to read */
731 ret
= gnutls_record_recv(conn
->ssl
[num
].session
, buf
, buffersize
);
732 if((ret
== GNUTLS_E_AGAIN
) || (ret
== GNUTLS_E_INTERRUPTED
)) {
737 if(ret
== GNUTLS_E_REHANDSHAKE
) {
738 /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
739 proper way" takes a whole lot of work. */
740 CURLcode rc
= handshake(conn
, conn
->ssl
[num
].session
, num
, FALSE
);
742 /* handshake() writes error message on its own */
744 *wouldblock
= TRUE
; /* then return as if this was a wouldblock */
750 failf(conn
->data
, "Peer closed the TLS connection");
755 failf(conn
->data
, "GnuTLS recv error (%d): %s",
756 (int)ret
, gnutls_strerror(ret
));
763 void Curl_gtls_session_free(void *ptr
)
768 size_t Curl_gtls_version(char *buffer
, size_t size
)
770 return snprintf(buffer
, size
, "GnuTLS/%s", gnutls_check_version(NULL
));
773 #endif /* USE_GNUTLS */