ssl.c
author Sunil Nimmagadda <sunil@nimmagadda.net>
Sun, 31 Aug 2014 16:18:35 +0500
changeset 37 e3dccf824f91
parent 0 9e2cb1ed20b1
child 43 6903f7870c4c
permissions -rw-r--r--
Disengaging imsgev is tricky. First imsgev_clear as we know there isn't anything left to be written. Signal imsgev termination by setting iev->terminate = 1(Mind that we are setting it from with the callback). As imsgev would again callback dispatch with IMSGEV_DONE, we cannot free imsgev yet. So iev_maildrop need to exist beyond lifetime of session until IMSGEV_DONE. Allocating it seperately from session. imsgev_close is avoided as it schedules another EV_WRITE which is not needed in our case. This fixes a crash observed consistently with a little perl script to simulate concurrent sessions. Thanks MALLOC_OPTIONS='SFG<<'

/*
 * Copyright (c) 2013 Sunil Nimmagadda <sunil@nimmagadda.net>
 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>

#include <openssl/ssl.h>
#include <openssl/engine.h>
#include <openssl/err.h>

#include "pop3d.h"
#include "ssl.h"

#define SSL_CIPHERS		"HIGH"
#define SSL_SESSION_TIMEOUT	300
#define CERTFILE		"/etc/ssl/server.crt"
#define KEYFILE			"/etc/ssl/private/server.key"

static char *ssl_load_file(const char *, off_t *);

void
ssl_init(void)
{
	/* SSL init */
	SSL_library_init();
	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();

	/* Init hardware cryto engines. */
	ENGINE_load_builtin_engines();
	ENGINE_register_all_complete();
}

void *
ssl_setup(void)
{
	SSL_CTX *ctx = NULL;
	char	*cert, *key;
	off_t	cert_len, key_len;

	/* SSL context creation */
	ctx = SSL_CTX_new(SSLv23_server_method());
	if (ctx == NULL) {
		ssl_error("ssl_ctx_create");
		fatal("ssl_ctx_create: could not create SSL context");
	}

	SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
	SSL_CTX_set_timeout(ctx, SSL_SESSION_TIMEOUT);
	SSL_CTX_set_options(ctx,
	    SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET);
	SSL_CTX_set_options(ctx,
	    SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);

	/* SSL certificate, key loading */
	cert = ssl_load_file(CERTFILE, &cert_len);
	if (cert == NULL)
		fatal("ssl_load_file: Unable to load " CERTFILE);

	key = ssl_load_file(KEYFILE, &key_len);
	if (key == NULL)
		fatal("ssl_load_file: Unable to load " KEYFILE);

	if (!SSL_CTX_set_cipher_list(ctx, SSL_CIPHERS))
		goto err;

	if (!ssl_ctx_use_certificate_chain(ctx, cert, cert_len))
		goto err;

	else if (!ssl_ctx_use_private_key(ctx, key, key_len))
		goto err;

	else if (!SSL_CTX_check_private_key(ctx))
		goto err;

	return (ctx);

err:
	if (ctx != NULL)
		SSL_CTX_free(ctx);
	ssl_error("ssl_setup");
	fatal("ssl_setup: cannot set SSL up");
	return (NULL);
}

void *
pop3s_init(SSL_CTX *ctx, int fd)
{
	SSL *ssl;

	if ((ssl = SSL_new(ctx)) == NULL)
		fatal("SSL_new");

	if (SSL_set_fd(ssl, fd) == 0)
		fatal("SSL_set_fd");

	return (ssl);
}

static char *
ssl_load_file(const char *name, off_t *len)
{
	struct stat	st;
	off_t		size;
	char		*buf = NULL;
	int		fd;

	if ((fd = open(name, O_RDONLY)) == -1)
		return (NULL);

	if (fstat(fd, &st) != 0)
		goto fail;

	size = st.st_size;
	if ((buf = calloc(1, size + 1)) == NULL)
		goto fail;
	if (read(fd, buf, size) != size)
		goto fail;

	close(fd);

	*len = size;
	return (buf);

fail:
	if (buf != NULL)
		free(buf);

	close(fd);
	return (NULL);
}

void
ssl_error(const char *where)
{
	unsigned long	code;
	char		errbuf[128];
	extern int	debug;

	if (!debug)
		return;

	for (; (code = ERR_get_error()) != 0 ;) {
		ERR_error_string_n(code, errbuf, sizeof(errbuf));
		logit(LOG_DEBUG, "SSL library error: %s: %s", where, errbuf);
	}
}