00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "setup.h"
00033 #ifdef USE_GNUTLS
00034 #include <gnutls/gnutls.h>
00035 #include <gnutls/x509.h>
00036
00037 #include <string.h>
00038 #include <stdlib.h>
00039 #include <ctype.h>
00040 #ifdef HAVE_SYS_SOCKET_H
00041 #include <sys/socket.h>
00042 #endif
00043
00044 #include "urldata.h"
00045 #include "sendf.h"
00046 #include "gtls.h"
00047 #include "sslgen.h"
00048 #include "parsedate.h"
00049 #include "connect.h"
00050 #include "select.h"
00051 #define _MPRINTF_REPLACE
00052 #include <curl/mprintf.h>
00053 #include "memory.h"
00054
00055 #include "memdebug.h"
00056
00057
00058
00059
00060 #ifdef GTLSDEBUG
00061 static void tls_log_func(int level, const char *str)
00062 {
00063 fprintf(stderr, "|<%d>| %s", level, str);
00064 }
00065 #endif
00066 static bool gtls_inited = FALSE;
00067
00068
00069
00070
00071
00072
00073
00074
00075 static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len)
00076 {
00077 return swrite(s, buf, len);
00078 }
00079
00080 static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len)
00081 {
00082 return sread(s, buf, len);
00083 }
00084
00085
00086 int Curl_gtls_init(void)
00087 {
00088
00089
00090
00091
00092
00093
00094 return 1;
00095 }
00096
00097 static int _Curl_gtls_init(void)
00098 {
00099 int ret = 1;
00100 if (!gtls_inited) {
00101 ret = gnutls_global_init()?0:1;
00102 #ifdef GTLSDEBUG
00103 gnutls_global_set_log_function(tls_log_func);
00104 gnutls_global_set_log_level(2);
00105 #endif
00106 gtls_inited = TRUE;
00107 }
00108 return ret;
00109 }
00110
00111 int Curl_gtls_cleanup(void)
00112 {
00113 if (gtls_inited)
00114 gnutls_global_deinit();
00115 return 1;
00116 }
00117
00118 static void showtime(struct SessionHandle *data,
00119 const char *text,
00120 time_t stamp)
00121 {
00122 struct tm *tm;
00123 #ifdef HAVE_GMTIME_R
00124 struct tm buffer;
00125 tm = (struct tm *)gmtime_r(&stamp, &buffer);
00126 #else
00127 tm = gmtime(&stamp);
00128 #endif
00129 snprintf(data->state.buffer,
00130 BUFSIZE,
00131 "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
00132 text,
00133 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
00134 tm->tm_mday,
00135 Curl_month[tm->tm_mon],
00136 tm->tm_year + 1900,
00137 tm->tm_hour,
00138 tm->tm_min,
00139 tm->tm_sec);
00140 infof(data, "%s", data->state.buffer);
00141 }
00142
00143
00144 static CURLcode handshake(struct connectdata *conn,
00145 gnutls_session session,
00146 int sockindex,
00147 bool duringconnect)
00148 {
00149 struct SessionHandle *data = conn->data;
00150 int rc;
00151 if (!gtls_inited)
00152 _Curl_gtls_init();
00153 do {
00154 rc = gnutls_handshake(session);
00155
00156 if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
00157 long timeout_ms = DEFAULT_CONNECT_TIMEOUT;
00158 long has_passed;
00159
00160 if(duringconnect && data->set.connecttimeout)
00161 timeout_ms = data->set.connecttimeout;
00162
00163 if(data->set.timeout) {
00164
00165 if(data->set.timeout < timeout_ms)
00166 timeout_ms = data->set.timeout;
00167 }
00168
00169
00170 has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
00171
00172
00173 timeout_ms -= has_passed;
00174
00175 if(timeout_ms < 0) {
00176
00177 failf(data, "SSL connection timeout");
00178 return CURLE_OPERATION_TIMEOUTED;
00179 }
00180
00181 rc = Curl_socket_ready(conn->sock[sockindex],
00182 conn->sock[sockindex], (int)timeout_ms);
00183 if(rc > 0)
00184
00185 continue;
00186 else if(0 == rc) {
00187
00188 failf(data, "SSL connection timeout");
00189 return CURLE_OPERATION_TIMEDOUT;
00190 }
00191 else {
00192
00193 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
00194 return CURLE_SSL_CONNECT_ERROR;
00195 }
00196 }
00197 else
00198 break;
00199 } while(1);
00200
00201 if (rc < 0) {
00202 failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc));
00203 return CURLE_SSL_CONNECT_ERROR;
00204 }
00205
00206 return CURLE_OK;
00207 }
00208
00209 static gnutls_x509_crt_fmt do_file_type(const char *type)
00210 {
00211 if(!type || !type[0])
00212 return GNUTLS_X509_FMT_PEM;
00213 if(curl_strequal(type, "PEM"))
00214 return GNUTLS_X509_FMT_PEM;
00215 if(curl_strequal(type, "DER"))
00216 return GNUTLS_X509_FMT_DER;
00217 return -1;
00218 }
00219
00220
00221
00222
00223
00224
00225 CURLcode
00226 Curl_gtls_connect(struct connectdata *conn,
00227 int sockindex)
00228
00229 {
00230 const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
00231 struct SessionHandle *data = conn->data;
00232 gnutls_session session;
00233 int rc;
00234 unsigned int cert_list_size;
00235 const gnutls_datum *chainp;
00236 unsigned int verify_status;
00237 gnutls_x509_crt x509_cert;
00238 char certbuf[256];
00239 size_t size;
00240 unsigned int algo;
00241 unsigned int bits;
00242 time_t clock;
00243 const char *ptr;
00244 void *ssl_sessionid;
00245 size_t ssl_idsize;
00246
00247 if (!gtls_inited) _Curl_gtls_init();
00248
00249 if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
00250 failf(data, "GnuTLS does not support SSLv2");
00251 return CURLE_SSL_CONNECT_ERROR;
00252 }
00253
00254
00255 rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
00256 if(rc < 0) {
00257 failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
00258 return CURLE_SSL_CONNECT_ERROR;
00259 }
00260
00261 if(data->set.ssl.CAfile) {
00262
00263 gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
00264 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
00265
00266 rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
00267 data->set.ssl.CAfile,
00268 GNUTLS_X509_FMT_PEM);
00269 if(rc < 0) {
00270 infof(data, "error reading ca cert file %s (%s)\n",
00271 data->set.ssl.CAfile, gnutls_strerror(rc));
00272 if (data->set.ssl.verifypeer)
00273 return CURLE_SSL_CACERT_BADFILE;
00274 }
00275 else
00276 infof(data, "found %d certificates in %s\n",
00277 rc, data->set.ssl.CAfile);
00278 }
00279
00280
00281 rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
00282 if(rc) {
00283 failf(data, "gnutls_init() failed: %d", rc);
00284 return CURLE_SSL_CONNECT_ERROR;
00285 }
00286
00287
00288 session = conn->ssl[sockindex].session;
00289
00290
00291 rc = gnutls_set_default_priority(session);
00292 if(rc < 0)
00293 return CURLE_SSL_CONNECT_ERROR;
00294
00295
00296
00297
00298 rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
00299 if(rc < 0)
00300 return CURLE_SSL_CONNECT_ERROR;
00301
00302 if(data->set.cert) {
00303 if( gnutls_certificate_set_x509_key_file(
00304 conn->ssl[sockindex].cred, data->set.cert,
00305 data->set.key != 0 ? data->set.key : data->set.cert,
00306 do_file_type(data->set.cert_type) ) ) {
00307 failf(data, "error reading X.509 key or certificate file");
00308 return CURLE_SSL_CONNECT_ERROR;
00309 }
00310 }
00311
00312
00313 rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
00314 conn->ssl[sockindex].cred);
00315
00316
00317 gnutls_transport_set_ptr(session,
00318 (gnutls_transport_ptr)conn->sock[sockindex]);
00319
00320
00321 gnutls_transport_set_push_function(session, Curl_gtls_push);
00322 gnutls_transport_set_pull_function(session, Curl_gtls_pull);
00323
00324
00325 gnutls_transport_set_lowat(session, 0);
00326
00327
00328
00329
00330 if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
00331
00332 gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
00333
00334
00335 infof (data, "SSL re-using session ID\n");
00336 }
00337
00338 rc = handshake(conn, session, sockindex, TRUE);
00339 if(rc)
00340
00341 return rc;
00342
00343
00344
00345
00346
00347
00348
00349 chainp = gnutls_certificate_get_peers(session, &cert_list_size);
00350 if(!chainp) {
00351 if(data->set.ssl.verifyhost) {
00352 failf(data, "failed to get server cert");
00353 return CURLE_SSL_PEER_CERTIFICATE;
00354 }
00355 infof(data, "\t common name: WARNING couldn't obtain\n");
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365 rc = gnutls_certificate_verify_peers2(session, &verify_status);
00366 if (rc < 0) {
00367 failf(data, "server cert verify failed: %d", rc);
00368 return CURLE_SSL_CONNECT_ERROR;
00369 }
00370
00371
00372 if(verify_status & GNUTLS_CERT_INVALID) {
00373 if (data->set.ssl.verifypeer) {
00374 failf(data, "server certificate verification failed. CAfile: %s",
00375 data->set.ssl.CAfile?data->set.ssl.CAfile:"none");
00376 return CURLE_SSL_CACERT;
00377 }
00378 else
00379 infof(data, "\t server certificate verification FAILED\n");
00380 }
00381 else
00382 infof(data, "\t server certificate verification OK\n");
00383
00384
00385 gnutls_x509_crt_init(&x509_cert);
00386
00387
00388
00389 gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
00390
00391 size=sizeof(certbuf);
00392 rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
00393 0,
00394 FALSE,
00395 certbuf,
00396 &size);
00397 if(rc) {
00398 infof(data, "error fetching CN from cert:%s\n",
00399 gnutls_strerror(rc));
00400 }
00401
00402
00403
00404
00405
00406
00407 rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
00408
00409 if(!rc) {
00410 if (data->set.ssl.verifyhost > 1) {
00411 failf(data, "SSL: certificate subject name (%s) does not match "
00412 "target host name '%s'", certbuf, conn->host.dispname);
00413 gnutls_x509_crt_deinit(x509_cert);
00414 return CURLE_SSL_PEER_CERTIFICATE;
00415 }
00416 else
00417 infof(data, "\t common name: %s (does not match '%s')\n",
00418 certbuf, conn->host.dispname);
00419 }
00420 else
00421 infof(data, "\t common name: %s (matched)\n", certbuf);
00422
00423
00424 clock = gnutls_x509_crt_get_expiration_time(x509_cert);
00425
00426 if(clock == (time_t)-1) {
00427 failf(data, "server cert expiration date verify failed");
00428 return CURLE_SSL_CONNECT_ERROR;
00429 }
00430
00431 if(clock < time(NULL)) {
00432 if (data->set.ssl.verifypeer) {
00433 failf(data, "server certificate expiration date has passed.");
00434 return CURLE_SSL_PEER_CERTIFICATE;
00435 }
00436 else
00437 infof(data, "\t server certificate expiration date FAILED\n");
00438 }
00439 else
00440 infof(data, "\t server certificate expiration date OK\n");
00441
00442 clock = gnutls_x509_crt_get_activation_time(x509_cert);
00443
00444 if(clock == (time_t)-1) {
00445 failf(data, "server cert activation date verify failed");
00446 return CURLE_SSL_CONNECT_ERROR;
00447 }
00448
00449 if(clock > time(NULL)) {
00450 if (data->set.ssl.verifypeer) {
00451 failf(data, "server certificate not activated yet.");
00452 return CURLE_SSL_PEER_CERTIFICATE;
00453 }
00454 else
00455 infof(data, "\t server certificate activation date FAILED\n");
00456 }
00457 else
00458 infof(data, "\t server certificate activation date OK\n");
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472 algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
00473 infof(data, "\t certificate public key: %s\n",
00474 gnutls_pk_algorithm_get_name(algo));
00475
00476
00477 infof(data, "\t certificate version: #%d\n",
00478 gnutls_x509_crt_get_version(x509_cert));
00479
00480
00481 size = sizeof(certbuf);
00482 gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
00483 infof(data, "\t subject: %s\n", certbuf);
00484
00485 clock = gnutls_x509_crt_get_activation_time(x509_cert);
00486 showtime(data, "start date", clock);
00487
00488 clock = gnutls_x509_crt_get_expiration_time(x509_cert);
00489 showtime(data, "expire date", clock);
00490
00491 size = sizeof(certbuf);
00492 gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
00493 infof(data, "\t issuer: %s\n", certbuf);
00494
00495 gnutls_x509_crt_deinit(x509_cert);
00496
00497
00498 ptr = gnutls_compression_get_name(gnutls_compression_get(session));
00499
00500 infof(data, "\t compression: %s\n", ptr);
00501
00502
00503 ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
00504 infof(data, "\t cipher: %s\n", ptr);
00505
00506
00507 ptr = gnutls_mac_get_name(gnutls_mac_get(session));
00508 infof(data, "\t MAC: %s\n", ptr);
00509
00510 if(!ssl_sessionid) {
00511
00512
00513
00514 gnutls_session_get_data(session, NULL, &ssl_idsize);
00515 ssl_sessionid = malloc(ssl_idsize);
00516
00517 if(ssl_sessionid) {
00518
00519 gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize);
00520
00521
00522 return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize);
00523 }
00524 }
00525
00526 return CURLE_OK;
00527 }
00528
00529
00530
00531 ssize_t Curl_gtls_send(struct connectdata *conn,
00532 int sockindex,
00533 void *mem,
00534 size_t len)
00535 {
00536 ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
00537
00538 if(rc < 0 ) {
00539 if(rc == GNUTLS_E_AGAIN)
00540 return 0;
00541 rc = -1;
00542 }
00543
00544 return rc;
00545 }
00546
00547 void Curl_gtls_close_all(struct SessionHandle *data)
00548 {
00549
00550 (void)data;
00551 }
00552
00553 static void close_one(struct connectdata *conn,
00554 int index)
00555 {
00556 if(conn->ssl[index].session) {
00557 gnutls_bye(conn->ssl[index].session, GNUTLS_SHUT_RDWR);
00558 gnutls_deinit(conn->ssl[index].session);
00559 }
00560 if(conn->ssl[index].cred)
00561 gnutls_certificate_free_credentials(conn->ssl[index].cred);
00562 }
00563
00564 void Curl_gtls_close(struct connectdata *conn)
00565 {
00566 if(conn->ssl[0].use)
00567 close_one(conn, 0);
00568 if(conn->ssl[1].use)
00569 close_one(conn, 1);
00570 }
00571
00572
00573
00574
00575
00576 int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
00577 {
00578 int result;
00579 int retval = 0;
00580 struct SessionHandle *data = conn->data;
00581 int done = 0;
00582 char buf[120];
00583
00584
00585
00586
00587
00588
00589 if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
00590 gnutls_bye(conn->ssl[sockindex].session, GNUTLS_SHUT_WR);
00591
00592 if(conn->ssl[sockindex].session) {
00593 while(!done) {
00594 int what = Curl_socket_ready(conn->sock[sockindex],
00595 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
00596 if(what > 0) {
00597
00598
00599 result = gnutls_record_recv(conn->ssl[sockindex].session,
00600 buf, sizeof(buf));
00601 switch(result) {
00602 case 0:
00603
00604
00605 done = 1;
00606 break;
00607 case GNUTLS_E_AGAIN:
00608 case GNUTLS_E_INTERRUPTED:
00609 infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
00610 break;
00611 default:
00612 retval = -1;
00613 done = 1;
00614 break;
00615 }
00616 }
00617 else if(0 == what) {
00618
00619 failf(data, "SSL shutdown timeout");
00620 done = 1;
00621 break;
00622 }
00623 else {
00624
00625 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
00626 retval = -1;
00627 done = 1;
00628 }
00629 }
00630 gnutls_deinit(conn->ssl[sockindex].session);
00631 }
00632 gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
00633
00634 conn->ssl[sockindex].session = NULL;
00635 conn->ssl[sockindex].use = FALSE;
00636
00637 return retval;
00638 }
00639
00640
00641
00642
00643
00644
00645 ssize_t Curl_gtls_recv(struct connectdata *conn,
00646 int num,
00647 char *buf,
00648 size_t buffersize,
00649 bool *wouldblock)
00650 {
00651 ssize_t ret;
00652
00653 ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
00654 if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
00655 *wouldblock = TRUE;
00656 return -1;
00657 }
00658
00659 if(ret == GNUTLS_E_REHANDSHAKE) {
00660
00661
00662 CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE);
00663 if(rc)
00664
00665 return rc;
00666 *wouldblock = TRUE;
00667 return -1;
00668 }
00669
00670 *wouldblock = FALSE;
00671 if (!ret) {
00672 failf(conn->data, "Peer closed the TLS connection");
00673 return -1;
00674 }
00675
00676 if (ret < 0) {
00677 failf(conn->data, "GnuTLS recv error (%d): %s",
00678 (int)ret, gnutls_strerror(ret));
00679 return -1;
00680 }
00681
00682 return ret;
00683 }
00684
00685 void Curl_gtls_session_free(void *ptr)
00686 {
00687 free(ptr);
00688 }
00689
00690 size_t Curl_gtls_version(char *buffer, size_t size)
00691 {
00692 return snprintf(buffer, size, " GnuTLS/%s", gnutls_check_version(NULL));
00693 }
00694
00695 #endif