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