[gnutls-dev] GNUTLS support for mutt
Marco d'Itri
md@Linux.IT
Sat Nov 3 21:29:02 2001
--jRHKVT23PllUwdXP
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
I wrote some nearly (?) working code to use mutt with the GNUTLS library
(http://gnutls.hellug.gr).
I tried it against a stunell'ed POP3 server and I can reliably connect
and download the message header and one message, but after I do this
all ssl_socket_read fails with MAC_FAILED, then mutt drops the
connection and the new ones fails too with the same error.
I see a similar error on the other side too.
Any clues from the gnutls people? I'm not sure if it's a bug in my code
or in gnutls. I'm using 0.2.9.1.
If you want to test it just unpack mutt, run configure --with-nss, copy
the file in the directory and fix the Makefile to use GNUTLS instead of
libnss.
--
ciao,
Marco
--jRHKVT23PllUwdXP
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="mutt_ssl_nss.c"
/*
* compile with -I/home/src/tls/gnutls-0.2.9/lib
* link with -lz -lgdbm -L/home/src/tls/gnutls-0.2.9/lib/.libs -lgnutls -lgcrypt
*/
/* Copyright (C) 2001 Marco d'Itri <md@linux.it>
* vim: set shiftwidth=2 tabstop=8
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/
#include <gnutls.h>
#undef LIST
#include "mutt.h"
#include "mutt_socket.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
#include "mutt_ssl.h"
char *SslCertFile = NULL;
char *SslEntropyFile = NULL;
typedef struct _sslsockdata
{
GNUTLS_STATE state;
X509PKI_CLIENT_CREDENTIALS xcred;
}
sslsockdata;
/* local prototypes */
static int ssl_init (void);
static int ssl_negotiate (CONNECTION*);
static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len);
static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len);
static int ssl_socket_open (CONNECTION * conn);
static int ssl_socket_close (CONNECTION * conn);
static int cert_callback (gnutls_DN *client_cert, gnutls_DN *issuer_cert,
int ncerts, gnutls_DN* req_ca_cert, int nreqs);
static int mutt_ssl_error (char* msg)
{
mutt_error ("%s", msg);
mutt_sleep (2);
return -1;
}
static int ssl_init (void)
{
static unsigned char init_complete = 0;
if (init_complete)
return 0;
if (gnutls_global_init () < 0)
return mutt_ssl_error ("gnutls_global_init");
init_complete = 1;
return 0;
}
int mutt_nss_socket_setup (CONNECTION * conn)
{
if (ssl_init() < 0)
return -1;
conn->open = ssl_socket_open;
conn->read = ssl_socket_read;
conn->write = ssl_socket_write;
conn->close = ssl_socket_close;
return 0;
}
static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len)
{
sslsockdata *data = conn->sockdata;
int ret;
ret = gnutls_read (conn->fd, data->state, buf, len);
if (gnutls_is_fatal_error(ret) == 1)
{
mutt_error ("ssl_socket_read (%s)", gnutls_strerror (ret));
mutt_sleep (4);
return -1;
}
return ret;
}
static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len)
{
sslsockdata *data = conn->sockdata;
int ret;
ret = gnutls_write (conn->fd, data->state, buf, len);
if (gnutls_is_fatal_error(ret) == 1)
{
mutt_error ("ssl_socket_write (%s)", gnutls_strerror (ret));
mutt_sleep (4);
return -1;
}
return ret;
}
static int ssl_socket_open (CONNECTION * conn)
{
sslsockdata* data;
if (raw_socket_open (conn) < 0)
return -1;
data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata));
conn->sockdata = data;
if (gnutls_allocate_x509_client_sc (&data->xcred, 1) < 0)
return mutt_ssl_error ("gnutls_allocate_x509_client_sc");
gnutls_set_x509_client_trust (data->xcred, "", "");
gnutls_set_x509_client_key (data->xcred, SslCertFile, "");
gnutls_set_x509_cert_callback (data->xcred, cert_callback);
/* XXX disable SSL protocols as needed */
gnutls_init(&data->state, GNUTLS_CLIENT);
gnutls_set_protocol_priority (data->state, GNUTLS_TLS1, GNUTLS_SSL3, 0);
gnutls_set_cipher_priority (data->state,
GNUTLS_3DES_CBC, GNUTLS_RIJNDAEL_CBC, 0);
gnutls_set_compression_priority (data->state,
GNUTLS_ZLIB, GNUTLS_NULL_COMPRESSION, 0);
gnutls_set_kx_priority (data->state,
GNUTLS_KX_DHE_RSA, GNUTLS_KX_RSA, GNUTLS_KX_SRP, GNUTLS_KX_DH_ANON, 0);
gnutls_set_mac_priority (data->state, GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0);
gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
gnutls_set_cred (data->state, GNUTLS_X509PKI, data->xcred);
if (ssl_negotiate (conn) < 0)
{
ssl_socket_close (conn);
return -1;
}
return 0;
}
/* ssl_negotiate: After SSL state has been initialised, attempt to negotiate
* SSL over the wire, including certificate checks. */
static int ssl_negotiate (CONNECTION * conn)
{
sslsockdata *data = conn->sockdata;
int err;
err = gnutls_handshake(conn->fd, data->state);
if (err < 0)
return mutt_ssl_error ("gnutls_handshake");
/*
if (!ssl_check_certificate (sockdata))
return -1;
*/
mutt_message (_("SSL connection using %s (%s)"),
gnutls_version_get_name (gnutls_get_current_version (data->state)),
gnutls_cipher_get_name (gnutls_get_current_cipher (data->state)));
mutt_sleep (0);
return 0;
}
static int ssl_socket_close (CONNECTION * conn)
{
sslsockdata *data = conn->sockdata;
if (data)
{
gnutls_bye (conn->fd, data->state, GNUTLS_SHUT_RDWR);
gnutls_free_x509_client_sc (data->xcred);
gnutls_deinit (data->state);
safe_free ((void **) &conn->sockdata);
}
return raw_socket_close (conn);
}
static int cert_callback (gnutls_DN *client_cert, gnutls_DN *issuer_cert,
int ncerts, gnutls_DN* req_ca_cert, int nreqs)
{
if (client_cert == NULL)
return 0;
return -1; /* send no certificate to the peer */
}
--jRHKVT23PllUwdXP--