#include <sys_defs.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#ifdef HAS_SSL
#include <openssl/rand.h>
#endif
#include <msg.h>
#include <events.h>
#include <dict.h>
#include <stringops.h>
#include <mymalloc.h>
#include <connect.h>
#include <myflock.h>
#include <mail_conf.h>
#include <mail_params.h>
#include <pfixtls.h>
#include <master_proto.h>
#include <mail_server.h>
char *var_tls_rand_source;
int var_tls_rand_bytes;
int var_tls_reseed_period;
int var_tls_prng_upd_period;
static int rand_exch_fd;
static int rand_source_dev_fd = -1;
static int rand_source_socket_fd = -1;
static int srvr_scache_db_active;
static int clnt_scache_db_active;
static DICT *srvr_scache_db = NULL;
static DICT *clnt_scache_db = NULL;
static void tlsmgr_prng_upd_event(int unused_event, char *dummy)
{
struct timeval tv;
unsigned char buffer[1024];
int next_period;
#ifdef HAS_SSL
GETTIMEOFDAY(&tv);
RAND_seed(&tv, sizeof(struct timeval));
if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) != 0)
msg_fatal("Could not lock random exchange file: %s",
strerror(errno));
lseek(rand_exch_fd, 0, SEEK_SET);
if (read(rand_exch_fd, buffer, 1024) < 0)
msg_fatal("reading exchange file failed");
RAND_seed(buffer, 1024);
RAND_bytes(buffer, 1024);
lseek(rand_exch_fd, 0, SEEK_SET);
if (write(rand_exch_fd, buffer, 1024) != 1024)
msg_fatal("Writing exchange file failed");
if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) != 0)
msg_fatal("Could not unlock random exchange file: %s",
strerror(errno));
next_period = (var_tls_prng_upd_period * buffer[0]) / 255;
event_request_timer(tlsmgr_prng_upd_event, dummy, next_period);
#endif
}
static void tlsmgr_reseed_event(int unused_event, char *dummy)
{
int egd_success;
int next_period;
int rand_bytes;
char buffer[255];
struct timeval tv;
unsigned char randbyte;
#ifdef HAS_SSL
GETTIMEOFDAY(&tv);
RAND_seed(&tv, sizeof(struct timeval));
if (rand_source_dev_fd != -1) {
rand_bytes = read(rand_source_dev_fd, buffer, var_tls_rand_bytes);
if (rand_bytes > 0)
RAND_seed(buffer, rand_bytes);
else if (rand_bytes < 0) {
msg_fatal("Read from entropy device %s failed",
var_tls_rand_source);
}
} else if (rand_source_socket_fd != -1) {
egd_success = 0;
buffer[0] = 1;
buffer[1] = var_tls_rand_bytes;
if (write(rand_source_socket_fd, buffer, 2) != 2)
msg_info("Could not talk to %s", var_tls_rand_source);
else if (read(rand_source_socket_fd, buffer, 1) != 1)
msg_info("Could not read info from %s", var_tls_rand_source);
else {
rand_bytes = buffer[0];
if (read(rand_source_socket_fd, buffer, rand_bytes) != rand_bytes)
msg_info("Could not read data from %s", var_tls_rand_source);
else {
egd_success = 1;
RAND_seed(buffer, rand_bytes);
}
}
if (!egd_success) {
msg_info("Lost connection to EGD-device, exiting to reconnect.");
exit(0);
}
} else if (*var_tls_rand_source) {
rand_bytes = RAND_load_file(var_tls_rand_source, var_tls_rand_bytes);
}
RAND_bytes(&randbyte, 1);
next_period = (var_tls_reseed_period * randbyte) / 255;
event_request_timer(tlsmgr_reseed_event, dummy, next_period);
#endif
}
static int tlsmgr_do_scache_check(DICT *scache_db, int scache_timeout,
int start)
{
#ifdef HAS_SSL
int func;
int len;
int n;
int delete = 0;
int result;
struct timeval tv;
const char *member;
const char *value;
char *member_copy;
unsigned char nibble, *data;
pfixtls_scache_info_t scache_info;
GETTIMEOFDAY(&tv);
RAND_seed(&tv, sizeof(struct timeval));
if (start)
func = DICT_SEQ_FUN_FIRST;
else
func = DICT_SEQ_FUN_NEXT;
result = dict_seq(scache_db, func, &member, &value);
if (result > 0)
return 0;
else if (result < 0)
msg_fatal("Database fault, should already be caught.");
else {
member_copy = mystrdup(member);
len = strlen(value);
RAND_seed(value, len);
if (len < 2 * sizeof(pfixtls_scache_info_t))
delete = 1;
else if (len > 2 * sizeof(pfixtls_scache_info_t))
len = 2 * sizeof(pfixtls_scache_info_t);
if (!delete) {
data = (unsigned char *)(&scache_info);
memset(data, 0, len / 2);
for (n = 0; n < len; n++) {
if ((value[n] >= '0') && (value[n] <= '9'))
nibble = value[n] - '0';
else
nibble = value[n] - 'A' + 10;
if (n % 2)
data[n / 2] |= nibble;
else
data[n / 2] |= (nibble << 4);
}
if ((scache_info.scache_db_version != scache_db_version) ||
(scache_info.openssl_version != openssl_version) ||
(scache_info.timestamp + scache_timeout < time(NULL)))
delete = 1;
}
if (delete)
result = dict_del(scache_db, member_copy);
myfree(member_copy);
}
if (delete && result)
msg_info("Could not delete %s", member);
return 1;
#else
return 0;
#endif
}
static void tlsmgr_clnt_cache_run_event(int unused_event, char *dummy)
{
clnt_scache_db_active = tlsmgr_do_scache_check(clnt_scache_db,
var_smtp_tls_scache_timeout, 1);
event_request_timer(tlsmgr_clnt_cache_run_event, dummy,
var_smtp_tls_scache_timeout);
}
static void tlsmgr_srvr_cache_run_event(int unused_event, char *dummy)
{
srvr_scache_db_active = tlsmgr_do_scache_check(srvr_scache_db,
var_smtpd_tls_scache_timeout, 1);
event_request_timer(tlsmgr_srvr_cache_run_event, dummy,
var_smtpd_tls_scache_timeout);
}
static DICT *tlsmgr_cache_open(const char *dbname)
{
DICT *retval;
char *dbpagname;
char *dbdirname;
if (!strncmp(dbname, "sdbm:", 5)) {
dbpagname = concatenate(dbname + 5, ".pag", NULL);
REMOVE(dbpagname);
myfree(dbpagname);
dbdirname = concatenate(dbname + 5, ".dir", NULL);
REMOVE(dbdirname);
myfree(dbdirname);
}
else {
msg_warn("Only type sdbm: supported: %s", dbname);
return NULL;
}
retval = dict_open(dbname, O_RDWR | O_CREAT | O_EXCL,
DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE);
if (!retval)
msg_warn("Could not create dictionary %s", dbname);
return retval;
}
static void tlsmgr_trigger_event(char *buf, int len,
char *unused_service, char **argv)
{
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
}
static int tlsmgr_loop(char *unused_name, char **unused_argv)
{
#define DONT_WAIT 0
#define WAIT_FOR_EVENT (-1)
if (clnt_scache_db_active)
clnt_scache_db_active = tlsmgr_do_scache_check(clnt_scache_db,
var_smtp_tls_scache_timeout, 0);
if (srvr_scache_db_active)
srvr_scache_db_active = tlsmgr_do_scache_check(srvr_scache_db,
var_smtpd_tls_scache_timeout, 0);
if (clnt_scache_db_active || srvr_scache_db_active)
return (DONT_WAIT);
return (WAIT_FOR_EVENT);
}
static void pre_accept(char *unused_name, char **unused_argv)
{
if (dict_changed()) {
msg_info("table has changed -- exiting");
exit(0);
}
}
static void tlsmgr_pre_init(char *unused_name, char **unused_argv)
{
int rand_bytes;
unsigned char buffer[255];
#ifdef HAS_SSL
if (*var_tls_rand_source) {
if (!strncmp(var_tls_rand_source, "dev:", 4)) {
rand_source_dev_fd = open(var_tls_rand_source + 4, 0, 0);
if (rand_source_dev_fd == -1)
msg_fatal("Could not open entropy device %s",
var_tls_rand_source);
if (var_tls_rand_bytes > 255)
var_tls_rand_bytes = 255;
rand_bytes = read(rand_source_dev_fd, buffer, var_tls_rand_bytes);
RAND_seed(buffer, rand_bytes);
} else if (!strncmp(var_tls_rand_source, "egd:", 4)) {
rand_source_socket_fd = unix_connect(var_tls_rand_source +4,
BLOCKING, 10);
if (rand_source_socket_fd == -1)
msg_fatal("Could not connect to %s", var_tls_rand_source);
if (var_tls_rand_bytes > 255)
var_tls_rand_bytes = 255;
buffer[0] = 1;
buffer[1] = var_tls_rand_bytes;
if (write(rand_source_socket_fd, buffer, 2) != 2)
msg_fatal("Could not talk to %s", var_tls_rand_source);
if (read(rand_source_socket_fd, buffer, 1) != 1)
msg_fatal("Could not read info from %s", var_tls_rand_source);
rand_bytes = buffer[0];
if (read(rand_source_socket_fd, buffer, rand_bytes) != rand_bytes)
msg_fatal("Could not read data from %s", var_tls_rand_source);
RAND_seed(buffer, rand_bytes);
} else {
rand_bytes = RAND_load_file(var_tls_rand_source,
var_tls_rand_bytes);
}
}
#endif
if (*var_tls_rand_exch_name) {
rand_exch_fd = open(var_tls_rand_exch_name, O_RDWR | O_CREAT, 0600);
}
if (*var_smtp_tls_scache_db)
clnt_scache_db = tlsmgr_cache_open(var_smtp_tls_scache_db);
if (*var_smtpd_tls_scache_db)
srvr_scache_db = tlsmgr_cache_open(var_smtpd_tls_scache_db);
}
static void tlsmgr_post_init(char *unused_name, char **unused_argv)
{
unsigned char buffer[1024];
var_use_limit = 0;
var_idle_limit = 0;
#ifdef HAS_SSL
if (rand_exch_fd >= 0) {
if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) == -1)
msg_fatal("Could not lock random exchange file: %s",
strerror(errno));
read(rand_exch_fd, buffer, 1024);
if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) == -1)
msg_fatal("Could not unlock random exchange file: %s",
strerror(errno));
RAND_seed(buffer, 1024);
tlsmgr_prng_upd_event(0, (char *) 0);
tlsmgr_reseed_event(0, (char *) 0);
}
#endif
clnt_scache_db_active = 0;
srvr_scache_db_active = 0;
if (clnt_scache_db)
tlsmgr_clnt_cache_run_event(0, (char *) 0);
if (srvr_scache_db)
tlsmgr_srvr_cache_run_event(0, (char *) 0);
}
int main(int argc, char **argv)
{
static CONFIG_STR_TABLE str_table[] = {
VAR_TLS_RAND_SOURCE, DEF_TLS_RAND_SOURCE, &var_tls_rand_source, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 0, 0,
VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_upd_period, 0, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
VAR_TLS_RAND_BYTES, DEF_TLS_RAND_BYTES, &var_tls_rand_bytes, 0, 0,
0,
};
trigger_server_main(argc, argv, tlsmgr_trigger_event,
MAIL_SERVER_TIME_TABLE, time_table,
MAIL_SERVER_INT_TABLE, int_table,
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_PRE_INIT, tlsmgr_pre_init,
MAIL_SERVER_POST_INIT, tlsmgr_post_init,
MAIL_SERVER_LOOP, tlsmgr_loop,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}