Wednesday, February 7, 2007

gnutls bug in sample code

Below is the source to a sample gnutls client, the tcp_connect() and tcp_close() functions were written by me, the rest is from /usr/share/doc/gnutls-doc/html/gnutls.html. I compile it with gcc -o client client.c -lgnutls:


#include
#include
#include
#include
#include
#include
#include
#include
#include

/* A very basic TLS client, with anonymous authentication.
*/

#define MAX_BUF 1024
#define SA struct sockaddr
#define MSG "GET / HTTP/1.0\r\n\r\n"

#define PORT 5556 /* connect to 5556 port */

int tcp_connect (void)
{
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(PORT);
inet_aton("127.0.0.1", &sa.sin_addr);
int fd = socket(PF_INET, SOCK_STREAM, 0);
if(fd < 0 || connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
{
printf("Can't connect\n");
exit(1);
}
}

void tcp_close (int sd)
{
close(sd);
}

int
main (void)
{
int ret, sd, ii;
gnutls_session_t session;
char buffer[MAX_BUF + 1];
gnutls_anon_client_credentials_t anoncred;
/* Need to enable anonymous KX specifically. */
const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };

gnutls_global_init ();

gnutls_anon_allocate_client_credentials (&anoncred);

/* Initialize TLS session
*/
gnutls_init (&session, GNUTLS_CLIENT);

/* Use default priorities */
gnutls_set_default_priority (session);
gnutls_kx_set_priority (session, kx_prio);

/* put the anonymous credentials to the current session
*/
gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);

/* connect to the peer
*/
sd = tcp_connect ();

gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);

/* Perform the TLS handshake
*/
ret = gnutls_handshake (session);

if (ret < 0)
{
fprintf (stderr, "*** Handshake failed\n");
gnutls_perror (ret);
goto end;
}
else
{
printf ("- Handshake was completed\n");
}

gnutls_record_send (session, MSG, strlen (MSG));

ret = gnutls_record_recv (session, buffer, MAX_BUF);
if (ret == 0)
{
printf ("- Peer has closed the TLS connection\n");
goto end;
}
else if (ret < 0)
{
fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
goto end;
}

printf ("- Received %d bytes: ", ret);
for (ii = 0; ii < ret; ii++)
{
fputc (buffer[ii], stdout);
}
fputs ("\n", stdout);

gnutls_bye (session, GNUTLS_SHUT_RDWR);

end:

tcp_close (sd);

gnutls_deinit (session);

gnutls_anon_free_client_credentials (anoncred);

gnutls_global_deinit ();

return 0;
}


Below is the source to a sample gnutls server, it is entirely taken from /usr/share/doc/gnutls-doc/html/gnutls.html and compiled with gcc server.c -lgnutls -o server:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* This is a sample TLS 1.0 echo server, for anonymous authentication only.
*/


#define SA struct sockaddr
#define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);}
#define MAX_BUF 1024
#define PORT 5556 /* listen to 5556 port */
#define DH_BITS 1024

/* These are global */
gnutls_anon_server_credentials_t anoncred;

gnutls_session_t
initialize_tls_session (void)
{
gnutls_session_t session;
const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };

gnutls_init (&session, GNUTLS_SERVER);

/* avoid calling all the priority functions, since the defaults
* are adequate.
*/
gnutls_set_default_priority (session);
gnutls_kx_set_priority (session, kx_prio);

gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);

gnutls_dh_set_prime_bits (session, DH_BITS);

return session;
}

static gnutls_dh_params_t dh_params;

static int
generate_dh_params (void)
{

/* Generate Diffie Hellman parameters - for use with DHE
* kx algorithms. These should be discarded and regenerated
* once a day, once a week or once a month. Depending on the
* security requirements.
*/
gnutls_dh_params_init (&dh_params);
gnutls_dh_params_generate2 (dh_params, DH_BITS);

return 0;
}

int
main (void)
{
int err, listen_sd, i;
int sd, ret;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
int client_len;
char topbuf[512];
gnutls_session_t session;
char buffer[MAX_BUF + 1];
int optval = 1;

/* this must be called once in the program
*/
gnutls_global_init ();

gnutls_anon_allocate_server_credentials (&anoncred);

generate_dh_params ();

gnutls_anon_set_server_dh_params (anoncred, dh_params);

/* Socket operations
*/
listen_sd = socket (AF_INET, SOCK_STREAM, 0);
SOCKET_ERR (listen_sd, "socket");

memset (&sa_serv, '\0', sizeof (sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (PORT); /* Server Port number */

setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int));

err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv));
SOCKET_ERR (err, "bind");
err = listen (listen_sd, 1024);
SOCKET_ERR (err, "listen");

printf ("Server ready. Listening to port '%d'.\n\n", PORT);

client_len = sizeof (sa_cli);
for (;;)
{
session = initialize_tls_session ();

sd = accept (listen_sd, (SA *) & sa_cli, &client_len);

printf ("- connection from %s, port %d\n",
inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf,
sizeof (topbuf)), ntohs (sa_cli.sin_port));

gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
ret = gnutls_handshake (session);
if (ret < 0)
{
close (sd);
gnutls_deinit (session);
fprintf (stderr, "*** Handshake has failed (%s)\n\n",
gnutls_strerror (ret));
continue;
}
printf ("- Handshake was completed\n");

/* see the Getting peer's information example */
/* print_info(session); */

i = 0;
for (;;)
{
memset (buffer, 0, MAX_BUF + 1);
ret = gnutls_record_recv (session, buffer, MAX_BUF);

if (ret == 0)
{
printf ("\n- Peer has closed the GNUTLS connection\n");
break;
}
else if (ret < 0)
{
fprintf (stderr, "\n*** Received corrupted "
"data(%d). Closing the connection.\n\n", ret);
break;
}
else if (ret > 0)
{
/* echo data back to the client
*/
gnutls_record_send (session, buffer, strlen (buffer));
}
}
printf ("\n");
/* do not wait for the peer to close the connection.
*/
gnutls_bye (session, GNUTLS_SHUT_WR);

close (sd);
gnutls_deinit (session);

}
close (listen_sd);

gnutls_anon_free_server_credentials (anoncred);

gnutls_global_deinit ();

return 0;

}


When I run them on the same machine (127.0.0.1 is hard-coded) the server displays the message *** Handshake has failed (A TLS packet with unexpected length was received.). Any ideas as to where the bug is? I have filed Debian bug report 409984, and when the bug is fixed I'll leave the blog entry as a sample of gnutls code.

Sunday, January 28, 2007

how to run dynamic ssh tunnels

service smtps
{
disable = no
socket_type = stream
wait = no
user = USER
server = /usr/bin/ssh
server_args = USER@HOST -C /home/USER/bin/localsmtp
bind = 127.0.0.1
}

service pop2
{
disable = no
socket_type = stream
wait = no
user = USER
server = /usr/bin/ssh
server_args = USER@HOST -C /home/USER/bin/localpop
bind = 127.0.0.1
}

I have the above in my xinetd configuration to automatically generate ssh tunnels. I configure my MUA to talk to localhost on the pop2 port for POP connections to my real POP server and I configure Postfix (my local MTA) to relay all mail through localhost on the smtps port with the directive "relayhost = 127.0.0.1:465".

The localpop script contains the command "nc 127.0.0.1 110" and the localsmtp script contains the command "nc 127.0.0.1 25". I use the localpop and localsmtp scripts so that if I change anything on the server end then I can easily adjust the scripts without reconfiguring the workstations that relay their mail.

The same configuration can be used whenever you have shell access via ssh to a machine that runs a MTA. It's not difficult and the ssh connections are terminated whenever the MUA or the MTA that initiated them times out and closes it's connection. This means that there is no need to have cron jobs monitoring the ssh tunnels or anything else that is difficult.

Monday, January 15, 2007

dbus error



Strange dbus error from the KDE power monitoring tool