ssl-proxy-gnutls.c revision cca4ba2a504d70a9fe9fee37f8433997359de52c
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include "login-common.h"
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include "ioloop.h"
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include "net.h"
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include "hash.h"
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include "ssl-proxy.h"
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#ifdef HAVE_GNUTLS
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#error broken currently
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <stdio.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <stdlib.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <unistd.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <fcntl.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <gcrypt.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen#include <gnutls/gnutls.h>
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstruct ssl_proxy {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen int refcount;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen gnutls_session session;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen struct ip_addr ip;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen int fd_ssl, fd_plain;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen struct io *io_ssl, *io_plain;
2dbb7b30d9bf6d6b0cd104c6931c4b44a77c2383Aki Tuomi int io_ssl_dir;
2dbb7b30d9bf6d6b0cd104c6931c4b44a77c2383Aki Tuomi
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen unsigned char outbuf_plain[1024];
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen unsigned int outbuf_pos_plain;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen size_t send_left_ssl, send_left_plain;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen};
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int protocol_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int kx_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int cipher_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int comp_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int mac_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenconst int cert_type_priority[] =
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen { GNUTLS_CRT_X509, 0 };
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
af2cc755b98e6e61e3167b0d5e69b7c88d8e6aa5Timo Sirainenstatic struct hash_table *ssl_proxies;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic gnutls_certificate_credentials x509_cred;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic gnutls_dh_params dh_params;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic gnutls_rsa_params rsa_params;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic void ssl_input(struct ssl_proxy *proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic void plain_input(struct ssl_proxy *proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic bool ssl_proxy_destroy(struct ssl_proxy *proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic const char *get_alert_text(struct ssl_proxy *proxy)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen{
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen}
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic int handle_ssl_error(struct ssl_proxy *proxy, int error)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen{
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (!gnutls_error_is_fatal(error)) {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (!verbose_ssl)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return 0;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen i_warning("Received SSL warning alert: %s [%s]",
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen get_alert_text(proxy),
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen net_ip2addr(&proxy->ip));
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen } else {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen i_warning("Non-fatal SSL error: %s: %s",
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen get_alert_text(proxy),
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen net_ip2addr(&proxy->ip));
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return 0;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (verbose_ssl) {
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen /* fatal error occurred */
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen i_warning("Received SSL fatal alert: %s [%s]",
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen get_alert_text(proxy),
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen net_ip2addr(&proxy->ip));
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen } else {
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen i_warning("Error reading from SSL client: %s [%s]",
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen gnutls_strerror(error),
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen net_ip2addr(&proxy->ip));
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen }
209335fbc1a5fe68e662242ea91e236fdb2ba29dTimo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen gnutls_alert_send_appropriate(proxy->session, error);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen ssl_proxy_destroy(proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return -1;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen}
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen{
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen int rcvd;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen rcvd = gnutls_record_recv(proxy->session, data, size);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (rcvd > 0)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return rcvd;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen /* disconnected, either by nicely telling us that we'll
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen close the connection, or by simply killing the
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen connection which gives us the packet length error. */
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen ssl_proxy_destroy(proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return -1;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return handle_ssl_error(proxy, rcvd);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen}
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic int proxy_send_ssl(struct ssl_proxy *proxy,
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen const void *data, size_t size)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen{
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen int sent;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen sent = gnutls_record_send(proxy->session, data, size);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (sent >= 0)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return sent;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen /* don't warn about errors related to unexpected
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen disconnection */
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen ssl_proxy_destroy(proxy);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return -1;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return handle_ssl_error(proxy, sent);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen}
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainenstatic int ssl_proxy_destroy(struct ssl_proxy *proxy)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen{
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen if (--proxy->refcount > 0)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return TRUE;
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen hash_table_remove(ssl_proxies, proxy);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi gnutls_deinit(proxy->session);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi if (proxy->io_ssl != NULL)
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi io_remove(proxy->io_ssl);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi if (proxy->io_plain != NULL)
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi io_remove(proxy->io_plain);
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi net_disconnect(proxy->fd_ssl);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi net_disconnect(proxy->fd_plain);
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi i_free(proxy);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi main_unref();
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi return FALSE;
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi}
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomistatic void ssl_output(struct ssl_proxy *proxy)
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi{
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi int sent;
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi sent = net_transmit(proxy->fd_plain,
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen proxy->outbuf_plain + proxy->outbuf_pos_plain,
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi proxy->send_left_plain);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi if (sent < 0) {
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi /* disconnected */
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi ssl_proxy_destroy(proxy);
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi return;
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi }
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi proxy->send_left_plain -= sent;
2d83aa5eebd1bb710ca2fc21316b89442f027f3dAki Tuomi proxy->outbuf_pos_plain += sent;
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi
a893aaa999856b1ba6e4541890016767aaa283c7Aki Tuomi if (proxy->send_left_plain > 0)
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return;
/* everything is sent, start reading again */
io_remove(proxy->io_ssl);
proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy);
}
static void ssl_input(struct ssl_proxy *proxy)
{
int rcvd, sent;
rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain,
sizeof(proxy->outbuf_plain));
if (rcvd <= 0)
return;
sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd);
if (sent == rcvd)
return;
if (sent < 0) {
/* disconnected */
ssl_proxy_destroy(proxy);
return;
}
/* everything wasn't sent - don't read anything until we've
sent it all */
proxy->outbuf_pos_plain = 0;
proxy->send_left_plain = rcvd - sent;
io_remove(proxy->io_ssl);
proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy);
}
static void plain_output(struct ssl_proxy *proxy)
{
int sent;
sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl);
if (sent <= 0)
return;
proxy->send_left_ssl -= sent;
if (proxy->send_left_ssl > 0)
return;
/* everything is sent, start reading again */
io_remove(proxy->io_plain);
proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy);
}
static void plain_input(struct ssl_proxy *proxy)
{
char buf[1024];
ssize_t rcvd, sent;
rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf));
if (rcvd < 0) {
/* disconnected */
gnutls_bye(proxy->session, 1);
ssl_proxy_destroy(proxy);
return;
}
sent = proxy_send_ssl(proxy, buf, (size_t)rcvd);
if (sent < 0 || sent == rcvd)
return;
/* everything wasn't sent - don't read anything until we've
sent it all */
proxy->send_left_ssl = rcvd - sent;
io_remove(proxy->io_plain);
proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy);
}
static void ssl_handshake(struct ssl_proxy *proxy)
{
int ret, dir;
ret = gnutls_handshake(proxy->session);
if (ret >= 0) {
/* handshake done, now we can start reading */
if (proxy->io_ssl != NULL)
io_remove(proxy->io_ssl);
proxy->io_plain = io_add(proxy->fd_plain, IO_READ,
plain_input, proxy);
proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ,
ssl_input, proxy);
return;
}
if (handle_ssl_error(proxy, ret) < 0)
return;
/* i/o interrupted */
dir = gnutls_record_get_direction(proxy->session) == 0 ?
IO_READ : IO_WRITE;
if (proxy->io_ssl_dir != dir) {
if (proxy->io_ssl != NULL)
io_remove(proxy->io_ssl);
proxy->io_ssl = io_add(proxy->fd_ssl, dir,
ssl_handshake, proxy);
proxy->io_ssl_dir = dir;
}
}
static gnutls_session initialize_state(void)
{
gnutls_session session;
gnutls_init(&session, GNUTLS_SERVER);
gnutls_protocol_set_priority(session, protocol_priority);
gnutls_cipher_set_priority(session, cipher_priority);
gnutls_compression_set_priority(session, comp_priority);
gnutls_kx_set_priority(session, kx_priority);
gnutls_mac_set_priority(session, mac_priority);
gnutls_certificate_type_set_priority(session, cert_type_priority);
gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
return session;
}
int ssl_proxy_new(int fd, struct ip_addr *ip)
{
struct ssl_proxy *proxy;
gnutls_session session;
int sfd[2];
if (!ssl_initialized) {
i_error("SSL support not enabled in configuration");
return -1;
}
session = initialize_state();
gnutls_transport_set_ptr(session, fd);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
i_error("socketpair() failed: %m");
gnutls_deinit(session);
return -1;
}
net_set_nonblock(sfd[0], TRUE);
net_set_nonblock(sfd[1], TRUE);
net_set_nonblock(fd, TRUE);
proxy = i_new(struct ssl_proxy, 1);
proxy->refcount = 1;
proxy->session = session;
proxy->fd_ssl = fd;
proxy->fd_plain = sfd[0];
proxy->ip = *ip;
hash_table_insert(ssl_proxies, proxy, proxy);
proxy->refcount++;
ssl_handshake(proxy);
if (!ssl_proxy_destroy(proxy)) {
/* handshake failed. return the disconnected socket anyway
so the caller doesn't try to use the old closed fd */
return sfd[1];
}
main_ref();
return sfd[1];
}
static void read_next_field(int fd, gnutls_datum *datum,
const char *fname, const char *field_name)
{
ssize_t ret;
/* get size */
ret = read(fd, &datum->size, sizeof(datum->size));
if (ret < 0)
i_fatal("read() failed for %s: %m", fname);
if (ret != sizeof(datum->size)) {
(void)unlink(fname);
i_fatal("Corrupted SSL parameter file %s: File too small",
fname);
}
if (datum->size > 10240) {
(void)unlink(fname);
i_fatal("Corrupted SSL parameter file %s: "
"Field '%s' too large (%u)",
fname, field_name, datum->size);
}
/* read the actual data */
datum->data = t_malloc(datum->size);
ret = read(fd, datum->data, datum->size);
if (ret < 0)
i_fatal("read() failed for %s: %m", fname);
if ((size_t)ret != datum->size) {
(void)unlink(fname);
i_fatal("Corrupted SSL parameter file %s: "
"Field '%s' not fully in file (%u < %u)",
fname, field_name, datum->size - ret, datum->size);
}
}
static void read_dh_parameters(int fd, const char *fname)
{
gnutls_datum dbits, prime, generator;
int ret, bits;
if ((ret = gnutls_dh_params_init(&dh_params)) < 0) {
i_fatal("gnutls_dh_params_init() failed: %s",
gnutls_strerror(ret));
}
/* read until bits field is 0 */
for (;;) {
read_next_field(fd, &dbits, fname, "DH bits");
if (dbits.size != sizeof(int)) {
(void)unlink(fname);
i_fatal("Corrupted SSL parameter file %s: "
"Field 'DH bits' has invalid size %u",
fname, dbits.size);
}
bits = *((int *) dbits.data);
if (bits == 0)
break;
read_next_field(fd, &prime, fname, "DH prime");
read_next_field(fd, &generator, fname, "DH generator");
ret = gnutls_dh_params_set(dh_params, prime, generator, bits);
if (ret < 0) {
i_fatal("gnutls_dh_params_set() failed: %s",
gnutls_strerror(ret));
}
}
}
static void read_rsa_parameters(int fd, const char *fname)
{
gnutls_datum m, e, d, p, q, u;
int ret;
read_next_field(fd, &m, fname, "RSA m");
read_next_field(fd, &e, fname, "RSA e");
read_next_field(fd, &d, fname, "RSA d");
read_next_field(fd, &p, fname, "RSA p");
read_next_field(fd, &q, fname, "RSA q");
read_next_field(fd, &u, fname, "RSA u");
if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) {
i_fatal("gnutls_rsa_params_init() failed: %s",
gnutls_strerror(ret));
}
/* only 512bit is allowed */
ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512);
if (ret < 0) {
i_fatal("gnutls_rsa_params_set() failed: %s",
gnutls_strerror(ret));
}
}
static void read_parameters(const char *fname)
{
int fd;
/* we'll wait until parameter file exists */
for (;;) {
fd = open(fname, O_RDONLY);
if (fd != -1)
break;
if (errno != ENOENT)
i_fatal("Can't open SSL parameter file %s: %m", fname);
sleep(1);
}
read_dh_parameters(fd, fname);
read_rsa_parameters(fd, fname);
i_close_fd(&fd);
}
static void gcrypt_log_handler(void *context ATTR_UNUSED, int level,
const char *fmt, va_list args)
{
if (level != GCRY_LOG_FATAL)
return;
T_BEGIN {
i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args));
} T_END;
}
void ssl_proxy_init(void)
{
const char *certfile, *keyfile, *paramfile;
unsigned char buf[4];
int ret;
certfile = getenv("SSL_CERT_FILE");
keyfile = getenv("SSL_KEY_FILE");
paramfile = getenv("SSL_PARAM_FILE");
if (certfile == NULL || keyfile == NULL || paramfile == NULL) {
/* SSL support is disabled */
return;
}
if ((ret = gnutls_global_init() < 0)) {
i_fatal("gnu_tls_global_init() failed: %s",
gnutls_strerror(ret));
}
/* gcrypt initialization - set log handler and make sure randomizer
opens /dev/urandom now instead of after we've chrooted */
gcry_set_log_handler(gcrypt_log_handler, NULL);
gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM);
read_parameters(paramfile);
if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
i_fatal("gnutls_certificate_allocate_credentials() failed: %s",
gnutls_strerror(ret));
}
ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
i_fatal("Can't load certificate files %s and %s: %s",
certfile, keyfile, gnutls_strerror(ret));
}
gnutls_certificate_set_dh_params(x509_cred, dh_params);
gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
ssl_proxies = hash_table_create(default_pool, 0, NULL, NULL);
ssl_initialized = TRUE;
}
void ssl_proxy_deinit(void)
{
struct hash_iterate_context *iter;
void *key, *value;
if (!ssl_initialized)
return;
iter = hash_table_iterate_init(ssl_proxies);
while (hash_table_iterate(iter, &key, &value))
ssl_proxy_destroy(value);
hash_table_iterate_deinit(iter);
hash_table_destroy(ssl_proxies);
gnutls_certificate_free_credentials(x509_cred);
gnutls_global_deinit();
}
#endif