1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.2 2007-03-15 19:22:13 andy 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_TYPES_H
41 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
51 #include "parsedate.h"
52 #include "connect.h" /* for the connect timeout */
54 #define _MPRINTF_REPLACE /* use our functions only */
55 #include <curl/mprintf.h>
57 /* The last #include file should be: */
60 /* Enable GnuTLS debugging by defining GTLSDEBUG */
61 /*#define GTLSDEBUG */
64 static void tls_log_func(int level
, const char *str
)
66 fprintf(stderr
, "|<%d>| %s", level
, str
);
71 * Custom push and pull callback functions used by GNU TLS to read and write
72 * to the socket. These functions are simple wrappers to send() and recv()
73 * (although here using the sread/swrite macros as defined by setup_once.h).
74 * We use custom functions rather than the GNU TLS defaults because it allows
75 * us to get specific about the fourth "flags" argument, and to use arbitrary
76 * private data with gnutls_transport_set_ptr if we wish.
78 static ssize_t
Curl_gtls_push(void *s
, const void *buf
, size_t len
)
80 return swrite(s
, buf
, len
);
83 static ssize_t
Curl_gtls_pull(void *s
, void *buf
, size_t len
)
85 return sread(s
, buf
, len
);
88 /* Global GnuTLS init, called from Curl_ssl_init() */
89 int Curl_gtls_init(void)
93 gnutls_global_set_log_function(tls_log_func
);
94 gnutls_global_set_log_level(2);
99 int Curl_gtls_cleanup(void)
101 gnutls_global_deinit();
105 static void showtime(struct SessionHandle
*data
,
112 tm
= (struct tm
*)gmtime_r(&stamp
, &buffer
);
116 snprintf(data
->state
.buffer
,
118 "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
120 Curl_wkday
[tm
->tm_wday
?tm
->tm_wday
-1:6],
122 Curl_month
[tm
->tm_mon
],
127 infof(data
, "%s", data
->state
.buffer
);
130 /* this function does a BLOCKING SSL/TLS (re-)handshake */
131 static CURLcode
handshake(struct connectdata
*conn
,
132 gnutls_session session
,
136 struct SessionHandle
*data
= conn
->data
;
140 rc
= gnutls_handshake(session
);
142 if((rc
== GNUTLS_E_AGAIN
) || (rc
== GNUTLS_E_INTERRUPTED
)) {
143 long timeout_ms
= DEFAULT_CONNECT_TIMEOUT
;
146 if(duringconnect
&& data
->set
.connecttimeout
)
147 timeout_ms
= data
->set
.connecttimeout
*1000;
149 if(data
->set
.timeout
) {
150 /* get the strictest timeout of the ones converted to milliseconds */
151 if((data
->set
.timeout
*1000) < timeout_ms
)
152 timeout_ms
= data
->set
.timeout
*1000;
155 /* Evaluate in milliseconds how much time that has passed */
156 has_passed
= Curl_tvdiff(Curl_tvnow(), data
->progress
.t_startsingle
);
158 /* subtract the passed time */
159 timeout_ms
-= has_passed
;
162 /* a precaution, no need to continue if time already is up */
163 failf(data
, "SSL connection timeout");
164 return CURLE_OPERATION_TIMEOUTED
;
167 rc
= Curl_select(conn
->sock
[sockindex
],
168 conn
->sock
[sockindex
], (int)timeout_ms
);
170 /* reabable or writable, go loop*/
174 failf(data
, "SSL connection timeout");
175 return CURLE_OPERATION_TIMEDOUT
;
178 /* anything that gets here is fatally bad */
179 failf(data
, "select on SSL socket, errno: %d", Curl_sockerrno());
180 return CURLE_SSL_CONNECT_ERROR
;
188 failf(data
, "gnutls_handshake() failed: %s", gnutls_strerror(rc
));
189 return CURLE_SSL_CONNECT_ERROR
;
195 static gnutls_x509_crt_fmt
do_file_type(const char *type
)
197 if(!type
|| !type
[0])
198 return GNUTLS_X509_FMT_PEM
;
199 if(curl_strequal(type
, "PEM"))
200 return GNUTLS_X509_FMT_PEM
;
201 if(curl_strequal(type
, "DER"))
202 return GNUTLS_X509_FMT_DER
;
208 * This function is called after the TCP connect has completed. Setup the TLS
209 * layer and do all necessary magic.
212 Curl_gtls_connect(struct connectdata
*conn
,
216 const int cert_type_priority
[] = { GNUTLS_CRT_X509
, 0 };
217 struct SessionHandle
*data
= conn
->data
;
218 gnutls_session session
;
220 unsigned int cert_list_size
;
221 const gnutls_datum
*chainp
;
222 unsigned int verify_status
;
223 gnutls_x509_crt x509_cert
;
224 char certbuf
[256]; /* big enough? */
233 /* GnuTLS only supports TLSv1 (and SSLv3?) */
234 if(data
->set
.ssl
.version
== CURL_SSLVERSION_SSLv2
) {
235 failf(data
, "GnuTLS does not support SSLv2");
236 return CURLE_SSL_CONNECT_ERROR
;
239 /* allocate a cred struct */
240 rc
= gnutls_certificate_allocate_credentials(&conn
->ssl
[sockindex
].cred
);
242 failf(data
, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc
));
243 return CURLE_SSL_CONNECT_ERROR
;
246 if(data
->set
.ssl
.CAfile
) {
247 /* set the trusted CA cert bundle file */
248 gnutls_certificate_set_verify_flags(conn
->ssl
[sockindex
].cred
,
249 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
);
251 rc
= gnutls_certificate_set_x509_trust_file(conn
->ssl
[sockindex
].cred
,
252 data
->set
.ssl
.CAfile
,
253 GNUTLS_X509_FMT_PEM
);
255 infof(data
, "error reading ca cert file %s (%s)\n",
256 data
->set
.ssl
.CAfile
, gnutls_strerror(rc
));
257 if (data
->set
.ssl
.verifypeer
)
258 return CURLE_SSL_CACERT_BADFILE
;
261 infof(data
, "found %d certificates in %s\n",
262 rc
, data
->set
.ssl
.CAfile
);
265 /* Initialize TLS session as a client */
266 rc
= gnutls_init(&conn
->ssl
[sockindex
].session
, GNUTLS_CLIENT
);
268 failf(data
, "gnutls_init() failed: %d", rc
);
269 return CURLE_SSL_CONNECT_ERROR
;
272 /* convenient assign */
273 session
= conn
->ssl
[sockindex
].session
;
275 /* Use default priorities */
276 rc
= gnutls_set_default_priority(session
);
278 return CURLE_SSL_CONNECT_ERROR
;
280 /* Sets the priority on the certificate types supported by gnutls. Priority
281 is higher for types specified before others. After specifying the types
282 you want, you must append a 0. */
283 rc
= gnutls_certificate_type_set_priority(session
, cert_type_priority
);
285 return CURLE_SSL_CONNECT_ERROR
;
288 if( gnutls_certificate_set_x509_key_file(
289 conn
->ssl
[sockindex
].cred
, data
->set
.cert
,
290 data
->set
.key
!= 0 ? data
->set
.key
: data
->set
.cert
,
291 do_file_type(data
->set
.cert_type
) ) ) {
292 failf(data
, "error reading X.509 key or certificate file");
293 return CURLE_SSL_CONNECT_ERROR
;
297 /* put the credentials to the current session */
298 rc
= gnutls_credentials_set(session
, GNUTLS_CRD_CERTIFICATE
,
299 conn
->ssl
[sockindex
].cred
);
301 /* set the connection handle (file descriptor for the socket) */
302 gnutls_transport_set_ptr(session
,
303 (gnutls_transport_ptr
)conn
->sock
[sockindex
]);
305 /* register callback functions to send and receive data. */
306 gnutls_transport_set_push_function(session
, Curl_gtls_push
);
307 gnutls_transport_set_pull_function(session
, Curl_gtls_pull
);
309 /* lowat must be set to zero when using custom push and pull functions. */
310 gnutls_transport_set_lowat(session
, 0);
312 /* This might be a reconnect, so we check for a session ID in the cache
313 to speed up things */
315 if(!Curl_ssl_getsessionid(conn
, &ssl_sessionid
, &ssl_idsize
)) {
316 /* we got a session id, use it! */
317 gnutls_session_set_data(session
, ssl_sessionid
, ssl_idsize
);
319 /* Informational message */
320 infof (data
, "SSL re-using session ID\n");
323 rc
= handshake(conn
, session
, sockindex
, TRUE
);
325 /* handshake() sets its own error message with failf() */
328 /* This function will return the peer's raw certificate (chain) as sent by
329 the peer. These certificates are in raw format (DER encoded for
330 X.509). In case of a X.509 then a certificate list may be present. The
331 first certificate in the list is the peer's certificate, following the
332 issuer's certificate, then the issuer's issuer etc. */
334 chainp
= gnutls_certificate_get_peers(session
, &cert_list_size
);
336 if(data
->set
.ssl
.verifyhost
) {
337 failf(data
, "failed to get server cert");
338 return CURLE_SSL_PEER_CERTIFICATE
;
340 infof(data
, "\t common name: WARNING couldn't obtain\n");
343 /* This function will try to verify the peer's certificate and return its
344 status (trusted, invalid etc.). The value of status should be one or more
345 of the gnutls_certificate_status_t enumerated elements bitwise or'd. To
346 avoid denial of service attacks some default upper limits regarding the
347 certificate key size and chain size are set. To override them use
348 gnutls_certificate_set_verify_limits(). */
350 rc
= gnutls_certificate_verify_peers2(session
, &verify_status
);
352 failf(data
, "server cert verify failed: %d", rc
);
353 return CURLE_SSL_CONNECT_ERROR
;
356 /* verify_status is a bitmask of gnutls_certificate_status bits */
357 if(verify_status
& GNUTLS_CERT_INVALID
) {
358 if (data
->set
.ssl
.verifypeer
) {
359 failf(data
, "server certificate verification failed. CAfile: %s",
360 data
->set
.ssl
.CAfile
?data
->set
.ssl
.CAfile
:"none");
361 return CURLE_SSL_CACERT
;
364 infof(data
, "\t server certificate verification FAILED\n");
367 infof(data
, "\t server certificate verification OK\n");
369 /* initialize an X.509 certificate structure. */
370 gnutls_x509_crt_init(&x509_cert
);
372 /* convert the given DER or PEM encoded Certificate to the native
373 gnutls_x509_crt_t format */
374 gnutls_x509_crt_import(x509_cert
, chainp
, GNUTLS_X509_FMT_DER
);
376 size
=sizeof(certbuf
);
377 rc
= gnutls_x509_crt_get_dn_by_oid(x509_cert
, GNUTLS_OID_X520_COMMON_NAME
,
378 0, /* the first and only one */
383 infof(data
, "error fetching CN from cert:%s\n",
384 gnutls_strerror(rc
));
387 /* This function will check if the given certificate's subject matches the
388 given hostname. This is a basic implementation of the matching described
389 in RFC2818 (HTTPS), which takes into account wildcards, and the subject
390 alternative name PKIX extension. Returns non zero on success, and zero on
392 rc
= gnutls_x509_crt_check_hostname(x509_cert
, conn
->host
.name
);
395 if (data
->set
.ssl
.verifyhost
> 1) {
396 failf(data
, "SSL: certificate subject name (%s) does not match "
397 "target host name '%s'", certbuf
, conn
->host
.dispname
);
398 gnutls_x509_crt_deinit(x509_cert
);
399 return CURLE_SSL_PEER_CERTIFICATE
;
402 infof(data
, "\t common name: %s (does not match '%s')\n",
403 certbuf
, conn
->host
.dispname
);
406 infof(data
, "\t common name: %s (matched)\n", certbuf
);
419 /* public key algorithm's parameters */
420 algo
= gnutls_x509_crt_get_pk_algorithm(x509_cert
, &bits
);
421 infof(data
, "\t certificate public key: %s\n",
422 gnutls_pk_algorithm_get_name(algo
));
424 /* version of the X.509 certificate. */
425 infof(data
, "\t certificate version: #%d\n",
426 gnutls_x509_crt_get_version(x509_cert
));
429 size
= sizeof(certbuf
);
430 gnutls_x509_crt_get_dn(x509_cert
, certbuf
, &size
);
431 infof(data
, "\t subject: %s\n", certbuf
);
433 clock
= gnutls_x509_crt_get_activation_time(x509_cert
);
434 showtime(data
, "start date", clock
);
436 clock
= gnutls_x509_crt_get_expiration_time(x509_cert
);
437 showtime(data
, "expire date", clock
);
439 size
= sizeof(certbuf
);
440 gnutls_x509_crt_get_issuer_dn(x509_cert
, certbuf
, &size
);
441 infof(data
, "\t issuer: %s\n", certbuf
);
443 gnutls_x509_crt_deinit(x509_cert
);
445 /* compression algorithm (if any) */
446 ptr
= gnutls_compression_get_name(gnutls_compression_get(session
));
447 /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
448 infof(data
, "\t compression: %s\n", ptr
);
450 /* the name of the cipher used. ie 3DES. */
451 ptr
= gnutls_cipher_get_name(gnutls_cipher_get(session
));
452 infof(data
, "\t cipher: %s\n", ptr
);
454 /* the MAC algorithms name. ie SHA1 */
455 ptr
= gnutls_mac_get_name(gnutls_mac_get(session
));
456 infof(data
, "\t MAC: %s\n", ptr
);
459 /* this session was not previously in the cache, add it now */
461 /* get the session ID data size */
462 gnutls_session_get_data(session
, NULL
, &ssl_idsize
);
463 ssl_sessionid
= malloc(ssl_idsize
); /* get a buffer for it */
466 /* extract session ID to the allocated buffer */
467 gnutls_session_get_data(session
, ssl_sessionid
, &ssl_idsize
);
469 /* store this session id */
470 return Curl_ssl_addsessionid(conn
, ssl_sessionid
, ssl_idsize
);
478 /* return number of sent (non-SSL) bytes */
479 ssize_t
Curl_gtls_send(struct connectdata
*conn
,
484 ssize_t rc
= gnutls_record_send(conn
->ssl
[sockindex
].session
, mem
, len
);
487 if(rc
== GNUTLS_E_AGAIN
)
488 return 0; /* EWOULDBLOCK equivalent */
489 rc
= -1; /* generic error code for send failure */
495 void Curl_gtls_close_all(struct SessionHandle
*data
)
497 /* FIX: make the OpenSSL code more generic and use parts of it here */
501 static void close_one(struct connectdata
*conn
,
504 if(conn
->ssl
[index
].session
) {
505 gnutls_bye(conn
->ssl
[index
].session
, GNUTLS_SHUT_RDWR
);
506 gnutls_deinit(conn
->ssl
[index
].session
);
508 gnutls_certificate_free_credentials(conn
->ssl
[index
].cred
);
511 void Curl_gtls_close(struct connectdata
*conn
)
520 * This function is called to shut down the SSL layer but keep the
521 * socket open (CCC - Clear Command Channel)
523 int Curl_gtls_shutdown(struct connectdata
*conn
, int sockindex
)
527 struct SessionHandle
*data
= conn
->data
;
532 /* This has only been tested on the proftpd server, and the mod_tls code
533 sends a close notify alert without waiting for a close notify alert in
534 response. Thus we wait for a close notify alert from the server, but
535 we do not send one. Let's hope other servers do the same... */
537 if(conn
->ssl
[sockindex
].session
) {
539 int what
= Curl_select(conn
->sock
[sockindex
],
540 CURL_SOCKET_BAD
, SSL_SHUTDOWN_TIMEOUT
);
542 /* Something to read, let's do it and hope that it is the close
543 notify alert from the server */
544 result
= gnutls_record_recv(conn
->ssl
[sockindex
].session
,
548 /* This is the expected response. There was no data but only
549 the close notify alert */
553 case GNUTLS_E_INTERRUPTED
:
554 infof(data
, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
564 failf(data
, "SSL shutdown timeout");
569 /* anything that gets here is fatally bad */
570 failf(data
, "select on SSL socket, errno: %d", Curl_sockerrno());
575 gnutls_deinit(conn
->ssl
[sockindex
].session
);
577 gnutls_certificate_free_credentials(conn
->ssl
[sockindex
].cred
);
579 conn
->ssl
[sockindex
].session
= NULL
;
580 conn
->ssl
[sockindex
].use
= FALSE
;
586 * If the read would block we return -1 and set 'wouldblock' to TRUE.
587 * Otherwise we return the amount of data read. Other errors should return -1
588 * and set 'wouldblock' to FALSE.
590 ssize_t
Curl_gtls_recv(struct connectdata
*conn
, /* connection data */
591 int num
, /* socketindex */
592 char *buf
, /* store read data here */
593 size_t buffersize
, /* max amount to read */
598 ret
= gnutls_record_recv(conn
->ssl
[num
].session
, buf
, buffersize
);
599 if((ret
== GNUTLS_E_AGAIN
) || (ret
== GNUTLS_E_INTERRUPTED
)) {
604 if(ret
== GNUTLS_E_REHANDSHAKE
) {
605 /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
606 proper way" takes a whole lot of work. */
607 CURLcode rc
= handshake(conn
, conn
->ssl
[num
].session
, num
, FALSE
);
609 /* handshake() writes error message on its own */
611 *wouldblock
= TRUE
; /* then return as if this was a wouldblock */
617 failf(conn
->data
, "Peer closed the TLS connection");
622 failf(conn
->data
, "GnuTLS recv error (%d): %s",
623 (int)ret
, gnutls_strerror(ret
));
630 void Curl_gtls_session_free(void *ptr
)
635 size_t Curl_gtls_version(char *buffer
, size_t size
)
637 return snprintf(buffer
, size
, " GnuTLS/%s", gnutls_check_version(NULL
));
640 #endif /* USE_GNUTLS */