- Implemented sync over TLS.
- Obsoleted the Socket code, replacing it with TLSClient, TLSServer.
- Added task server details to the 'diagnostics' command output.
- 'rc.debug.tls' controls the GnuTLS log level.
- Removed redundant cmake diagnostics.
This commit is contained in:
Paul Beckingham 2013-05-12 01:05:58 -04:00
parent f15c9a1b7e
commit 1e9fb8eebc
13 changed files with 173 additions and 195 deletions

View file

@ -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})

View file

@ -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).

View file

@ -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

View file

@ -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"

View file

@ -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 <cmake.h>
#ifdef HAVE_LIBGNUTLS
#include <iostream>
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <TLSClient.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <Socket.h>
#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

View file

@ -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 <string>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <gnutls/gnutls.h>
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
////////////////////////////////////////////////////////////////////////////////

View file

@ -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)

View file

@ -25,15 +25,12 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <iostream>
#include <sstream>
#include <inttypes.h>
#include <Context.h>
#include <cmake.h>
#include <Socket.h> // TODO Socket is obsolete.
/*
#include <TLSClient.h>
*/
#include <Color.h>
#include <text.h>
#include <i18n.h>
@ -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;

View file

@ -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

View file

@ -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}"

View file

@ -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}"

View file

@ -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}"

View file

@ -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}"