[dovecot-cvs] dovecot/src/login ssl-proxy-gnutls.c,NONE,1.1 ssl-proxy-openssl.c,NONE,1.1 Makefile.am,1.1.1.1,1.2 ssl-proxy.c,1.19,1.20
cras at procontrol.fi
cras at procontrol.fi
Wed Nov 20 16:05:16 EET 2002
- Previous message: [dovecot-cvs] dovecot acconfig.h,1.13,1.14 configure.in,1.44,1.45
- Next message: [dovecot-cvs] dovecot/src/master ssl-init-gnutls.c,NONE,1.1 ssl-init-openssl.c,NONE,1.1 Makefile.am,1.3,1.4 ssl-init.c,1.1,1.2 ssl-init.h,1.1,1.2
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /home/cvs/dovecot/src/login
In directory danu:/tmp/cvs-serv18612/src/login
Modified Files:
Makefile.am ssl-proxy.c
Added Files:
ssl-proxy-gnutls.c ssl-proxy-openssl.c
Log Message:
Support for OpenSSL.
--- NEW FILE: ssl-proxy-gnutls.c ---
/* Copyright (C) 2002 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "ssl-proxy.h"
#ifdef HAVE_GNUTLS
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <gcrypt.h>
#include <gnutls/gnutls.h>
typedef struct {
int refcount;
gnutls_session session;
int fd_ssl, fd_plain;
IO io_ssl, io_plain;
int io_ssl_dir;
unsigned char outbuf_plain[1024];
unsigned int outbuf_pos_plain;
size_t send_left_ssl, send_left_plain;
} SSLProxy;
const int protocol_priority[] =
{ GNUTLS_TLS1, GNUTLS_SSL3, 0 };
const int kx_priority[] =
{ GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
const int cipher_priority[] =
{ GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
const int comp_priority[] =
{ GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
const int mac_priority[] =
{ GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
const int cert_type_priority[] =
{ GNUTLS_CRT_X509, 0 };
static gnutls_certificate_credentials x509_cred;
static gnutls_dh_params dh_params;
static gnutls_rsa_params rsa_params;
static void ssl_input(void *context, int handle, IO io);
static void plain_input(void *context, int handle, IO io);
static int ssl_proxy_destroy(SSLProxy *proxy);
static const char *get_alert_text(SSLProxy *proxy)
{
return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
}
static int handle_ssl_error(SSLProxy *proxy, int error)
{
if (!gnutls_error_is_fatal(error)) {
if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
i_warning("Received SSL warning alert: %s",
get_alert_text(proxy));
}
return 0;
}
/* fatal error occured */
if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
i_warning("Received SSL fatal alert: %s",
get_alert_text(proxy));
} else {
i_warning("Error reading from SSL client: %s",
gnutls_strerror(error));
}
gnutls_alert_send_appropriate(proxy->session, error);
ssl_proxy_destroy(proxy);
return -1;
}
static int proxy_recv_ssl(SSLProxy *proxy, void *data, size_t size)
{
int rcvd;
rcvd = gnutls_record_recv(proxy->session, data, size);
if (rcvd > 0)
return rcvd;
if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
/* disconnected, either by nicely telling us that we'll
close the connection, or by simply killing the
connection which gives us the packet length error. */
ssl_proxy_destroy(proxy);
return -1;
}
return handle_ssl_error(proxy, rcvd);
}
static int proxy_send_ssl(SSLProxy *proxy, const void *data, size_t size)
{
int sent;
sent = gnutls_record_send(proxy->session, data, size);
if (sent >= 0)
return sent;
if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
/* don't warn about errors related to unexpected disconnection */
ssl_proxy_destroy(proxy);
return -1;
}
return handle_ssl_error(proxy, sent);
}
static int ssl_proxy_destroy(SSLProxy *proxy)
{
if (--proxy->refcount > 0)
return TRUE;
gnutls_deinit(proxy->session);
(void)net_disconnect(proxy->fd_ssl);
(void)net_disconnect(proxy->fd_plain);
if (proxy->io_ssl != NULL)
io_remove(proxy->io_ssl);
if (proxy->io_plain != NULL)
io_remove(proxy->io_plain);
i_free(proxy);
main_unref();
return FALSE;
}
static void ssl_output(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
int sent;
sent = net_transmit(proxy->fd_plain,
proxy->outbuf_plain + proxy->outbuf_pos_plain,
proxy->send_left_plain);
if (sent < 0) {
/* disconnected */
ssl_proxy_destroy(proxy);
return;
}
proxy->send_left_plain -= sent;
proxy->outbuf_pos_plain += sent;
if (proxy->send_left_plain > 0)
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(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
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(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
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(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
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(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
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_handshake_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_cert_type_set_priority(session, cert_type_priority);
gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
return session;
}
int ssl_proxy_new(int fd)
{
SSLProxy *proxy;
gnutls_session session;
int sfd[2];
if (!ssl_initialized)
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);
proxy = i_new(SSLProxy, 1);
proxy->refcount = 1;
proxy->session = session;
proxy->fd_ssl = fd;
proxy->fd_plain = sfd[0];
proxy->refcount++;
ssl_handshake(proxy, -1, NULL);
if (!ssl_proxy_destroy(proxy))
return -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);
(void)close(fd);
}
static void gcrypt_log_handler(void *context __attr_unused__, int level,
const char *fmt, va_list args)
{
char *buf;
t_push();
buf = t_malloc(printf_string_upper_bound(fmt, args));
vsprintf(buf, fmt, args);
if (level == GCRY_LOG_FATAL)
i_error("gcrypt fatal: %s", buf);
t_pop();
}
void ssl_proxy_init(void)
{
const char *certfile, *keyfile, *paramfile;
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_cred(&x509_cred)) < 0) {
i_fatal("gnutls_certificate_allocate_cred() 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));
}
ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
if (ret < 0)
i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
if (ret < 0)
i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
ssl_initialized = TRUE;
}
void ssl_proxy_deinit(void)
{
if (ssl_initialized) {
gnutls_certificate_free_cred(x509_cred);
gnutls_global_deinit();
}
}
#endif
--- NEW FILE: ssl-proxy-openssl.c ---
/* Copyright (C) 2002 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "ssl-proxy.h"
#ifdef HAVE_OPENSSL
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
typedef enum {
SSL_STATE_HANDSHAKE,
SSL_STATE_READ,
SSL_STATE_WRITE
} SSLState;
typedef struct {
int refcount;
SSL *ssl;
SSLState state;
int fd_ssl, fd_plain;
IO io_ssl, io_plain_read, io_plain_write;
int io_ssl_dir;
unsigned char plainout_buf[1024];
unsigned int plainout_pos, plainout_size;
unsigned char sslout_buf[1024];
unsigned int sslout_pos, sslout_size;
} SSLProxy;
static SSL_CTX *ssl_ctx;
static void plain_read(SSLProxy *proxy);
static void plain_write(SSLProxy *proxy);
static int ssl_proxy_destroy(SSLProxy *proxy);
static void ssl_set_direction(SSLProxy *proxy, int dir);
static void plain_block_input(SSLProxy *proxy, int block)
{
if (block) {
if (proxy->io_plain_read != NULL) {
io_remove(proxy->io_plain_read);
proxy->io_plain_read = NULL;
}
} else {
if (proxy->io_plain_read == NULL) {
proxy->io_plain_read =
io_add(proxy->fd_plain, IO_READ,
(IOFunc) plain_read, proxy);
}
}
}
static void ssl_block(SSLProxy *proxy, int block)
{
i_assert(proxy->state == SSL_STATE_READ);
if (block) {
if (proxy->io_ssl != NULL) {
io_remove(proxy->io_ssl);
proxy->io_ssl = NULL;
}
proxy->io_ssl_dir = -2;
} else {
proxy->io_ssl_dir = -1;
ssl_set_direction(proxy, IO_READ);
}
}
static void plain_read(SSLProxy *proxy)
{
ssize_t ret;
i_assert(proxy->sslout_size == 0);
ret = net_receive(proxy->fd_plain, proxy->sslout_buf,
sizeof(proxy->sslout_buf));
if (ret < 0)
ssl_proxy_destroy(proxy);
else {
proxy->sslout_size = ret;
proxy->sslout_pos = 0;
proxy->state = SSL_STATE_WRITE;
ssl_set_direction(proxy, IO_WRITE);
plain_block_input(proxy, TRUE);
}
}
static void plain_write(SSLProxy *proxy)
{
ssize_t ret;
ret = net_transmit(proxy->fd_plain,
proxy->plainout_buf + proxy->plainout_pos,
proxy->plainout_size);
if (ret < 0)
ssl_proxy_destroy(proxy);
else {
proxy->plainout_size -= ret;
proxy->plainout_pos += ret;
if (proxy->plainout_size > 0) {
ssl_block(proxy, TRUE);
if (proxy->io_plain_write == NULL) {
proxy->io_plain_write =
io_add(proxy->fd_plain, IO_WRITE,
(IOFunc) plain_write, proxy);
}
} else {
proxy->plainout_pos = 0;
ssl_block(proxy, FALSE);
if (proxy->io_plain_write != NULL) {
io_remove(proxy->io_plain_write);
proxy->io_plain_write = NULL;
}
}
}
}
const char *ssl_last_error(void)
{
unsigned long err;
char *buf;
err = ERR_get_error();
if (err == 0)
return strerror(errno);
buf = t_malloc(256);
buf[255] = '\0';
ERR_error_string_n(err, buf, 255);
return buf;
}
static void ssl_handle_error(SSLProxy *proxy, int err, const char *func)
{
err = SSL_get_error(proxy->ssl, err);
switch (err) {
case SSL_ERROR_WANT_READ:
ssl_set_direction(proxy, IO_READ);
break;
case SSL_ERROR_WANT_WRITE:
ssl_set_direction(proxy, IO_WRITE);
break;
case SSL_ERROR_SYSCALL:
/* eat up the error queue */
i_error("%s failed: %s", func, ssl_last_error());
ssl_proxy_destroy(proxy);
break;
case SSL_ERROR_ZERO_RETURN:
/* clean connection closing */
default:
ssl_proxy_destroy(proxy);
break;
}
}
static void ssl_handshake_step(SSLProxy *proxy)
{
int ret;
ret = SSL_accept(proxy->ssl);
if (ret != 1) {
plain_block_input(proxy, TRUE);
ssl_handle_error(proxy, ret, "SSL_accept()");
} else {
plain_block_input(proxy, FALSE);
ssl_set_direction(proxy, IO_READ);
proxy->state = SSL_STATE_READ;
}
}
static void ssl_read_step(SSLProxy *proxy)
{
int ret;
i_assert(proxy->plainout_size == 0);
ret = SSL_read(proxy->ssl, proxy->plainout_buf,
sizeof(proxy->plainout_buf));
if (ret <= 0) {
plain_block_input(proxy, TRUE);
ssl_handle_error(proxy, ret, "SSL_read()");
} else {
plain_block_input(proxy, FALSE);
ssl_set_direction(proxy, IO_READ);
proxy->plainout_pos = 0;
proxy->plainout_size = ret;
plain_write(proxy);
}
}
static void ssl_write_step(SSLProxy *proxy)
{
int ret;
ret = SSL_write(proxy->ssl, proxy->sslout_buf + proxy->sslout_pos,
proxy->sslout_size);
if (ret <= 0) {
plain_block_input(proxy, TRUE);
ssl_handle_error(proxy, ret, "SSL_write()");
} else {
proxy->sslout_size -= ret;
proxy->sslout_pos += ret;
if (proxy->sslout_size > 0) {
plain_block_input(proxy, TRUE);
ssl_set_direction(proxy, IO_WRITE);
proxy->state = SSL_STATE_WRITE;
} else {
plain_block_input(proxy, FALSE);
ssl_set_direction(proxy, IO_READ);
proxy->state = SSL_STATE_READ;
proxy->sslout_pos = 0;
}
}
}
static void ssl_step(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
SSLProxy *proxy = context;
switch (proxy->state) {
case SSL_STATE_HANDSHAKE:
ssl_handshake_step(proxy);
break;
case SSL_STATE_READ:
ssl_read_step(proxy);
break;
case SSL_STATE_WRITE:
ssl_write_step(proxy);
break;
}
}
static void ssl_set_direction(SSLProxy *proxy, int dir)
{
i_assert(proxy->io_ssl_dir != -2);
if (proxy->io_ssl_dir == dir)
return;
if (proxy->io_ssl != NULL)
io_remove(proxy->io_ssl);
proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_step, proxy);
}
int ssl_proxy_new(int fd)
{
SSLProxy *proxy;
SSL *ssl;
int sfd[2];
if (!ssl_initialized)
return -1;
ssl = SSL_new(ssl_ctx);
if (ssl == NULL) {
i_error("SSL_new() failed: %s", ssl_last_error());
return -1;
}
SSL_set_accept_state(ssl);
if (SSL_set_fd(ssl, fd) != 1) {
i_error("SSL_set_fd() failed: %s", ssl_last_error());
return -1;
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
i_error("socketpair() failed: %m");
SSL_free(ssl);
return -1;
}
net_set_nonblock(sfd[0], TRUE);
net_set_nonblock(sfd[1], TRUE);
proxy = i_new(SSLProxy, 1);
proxy->refcount = 1;
proxy->ssl = ssl;
proxy->fd_ssl = fd;
proxy->fd_plain = sfd[0];
proxy->state = SSL_STATE_HANDSHAKE;
ssl_set_direction(proxy, IO_READ);
proxy->refcount++;
ssl_handshake_step(proxy);
if (!ssl_proxy_destroy(proxy))
return -1;
main_ref();
return sfd[1];
}
static int ssl_proxy_destroy(SSLProxy *proxy)
{
if (--proxy->refcount > 0)
return TRUE;
SSL_free(proxy->ssl);
(void)net_disconnect(proxy->fd_ssl);
(void)net_disconnect(proxy->fd_plain);
if (proxy->io_ssl != NULL)
io_remove(proxy->io_ssl);
if (proxy->io_plain_read != NULL)
io_remove(proxy->io_plain_read);
if (proxy->io_plain_write != NULL)
io_remove(proxy->io_plain_write);
i_free(proxy);
main_unref();
return FALSE;
}
void ssl_proxy_init(void)
{
const char *certfile, *keyfile, *paramfile;
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;
}
SSL_library_init();
SSL_load_error_strings();
if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
i_fatal("SSL_CTX_new() failed");
ret = SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile);
if (ret != 1) {
i_fatal("Can't load certificate file %s: %s",
certfile, ssl_last_error());
}
ret = SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile, SSL_FILETYPE_PEM);
if (ret != 1) {
i_fatal("Can't load private key file %s: %s",
keyfile, ssl_last_error());
}
ssl_initialized = TRUE;
}
void ssl_proxy_deinit(void)
{
if (ssl_initialized)
SSL_CTX_free(ssl_ctx);
}
#endif
Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/login/Makefile.am,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- Makefile.am 9 Aug 2002 09:15:53 -0000 1.1.1.1
+++ Makefile.am 20 Nov 2002 14:05:13 -0000 1.2
@@ -13,7 +13,9 @@
client-authenticate.c \
main.c \
master.c \
- ssl-proxy.c
+ ssl-proxy.c \
+ ssl-proxy-gnutls.c \
+ ssl-proxy-openssl.c
noinst_HEADERS = \
auth-connection.h \
Index: ssl-proxy.c
===================================================================
RCS file: /home/cvs/dovecot/src/login/ssl-proxy.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- ssl-proxy.c 17 Nov 2002 09:42:08 -0000 1.19
+++ ssl-proxy.c 20 Nov 2002 14:05:13 -0000 1.20
@@ -1,526 +1,10 @@
/* Copyright (C) 2002 Timo Sirainen */
-#include "common.h"
-#include "ioloop.h"
-#include "network.h"
-#include "ssl-proxy.h"
+#include "lib.h"
int ssl_initialized = FALSE;
-#ifdef HAVE_SSL
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <gcrypt.h>
-#include <gnutls/gnutls.h>
-
-typedef struct {
- int refcount;
-
- gnutls_session session;
- int fd_ssl, fd_plain;
- IO io_ssl, io_plain;
- int io_ssl_dir;
-
- unsigned char outbuf_plain[1024];
- unsigned int outbuf_pos_plain;
-
- size_t send_left_ssl, send_left_plain;
-} SSLProxy;
-
-const int protocol_priority[] =
- { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
-const int kx_priority[] =
- { GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 };
-const int cipher_priority[] =
- { GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC,
- GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 };
-const int comp_priority[] =
- { GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
-const int mac_priority[] =
- { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
-const int cert_type_priority[] =
- { GNUTLS_CRT_X509, 0 };
-
-static gnutls_certificate_credentials x509_cred;
-static gnutls_dh_params dh_params;
-static gnutls_rsa_params rsa_params;
-
-static void ssl_input(void *context, int handle, IO io);
-static void plain_input(void *context, int handle, IO io);
-static int ssl_proxy_destroy(SSLProxy *proxy);
-
-static const char *get_alert_text(SSLProxy *proxy)
-{
- return gnutls_alert_get_name(gnutls_alert_get(proxy->session));
-}
-
-static int handle_ssl_error(SSLProxy *proxy, int error)
-{
- if (!gnutls_error_is_fatal(error)) {
- if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) {
- i_warning("Received SSL warning alert: %s",
- get_alert_text(proxy));
- }
- return 0;
- }
-
- /* fatal error occured */
- if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) {
- i_warning("Received SSL fatal alert: %s",
- get_alert_text(proxy));
- } else {
- i_warning("Error reading from SSL client: %s",
- gnutls_strerror(error));
- }
-
- gnutls_alert_send_appropriate(proxy->session, error);
- ssl_proxy_destroy(proxy);
- return -1;
-}
-
-static int proxy_recv_ssl(SSLProxy *proxy, void *data, size_t size)
-{
- int rcvd;
-
- rcvd = gnutls_record_recv(proxy->session, data, size);
- if (rcvd > 0)
- return rcvd;
-
- if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
- /* disconnected, either by nicely telling us that we'll
- close the connection, or by simply killing the
- connection which gives us the packet length error. */
- ssl_proxy_destroy(proxy);
- return -1;
- }
-
- return handle_ssl_error(proxy, rcvd);
-}
-
-static int proxy_send_ssl(SSLProxy *proxy, const void *data, size_t size)
-{
- int sent;
-
- sent = gnutls_record_send(proxy->session, data, size);
- if (sent >= 0)
- return sent;
-
- if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) {
- /* don't warn about errors related to unexpected disconnection */
- ssl_proxy_destroy(proxy);
- return -1;
- }
-
- return handle_ssl_error(proxy, sent);
-}
-
-static int ssl_proxy_destroy(SSLProxy *proxy)
-{
- if (--proxy->refcount > 0)
- return TRUE;
-
- gnutls_deinit(proxy->session);
-
- (void)net_disconnect(proxy->fd_ssl);
- (void)net_disconnect(proxy->fd_plain);
-
- if (proxy->io_ssl != NULL)
- io_remove(proxy->io_ssl);
- if (proxy->io_plain != NULL)
- io_remove(proxy->io_plain);
-
- i_free(proxy);
-
- main_unref();
- return FALSE;
-}
-
-static void ssl_output(void *context, int fd __attr_unused__,
- IO io __attr_unused__)
-{
- SSLProxy *proxy = context;
- int sent;
-
- sent = net_transmit(proxy->fd_plain,
- proxy->outbuf_plain + proxy->outbuf_pos_plain,
- proxy->send_left_plain);
- if (sent < 0) {
- /* disconnected */
- ssl_proxy_destroy(proxy);
- return;
- }
-
- proxy->send_left_plain -= sent;
- proxy->outbuf_pos_plain += sent;
-
- if (proxy->send_left_plain > 0)
- 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(void *context, int fd __attr_unused__,
- IO io __attr_unused__)
-{
- SSLProxy *proxy = context;
- 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(void *context, int fd __attr_unused__,
- IO io __attr_unused__)
-{
- SSLProxy *proxy = context;
- 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(void *context, int fd __attr_unused__,
- IO io __attr_unused__)
-{
- SSLProxy *proxy = context;
- 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(void *context, int fd __attr_unused__,
- IO io __attr_unused__)
-{
- SSLProxy *proxy = context;
- 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_handshake_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_cert_type_set_priority(session, cert_type_priority);
-
- gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
- return session;
-}
-
-int ssl_proxy_new(int fd)
-{
- SSLProxy *proxy;
- gnutls_session session;
- int sfd[2];
-
- if (!ssl_initialized)
- 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);
-
- proxy = i_new(SSLProxy, 1);
- proxy->refcount = 1;
- proxy->session = session;
- proxy->fd_ssl = fd;
- proxy->fd_plain = sfd[0];
-
- proxy->refcount++;
- ssl_handshake(proxy, -1, NULL);
- if (!ssl_proxy_destroy(proxy))
- return -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);
-
- (void)close(fd);
-}
-
-static void gcrypt_log_handler(void *context __attr_unused__, int level,
- const char *fmt, va_list args)
-{
- char *buf;
-
- t_push();
-
- buf = t_malloc(printf_string_upper_bound(fmt, args));
- vsprintf(buf, fmt, args);
-
- if (level == GCRY_LOG_FATAL)
- i_error("gcrypt fatal: %s", buf);
-
- t_pop();
-}
-
-void ssl_proxy_init(void)
-{
- const char *certfile, *keyfile, *paramfile;
- 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_cred(&x509_cred)) < 0) {
- i_fatal("gnutls_certificate_allocate_cred() 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));
- }
-
- ret = gnutls_certificate_set_dh_params(x509_cred, dh_params);
- if (ret < 0)
- i_fatal("Can't set DH parameters: %s", gnutls_strerror(ret));
- ret = gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
- if (ret < 0)
- i_fatal("Can't set RSA parameters: %s", gnutls_strerror(ret));
-
- ssl_initialized = TRUE;
-}
-
-void ssl_proxy_deinit(void)
-{
- if (ssl_initialized) {
- gnutls_certificate_free_cred(x509_cred);
- gnutls_global_deinit();
- }
-}
-
-#else
+#ifndef HAVE_SSL
/* no SSL support */
- Previous message: [dovecot-cvs] dovecot acconfig.h,1.13,1.14 configure.in,1.44,1.45
- Next message: [dovecot-cvs] dovecot/src/master ssl-init-gnutls.c,NONE,1.1 ssl-init-openssl.c,NONE,1.1 Makefile.am,1.3,1.4 ssl-init.c,1.1,1.2 ssl-init.h,1.1,1.2
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the dovecot-cvs
mailing list