ENH: typo
[cmake.git] / Utilities / cmcurl / gtls.c
blob36d83e9ce8ad2c34d2bbc705558f93e701162865
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
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.
32 #include "setup.h"
33 #ifdef USE_GNUTLS
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
47 #include "urldata.h"
48 #include "sendf.h"
49 #include "gtls.h"
50 #include "sslgen.h"
51 #include "parsedate.h"
52 #include "connect.h" /* for the connect timeout */
53 #include "select.h"
54 #define _MPRINTF_REPLACE /* use our functions only */
55 #include <curl/mprintf.h>
56 #include "memory.h"
57 /* The last #include file should be: */
58 #include "memdebug.h"
60 /* Enable GnuTLS debugging by defining GTLSDEBUG */
61 /*#define GTLSDEBUG */
63 #ifdef GTLSDEBUG
64 static void tls_log_func(int level, const char *str)
66 fprintf(stderr, "|<%d>| %s", level, str);
68 #endif
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)
91 gnutls_global_init();
92 #ifdef GTLSDEBUG
93 gnutls_global_set_log_function(tls_log_func);
94 gnutls_global_set_log_level(2);
95 #endif
96 return 1;
99 int Curl_gtls_cleanup(void)
101 gnutls_global_deinit();
102 return 1;
105 static void showtime(struct SessionHandle *data,
106 const char *text,
107 time_t stamp)
109 struct tm *tm;
110 #ifdef HAVE_GMTIME_R
111 struct tm buffer;
112 tm = (struct tm *)gmtime_r(&stamp, &buffer);
113 #else
114 tm = gmtime(&stamp);
115 #endif
116 snprintf(data->state.buffer,
117 BUFSIZE,
118 "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
119 text,
120 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
121 tm->tm_mday,
122 Curl_month[tm->tm_mon],
123 tm->tm_year + 1900,
124 tm->tm_hour,
125 tm->tm_min,
126 tm->tm_sec);
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,
133 int sockindex,
134 bool duringconnect)
136 struct SessionHandle *data = conn->data;
137 int rc;
139 do {
140 rc = gnutls_handshake(session);
142 if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
143 long timeout_ms = DEFAULT_CONNECT_TIMEOUT;
144 long has_passed;
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;
161 if(timeout_ms < 0) {
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);
169 if(rc > 0)
170 /* reabable or writable, go loop*/
171 continue;
172 else if(0 == rc) {
173 /* timeout */
174 failf(data, "SSL connection timeout");
175 return CURLE_OPERATION_TIMEDOUT;
177 else {
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;
183 else
184 break;
185 } while(1);
187 if (rc < 0) {
188 failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc));
189 return CURLE_SSL_CONNECT_ERROR;
192 return CURLE_OK;
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;
203 return -1;
208 * This function is called after the TCP connect has completed. Setup the TLS
209 * layer and do all necessary magic.
211 CURLcode
212 Curl_gtls_connect(struct connectdata *conn,
213 int sockindex)
216 const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
217 struct SessionHandle *data = conn->data;
218 gnutls_session session;
219 int rc;
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? */
225 size_t size;
226 unsigned int algo;
227 unsigned int bits;
228 time_t clock;
229 const char *ptr;
230 void *ssl_sessionid;
231 size_t ssl_idsize;
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);
241 if(rc < 0) {
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);
254 if(rc < 0) {
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;
260 else
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);
267 if(rc) {
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);
277 if(rc < 0)
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);
284 if(rc < 0)
285 return CURLE_SSL_CONNECT_ERROR;
287 if(data->set.cert) {
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);
324 if(rc)
325 /* handshake() sets its own error message with failf() */
326 return rc;
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);
335 if(!chainp) {
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);
351 if (rc < 0) {
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;
363 else
364 infof(data, "\t server certificate verification FAILED\n");
366 else
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 */
379 FALSE,
380 certbuf,
381 &size);
382 if(rc) {
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
391 failure. */
392 rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
394 if(!rc) {
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;
401 else
402 infof(data, "\t common name: %s (does not match '%s')\n",
403 certbuf, conn->host.dispname);
405 else
406 infof(data, "\t common name: %s (matched)\n", certbuf);
408 /* Show:
410 - ciphers used
411 - subject
412 - start date
413 - expire date
414 - common name
415 - issuer
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);
458 if(!ssl_sessionid) {
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 */
465 if(ssl_sessionid) {
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);
474 return CURLE_OK;
478 /* return number of sent (non-SSL) bytes */
479 ssize_t Curl_gtls_send(struct connectdata *conn,
480 int sockindex,
481 void *mem,
482 size_t len)
484 ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
486 if(rc < 0 ) {
487 if(rc == GNUTLS_E_AGAIN)
488 return 0; /* EWOULDBLOCK equivalent */
489 rc = -1; /* generic error code for send failure */
492 return rc;
495 void Curl_gtls_close_all(struct SessionHandle *data)
497 /* FIX: make the OpenSSL code more generic and use parts of it here */
498 (void)data;
501 static void close_one(struct connectdata *conn,
502 int index)
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)
513 if(conn->ssl[0].use)
514 close_one(conn, 0);
515 if(conn->ssl[1].use)
516 close_one(conn, 1);
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)
525 int result;
526 int retval = 0;
527 struct SessionHandle *data = conn->data;
528 int done = 0;
529 ssize_t nread;
530 char buf[120];
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) {
538 while(!done) {
539 int what = Curl_select(conn->sock[sockindex],
540 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
541 if(what > 0) {
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,
545 buf, sizeof(buf));
546 switch(result) {
547 case 0:
548 /* This is the expected response. There was no data but only
549 the close notify alert */
550 done = 1;
551 break;
552 case GNUTLS_E_AGAIN:
553 case GNUTLS_E_INTERRUPTED:
554 infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
555 break;
556 default:
557 retval = -1;
558 done = 1;
559 break;
562 else if(0 == what) {
563 /* timeout */
564 failf(data, "SSL shutdown timeout");
565 done = 1;
566 break;
568 else {
569 /* anything that gets here is fatally bad */
570 failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
571 retval = -1;
572 done = 1;
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;
582 return retval;
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 */
594 bool *wouldblock)
596 ssize_t ret;
598 ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
599 if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
600 *wouldblock = TRUE;
601 return -1;
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);
608 if(rc)
609 /* handshake() writes error message on its own */
610 return rc;
611 *wouldblock = TRUE; /* then return as if this was a wouldblock */
612 return -1;
615 *wouldblock = FALSE;
616 if (!ret) {
617 failf(conn->data, "Peer closed the TLS connection");
618 return -1;
621 if (ret < 0) {
622 failf(conn->data, "GnuTLS recv error (%d): %s",
623 (int)ret, gnutls_strerror(ret));
624 return -1;
627 return ret;
630 void Curl_gtls_session_free(void *ptr)
632 free(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 */