diff --git a/CMakeLists.txt b/CMakeLists.txt index 61ecb0e03..b019b0a42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,6 @@ set (PACKAGE_STRING "${PACKAGE} ${VERSION}") message ("-- Looking for GnuTLS") find_package (GnuTLS) if (GNUTLS_FOUND) - message ("-- Found GnuTLS: ${GNUTLS_LIBRARIES}") set (HAVE_LIBGNUTLS true) set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIR}) set (TASK_LIBRARIES ${TASK_LIBRARIES} ${GNUTLS_LIBRARIES}) @@ -79,7 +78,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules") message ("-- Looking for GNU Readline") find_package (Readline) if (READLINE_FOUND) - message ("-- Found GNU Readline: ${READLINE_LIBRARIES}") set (HAVE_READLINE true) set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${READLINE_INCLUDE_DIR}) set (TASK_LIBRARIES ${TASK_LIBRARIES} ${READLINE_LIBRARIES}) diff --git a/ChangeLog b/ChangeLog index bdd572790..32bf8118b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,8 @@ Features + Merged three l10n utility scripts into one tools, scripts/utils/l10n, which will help the translation effort. + The 'due' urgency component now uses seconds, not days, in the calculation. + + The 'debug.tls' configuration variable takes an integer which corresponds to + the GnuTLS log level. For debugging. Bugs + #1196 Now builds on Hurd (thanks to Jakub Wilk). diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f38c7e6e..4e78b7e45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ set (task_SRCS A3.cpp A3.h Task.cpp Task.h Taskmod.cpp Taskmod.h Timer.cpp Timer.h + TLSClient.cpp TLSClient.h Transport.cpp Transport.h TransportCurl.cpp TransportCurl.h TransportRSYNC.cpp TransportRSYNC.h @@ -41,7 +42,6 @@ set (task_SRCS A3.cpp A3.h legacy.cpp recur.cpp rules.cpp - Socket.cpp Socket.h sort.cpp text.cpp text.h utf8.cpp utf8.h diff --git a/src/Config.cpp b/src/Config.cpp index f3e384e7b..08ce5c5ae 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -290,6 +290,7 @@ std::string Config::_defaults = "list.all.tags=no # Include old tag names in 'tags' command\n" "print.empty.columns=no # Print columns which have no data for any task\n" "debug=no # Display diagnostics\n" + "debug.tls=0 # GnuTLS log level\n" "extensions=off # Extension system master switch\n" "fontunderline=yes # Uses underlines rather than -------\n" "shell.prompt=task> # Prompt used by the shell command\n" diff --git a/src/Socket.cpp b/src/TLSClient.cpp similarity index 61% rename from src/Socket.cpp rename to src/TLSClient.cpp index 9b26cbeae..6227f7600 100644 --- a/src/Socket.cpp +++ b/src/TLSClient.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // taskwarrior - a command line task list manager. // -// Copyright 2006-2012, Paul Beckingham, Federico Hernandez. +// Copyright 2006 - 2013, Paul Beckingham, Federico Hernandez. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,45 +25,92 @@ // //////////////////////////////////////////////////////////////////////////////// +#include + +#ifdef HAVE_LIBGNUTLS + #include -#include -#include -#include -#include -#include -#include +#include #include +#include #include +#include #include -#include -#include -#include + +#define MAX_BUF 1024 //////////////////////////////////////////////////////////////////////////////// -Socket::Socket () : - _socket (0), - _limit (0), // Unlimited - _debug (false) +static void gnutls_log_function (int level, const char* message) +{ + std::cout << "c: " << level << " " << message; +} + +//////////////////////////////////////////////////////////////////////////////// +TLSClient::TLSClient () +: _ca ("") +, _socket (0) +, _debug (false) { } //////////////////////////////////////////////////////////////////////////////// -Socket::Socket (int s) : - _socket (s), - _limit (0), // Unlimited - _debug (false) +TLSClient::~TLSClient () { + gnutls_deinit (_session); + gnutls_certificate_free_credentials (_credentials); + gnutls_global_deinit (); + + if (_socket) + { + shutdown (_socket, SHUT_RDWR); + close (_socket); + } } //////////////////////////////////////////////////////////////////////////////// -Socket::~Socket () +void TLSClient::limit (int max) { - close (); + _limit = max; } //////////////////////////////////////////////////////////////////////////////// -// For clients. -void Socket::connect (const std::string& host, const std::string& port) +// Calling this method results in all subsequent socket traffic being sent to +// std::cout, labelled with >>> for outgoing, <<< for incoming. +void TLSClient::debug (int level) +{ + _debug = true; + + gnutls_global_set_log_function (gnutls_log_function); + gnutls_global_set_log_level (level); +} + +//////////////////////////////////////////////////////////////////////////////// +void TLSClient::init (const std::string& ca) +{ + _ca = ca; + + gnutls_global_init (); + gnutls_certificate_allocate_credentials (&_credentials); + gnutls_certificate_set_x509_trust_file (_credentials, _ca.c_str (), GNUTLS_X509_FMT_PEM); + gnutls_init (&_session, GNUTLS_CLIENT); + + // Use default priorities. + const char *err; + int ret = gnutls_priority_set_direct (_session, "NORMAL", &err); + if (ret < 0) + { + if (ret == GNUTLS_E_INVALID_REQUEST) + std::cout << "c: ERROR Priority error at: " << err << "\n"; + + exit (1); + } + + // Apply the x509 credentials to the current session. + gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials); +} + +//////////////////////////////////////////////////////////////////////////////// +void TLSClient::connect (const std::string& host, const std::string& port) { // use IPv4 or IPv6, does not matter. struct addrinfo hints; @@ -95,10 +142,7 @@ void Socket::connect (const std::string& host, const std::string& port) throw "ERROR: " + std::string (::strerror (errno)); if (::connect (_socket, p->ai_addr, p->ai_addrlen) == -1) - { - close (); continue; - } break; } @@ -107,80 +151,31 @@ void Socket::connect (const std::string& host, const std::string& port) if (p == NULL) throw "ERROR: Could not connect to " + host + " " + port; -} -//////////////////////////////////////////////////////////////////////////////// -void Socket::close () -{ - if (_socket) - ::close (_socket); - _socket = 0; -} + gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) _socket); -//////////////////////////////////////////////////////////////////////////////// -// For servers. -void Socket::bind (const std::string& port) -{ - // use IPv4 or IPv6, does not matter. - struct addrinfo hints; - memset (&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; // use my IP + // Perform the TLS handshake + int ret = gnutls_handshake (_session); - struct addrinfo* res; - if (::getaddrinfo (NULL, port.c_str (), &hints, &res) != 0) - throw "ERROR: " + std::string (::gai_strerror (errno)); - - if ((_socket = ::socket (res->ai_family, - res->ai_socktype, - res->ai_protocol)) == -1) - throw "ERROR: Can not bind to port " + port; - - // When a socket is closed, it remains unavailable for a while (netstat -an). - // Setting SO_REUSEADDR allows this program to assume control of a closed, but - // unavailable socket. - int on = 1; - if (::setsockopt (_socket, - SOL_SOCKET, - SO_REUSEADDR, - (const void*) &on, - sizeof (on)) == -1) - throw "ERROR: " + std::string (::strerror (errno)); - - if (::bind (_socket, res->ai_addr, res->ai_addrlen) == -1) - throw "ERROR: " + std::string (::strerror (errno)); -} - -//////////////////////////////////////////////////////////////////////////////// -void Socket::listen (int queue /*= 5*/) -{ - if (::listen (_socket, queue) < 0) - throw "ERROR: " + std::string (::strerror (errno)); -} - -//////////////////////////////////////////////////////////////////////////////// -int Socket::accept () -{ - struct sockaddr_storage client; - socklen_t length = sizeof client; - int connection; - - do + if (ret < 0) { - memset (&client, 0, length); - connection = ::accept (_socket, (struct sockaddr*) &client, &length); + std::cout << "c: ERROR Handshake failed\n"; + gnutls_perror (ret); + } + else + { + std::cout << "c: INFO Handshake was completed\n"; } - while (errno == EINTR); - - if (connection < 0) - throw "ERROR: " + std::string (::strerror (errno)); - - return connection; } //////////////////////////////////////////////////////////////////////////////// -void Socket::write (const std::string& data) +void TLSClient::bye () +{ + gnutls_bye (_session, GNUTLS_SHUT_RDWR); +} + +//////////////////////////////////////////////////////////////////////////////// +void TLSClient::send (const std::string& data) { std::string packet = "XXXX" + data; @@ -199,9 +194,10 @@ void Socket::write (const std::string& data) int status; do { - status = ::send (_socket, packet.c_str () + total, remaining, 0); + status = gnutls_record_send (_session, packet.c_str () + total, remaining); } - while (errno == EINTR); + while (errno == GNUTLS_E_INTERRUPTED || + errno == GNUTLS_E_AGAIN); if (status == -1) break; @@ -211,25 +207,28 @@ void Socket::write (const std::string& data) } if (_debug) - std::cout << ">>> " + std::cout << "c: INFO Sending 'XXXX" << data.c_str () - << " (" << total << " bytes)" + << "' (" << total << " bytes)" << std::endl; } //////////////////////////////////////////////////////////////////////////////// -void Socket::read (std::string& data) +void TLSClient::recv (std::string& data) { data = ""; // No appending of data. int received = 0; // Get the encoded length. - unsigned char header[4]; + unsigned char header[4] = {0}; do { - received = ::recv (_socket, header, sizeof (header), 0); + received = gnutls_record_recv (_session, header, 4); } - while (errno == EINTR); + while (received > 0 && + (errno == GNUTLS_E_INTERRUPTED || + errno == GNUTLS_E_AGAIN)); + int total = received; // Decode the length. @@ -237,11 +236,12 @@ void Socket::read (std::string& data) (header[1]<<16) | (header[2]<<8) | header[3]; + std::cout << "c: INFO expecting " << expected << " bytes.\n"; // TODO This would be a good place to assert 'expected < _limit'. // Arbitrary buffer size. - char buffer[8192]; + char buffer[MAX_BUF]; // Keep reading until no more data. Concatenate chunks of data if a) the // read was interrupted by a signal, and b) if there is more data than @@ -250,17 +250,22 @@ void Socket::read (std::string& data) { do { - received = ::recv (_socket, buffer, sizeof (buffer) - 1, 0); + received = gnutls_record_recv (_session, buffer, MAX_BUF - 1); } - while (errno == EINTR); + while (received > 0 && + (errno == GNUTLS_E_INTERRUPTED || + errno == GNUTLS_E_AGAIN)); // Other end closed the connection. if (received == 0) + { + std::cout << "c: INFO Peer has closed the TLS connection\n"; break; + } // Something happened. if (received < 0) - throw "ERROR: " + std::string (::strerror (errno)); + throw "ERROR: " + std::string (gnutls_strerror (received)); buffer [received] = '\0'; data += buffer; @@ -273,35 +278,11 @@ void Socket::read (std::string& data) while (received > 0 && total < (int) expected); if (_debug) - std::cout << "<<< " + std::cout << "c: INFO Receiving 'XXXX" << data.c_str () - << " (" << total << " bytes)" + << "' (" << total << " bytes)" << std::endl; } //////////////////////////////////////////////////////////////////////////////// -void Socket::limit (int max) -{ - _limit = max; -} - -//////////////////////////////////////////////////////////////////////////////// -// Calling this method results in all subsequent socket traffic being sent to -// std::cout, labelled with >>> for outgoing, <<< for incoming. -void Socket::debug () -{ - _debug = true; -} - -//////////////////////////////////////////////////////////////////////////////// -// get sockaddr, IPv4 or IPv6: -void* Socket::get_in_addr (struct sockaddr* sa) -{ - if (sa->sa_family == AF_INET) - return &(((struct sockaddr_in*) sa)->sin_addr); - - return &(((struct sockaddr_in6*) sa)->sin6_addr); -} - -//////////////////////////////////////////////////////////////////////////////// - +#endif diff --git a/src/Socket.h b/src/TLSClient.h similarity index 71% rename from src/Socket.h rename to src/TLSClient.h index c9f491029..45e9f0cc3 100644 --- a/src/Socket.h +++ b/src/TLSClient.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // taskwarrior - a command line task list manager. // -// Copyright 2006-2013, Paul Beckingham, Federico Hernandez. +// Copyright 2006 - 2013, Paul Beckingham, Federico Hernandez. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -24,49 +24,39 @@ // http://www.opensource.org/licenses/mit-license.php // //////////////////////////////////////////////////////////////////////////////// +#ifndef INCLUDED_TLSCLIENT +#define INCLUDED_TLSCLIENT -#ifndef INCLUDED_SOCKET -#define INCLUDED_SOCKET +#ifdef HAVE_LIBGNUTLS #include -#include -#include -#include -#include -#include -#include +#include -class Socket +class TLSClient { public: - Socket (); - Socket (int); - ~Socket (); - - // Client - void connect (const std::string&, const std::string&); - - // Server - void bind (const std::string&); - void listen (int queue = 5); - int accept (); - void read (std::string&); - void write (const std::string&); - - void close (); - + TLSClient (); + ~TLSClient (); void limit (int); - void debug (); + void debug (int); + void init (const std::string&); + void connect (const std::string&, const std::string&); + void bye (); + + void send (const std::string&); + void recv (std::string&); private: - void* get_in_addr (struct sockaddr*); - -private: - int _socket; - int _limit; - bool _debug; + std::string _ca; + gnutls_certificate_credentials_t _credentials; + gnutls_session_t _session; + int _socket; + int _limit; + bool _debug; }; +#endif #endif //////////////////////////////////////////////////////////////////////////////// + diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index a49a6e577..764e93cb7 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -213,14 +213,6 @@ int CmdDiagnostics::execute (std::string& output) << location.mode () << "\n"; - out << " Server: " - << context.config.get ("taskd.server") - << "\n"; - - out << " Cert: " - << context.config.get ("taskd.certificate") - << "\n"; - out << " Locking: " << (context.config.getBoolean ("locking") ? STRING_CMD_DIAG_ENABLED @@ -236,7 +228,25 @@ int CmdDiagnostics::execute (std::string& output) else if ((peditor = getenv ("EDITOR")) != NULL) out << " $EDITOR: " << peditor << "\n"; - out << "\n"; + out << " Server: " + << context.config.get ("taskd.server") + << "\n"; + + out << " Cert: " + << context.config.get ("taskd.certificate") + << "\n"; + + // Get credentials, but mask out the key. + std::string credentials = context.config.get ("taskd.credentials"); + std::string::size_type last_slash = credentials.rfind ('/'); + if (last_slash != std::string::npos) + credentials = credentials.substr (0, last_slash) + + "/" + + std::string (credentials.length () - last_slash - 1, '*'); + + out << " Creds: " + << credentials + << "\n\n"; // External commands. out << bold.colorize (STRING_CMD_DIAG_EXTERNAL) diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index c8b5ac875..1ca5af0be 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -25,15 +25,12 @@ // //////////////////////////////////////////////////////////////////////////////// +#include #include #include #include #include -#include -#include // TODO Socket is obsolete. -/* #include -*/ #include #include #include @@ -77,6 +74,10 @@ int CmdSync::execute (std::string& output) if (credentials.size () != 3) throw std::string (STRING_CMD_SYNC_BAD_CRED); + std::string certificate = context.config.get ("taskd.certificate"); + if (certificate == "") + throw std::string (STRING_CMD_SYNC_BAD_CERT); + // Read backlog.data. std::string payload = ""; File backlog (context.config.get ("data.location") + "/backlog.data"); @@ -110,7 +111,7 @@ int CmdSync::execute (std::string& output) << "\n"; Msg response; - if (send (connection, request, response)) + if (send (connection, certificate, request, response)) { std::string code = response.get ("code"); if (code == "200") @@ -259,6 +260,7 @@ int CmdSync::execute (std::string& output) //////////////////////////////////////////////////////////////////////////////// bool CmdSync::send ( const std::string& to, + const std::string& certificate, const Msg& request, Msg& response) { @@ -270,23 +272,14 @@ bool CmdSync::send ( std::string server = to.substr (0, colon); std::string port = to.substr (colon + 1); + File cert (certificate); + try { - // TODO Socket is obsolete. - Socket s; - s.connect (server, port); - s.write (request.serialize () + "\n"); - - std::string incoming; - s.read (incoming); - s.close (); - -/* // A very basic TLS client, with X.509 authentication. TLSClient client; - client.debug (); // TODO if (context.config.get ("debug")) - client.limit (1024); // TODO ??? - client.init ("pki/client.cert.pem"); // TODO ??? + client.debug (context.config.getInteger ("debug.tls")); + client.init (cert); client.connect (server, port); client.send (request.serialize () + "\n"); @@ -294,7 +287,6 @@ bool CmdSync::send ( std::string incoming; client.recv (incoming); client.bye (); -*/ response.parse (incoming); return true; diff --git a/src/commands/CmdSync.h b/src/commands/CmdSync.h index 074b68d0d..6bae60f49 100644 --- a/src/commands/CmdSync.h +++ b/src/commands/CmdSync.h @@ -39,7 +39,7 @@ public: int execute (std::string&); private: - bool send (const std::string&, const Msg&, Msg&); + bool send (const std::string&, const std::string&, const Msg&, Msg&); }; #endif diff --git a/src/en-US.h b/src/en-US.h index 91bdb75fd..699211f1d 100644 --- a/src/en-US.h +++ b/src/en-US.h @@ -404,6 +404,7 @@ #define STRING_CMD_SYNC_USAGE "Synchronizes data with the Task Server" #define STRING_CMD_SYNC_NO_SERVER "Task Server is not configured." #define STRING_CMD_SYNC_BAD_CRED "Task Server credentials malformed." +#define STRING_CMD_SYNC_BAD_CERT "Task Server certificate missing." #define STRING_CMD_SYNC_ADD " add {1} '{2}'" #define STRING_CMD_SYNC_MOD "modify {1} '{2}'" #define STRING_CMD_SYNC_PROGRESS "Syncing with {1}" diff --git a/src/es-ES.h b/src/es-ES.h index 049bd1af0..b5390aab6 100644 --- a/src/es-ES.h +++ b/src/es-ES.h @@ -415,6 +415,7 @@ #define STRING_CMD_SYNC_USAGE "Sincroniza datos con el Servidor Task" #define STRING_CMD_SYNC_NO_SERVER "El Servidor Task no está configurado." #define STRING_CMD_SYNC_BAD_CRED "Credenciales del Servidor Task incorrectas." +#define STRING_CMD_SYNC_BAD_CERT "Task Server certificate missing." #define STRING_CMD_SYNC_ADD " añade {1} '{2}'" #define STRING_CMD_SYNC_MOD "modifica {1} '{2}'" #define STRING_CMD_SYNC_PROGRESS "Sincronizando con {1}" diff --git a/src/fr-FR.h b/src/fr-FR.h index 0bf8a7978..fe6fbf351 100644 --- a/src/fr-FR.h +++ b/src/fr-FR.h @@ -404,6 +404,7 @@ #define STRING_CMD_SYNC_USAGE "Synchronizes data with the Task Server" #define STRING_CMD_SYNC_NO_SERVER "Task Server is not configured." #define STRING_CMD_SYNC_BAD_CRED "Task Server credentials malformed." +#define STRING_CMD_SYNC_BAD_CERT "Task Server certificate missing." #define STRING_CMD_SYNC_ADD " add {1} '{2}'" #define STRING_CMD_SYNC_MOD "modify {1} '{2}'" #define STRING_CMD_SYNC_PROGRESS "Syncing with {1}" diff --git a/src/it-IT.h b/src/it-IT.h index a3f96886b..fdfac8dda 100644 --- a/src/it-IT.h +++ b/src/it-IT.h @@ -405,6 +405,7 @@ #define STRING_CMD_SYNC_USAGE "Sincronizza i dati con il Task Server" #define STRING_CMD_SYNC_NO_SERVER "Task Server non configurato." #define STRING_CMD_SYNC_BAD_CRED "Credenziali del Task Server malformate." +#define STRING_CMD_SYNC_BAD_CERT "Task Server certificate missing." #define STRING_CMD_SYNC_ADD " aggiunto {1} '{2}'" #define STRING_CMD_SYNC_MOD "modificato {1} '{2}'" #define STRING_CMD_SYNC_PROGRESS "Sincronizzazione con {1}"