main.c revision 5a250816ffc4cc5db203f9410ea99b6601c7b91a
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "login-common.h"
def516ea503a60f20d510c14d5070b7ff5bbddf4Timo Sirainen#include "ioloop.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "randgen.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include "process-title.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include "restrict-access.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "restrict-process-size.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "master-auth.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "master-service.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "master-interface.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "client-common.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "access-lookup.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "anvil-client.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "auth-client.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "ssl-proxy.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include "login-proxy.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include <stdlib.h>
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include <unistd.h>
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#include <syslog.h>
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#define DEFAULT_LOGIN_SOCKET "login"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen#define AUTH_CLIENT_IDLE_TIMEOUT_MSECS (1000*60)
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstruct login_access_lookup {
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen struct master_service_connection conn;
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen struct io *io;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen char **sockets, **next_socket;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen struct access_lookup *access;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen};
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainenconst struct login_binary *login_binary;
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainenstruct auth_client *auth_client;
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainenstruct master_auth *master_auth;
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainenbool closing_down;
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstruct anvil_client *anvil;
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainen
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainenconst struct login_settings *global_login_settings;
a04cd96888653891272a512f7735121193af7b35Timo Sirainenvoid **global_other_settings;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstatic struct timeout *auth_client_to;
4525c4a8f8d1a6365e4469c0c8f46575400a9a67Timo Sirainenstatic bool shutting_down = FALSE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainenstatic bool ssl_connections = FALSE;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstatic void login_access_lookup_next(struct login_access_lookup *lookup);
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenvoid login_refresh_proctitle(void)
a04cd96888653891272a512f7735121193af7b35Timo Sirainen{
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen struct client *client = clients;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen const char *addr;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (!global_login_settings->verbose_proctitle)
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (clients_get_count() == 0) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen process_title_set("");
a04cd96888653891272a512f7735121193af7b35Timo Sirainen } else if (clients_get_count() > 1 || client == NULL) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen process_title_set(t_strdup_printf("[%u connections (%u TLS)]",
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen clients_get_count(), ssl_proxy_get_count()));
a04cd96888653891272a512f7735121193af7b35Timo Sirainen } else if ((addr = net_ip2addr(&client->ip)) != NULL) {
a04cd96888653891272a512f7735121193af7b35Timo Sirainen process_title_set(t_strdup_printf(client->tls ?
a04cd96888653891272a512f7735121193af7b35Timo Sirainen "[%s TLS]" : "[%s]", addr));
a04cd96888653891272a512f7735121193af7b35Timo Sirainen } else {
a04cd96888653891272a512f7735121193af7b35Timo Sirainen process_title_set(client->tls ? "[TLS]" : "");
a04cd96888653891272a512f7735121193af7b35Timo Sirainen }
a04cd96888653891272a512f7735121193af7b35Timo Sirainen}
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstatic void auth_client_idle_timeout(struct auth_client *auth_client)
a04cd96888653891272a512f7735121193af7b35Timo Sirainen{
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen auth_client_disconnect(auth_client);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen timeout_remove(&auth_client_to);
a04cd96888653891272a512f7735121193af7b35Timo Sirainen}
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainenvoid login_client_destroyed(void)
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen{
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (clients == NULL && auth_client_to == NULL) {
a04cd96888653891272a512f7735121193af7b35Timo Sirainen auth_client_to = timeout_add(AUTH_CLIENT_IDLE_TIMEOUT_MSECS,
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen auth_client_idle_timeout,
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen auth_client);
a04cd96888653891272a512f7735121193af7b35Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen}
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic void login_die(void)
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen{
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen shutting_down = TRUE;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen login_proxy_kill_idle();
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen if (!auth_client_is_connected(auth_client)) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen /* we don't have auth client, and we might never get one */
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen clients_destroy_all();
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen}
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainenstatic void
55a7410569737197afb302b07b488973324b0cc5Timo Sirainenclient_connected_finish(const struct master_service_connection *conn)
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen{
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen struct client *client;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen struct ssl_proxy *proxy;
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen struct ip_addr local_ip;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen const struct login_settings *set;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen unsigned int local_port;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen pool_t pool;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen int fd_ssl;
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen void **other_sets;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (net_getsockname(conn->fd, &local_ip, &local_port) < 0) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen memset(&local_ip, 0, sizeof(local_ip));
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen local_port = 0;
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen pool = pool_alloconly_create("login client", 5*1024);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen set = login_settings_read(pool, &local_ip,
7c7a364a72d4edd1701df72fee835c09db19d933Timo Sirainen &conn->remote_ip, NULL, &other_sets);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (!ssl_connections && !conn->ssl) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen client = client_create(conn->fd, FALSE, pool, set, other_sets,
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen &local_ip, &conn->remote_ip);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen } else {
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen fd_ssl = ssl_proxy_alloc(conn->fd, &conn->remote_ip, set,
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen &proxy);
fd4632d0060b2e9eef513b544ccff1e26d1fc222Timo Sirainen if (fd_ssl == -1) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen net_disconnect(conn->fd);
4525c4a8f8d1a6365e4469c0c8f46575400a9a67Timo Sirainen pool_unref(&pool);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen master_service_client_connection_destroyed(master_service);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen }
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen client = client_create(fd_ssl, TRUE, pool, set, other_sets,
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen &local_ip, &conn->remote_ip);
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen client->ssl_proxy = proxy;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen ssl_proxy_set_client(proxy, client);
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen ssl_proxy_start(proxy);
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen client->remote_port = conn->remote_port;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen client->local_port = local_port;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen if (auth_client_to != NULL)
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen timeout_remove(&auth_client_to);
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen}
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
static void login_access_lookup_free(struct login_access_lookup *lookup)
{
if (lookup->io != NULL)
io_remove(&lookup->io);
if (lookup->access != NULL)
access_lookup_destroy(&lookup->access);
if (lookup->conn.fd != -1) {
if (close(lookup->conn.fd) < 0)
i_error("close(client) failed: %m");
master_service_client_connection_destroyed(master_service);
}
p_strsplit_free(default_pool, lookup->sockets);
i_free(lookup);
}
static void login_access_callback(bool success, void *context)
{
struct login_access_lookup *lookup = context;
if (!success) {
i_info("access(%s): Client refused (rip=%s)",
*lookup->next_socket,
net_ip2addr(&lookup->conn.remote_ip));
login_access_lookup_free(lookup);
} else {
lookup->next_socket++;
login_access_lookup_next(lookup);
}
}
static void login_access_lookup_next(struct login_access_lookup *lookup)
{
if (*lookup->next_socket == NULL) {
/* last one */
if (lookup->io != NULL)
io_remove(&lookup->io);
client_connected_finish(&lookup->conn);
lookup->conn.fd = -1;
login_access_lookup_free(lookup);
return;
}
lookup->access = access_lookup(*lookup->next_socket, lookup->conn.fd,
login_binary->protocol,
login_access_callback, lookup);
if (lookup->access == NULL)
login_access_lookup_free(lookup);
}
static void client_input_error(struct login_access_lookup *lookup)
{
char c;
int ret;
ret = recv(lookup->conn.fd, &c, 1, MSG_PEEK);
if (ret <= 0) {
i_info("access(%s): Client disconnected during lookup (rip=%s)",
*lookup->next_socket,
net_ip2addr(&lookup->conn.remote_ip));
login_access_lookup_free(lookup);
} else {
/* actual input. stop listening until lookup is done. */
io_remove(&lookup->io);
}
}
static void client_connected(struct master_service_connection *conn)
{
const char *access_sockets =
global_login_settings->login_access_sockets;
struct login_access_lookup *lookup;
master_service_client_connection_accept(conn);
/* make sure we're connected (or attempting to connect) to auth */
auth_client_connect(auth_client);
if (*access_sockets == '\0') {
/* no access checks */
client_connected_finish(conn);
return;
}
lookup = i_new(struct login_access_lookup, 1);
lookup->conn = *conn;
lookup->io = io_add(conn->fd, IO_READ, client_input_error, lookup);
lookup->sockets = p_strsplit_spaces(default_pool, access_sockets, " ");
lookup->next_socket = lookup->sockets;
login_access_lookup_next(lookup);
}
static void auth_connect_notify(struct auth_client *client ATTR_UNUSED,
bool connected, void *context ATTR_UNUSED)
{
if (connected)
clients_notify_auth_connected();
else if (shutting_down)
clients_destroy_all();
}
static bool anvil_reconnect_callback(void)
{
master_service_stop_new_connections(master_service);
return FALSE;
}
static void main_preinit(bool allow_core_dumps)
{
unsigned int max_fds;
random_init();
/* Initialize SSL proxy so it can read certificate and private
key file. */
ssl_proxy_init();
/* set the number of fds we want to use. it may get increased or
decreased. leave a couple of extra fds for auth sockets and such.
worst case each connection can use:
- 1 for client
- 1 for login proxy
- 2 for client-side ssl proxy
- 2 for server-side ssl proxy (with login proxy)
*/
max_fds = MASTER_LISTEN_FD_FIRST + 16 +
master_service_get_socket_count(master_service) +
master_service_get_client_limit(master_service)*6;
restrict_fd_limit(max_fds);
io_loop_set_max_fd_count(current_ioloop, max_fds);
i_assert(strcmp(global_login_settings->ssl, "no") == 0 ||
ssl_initialized);
if (global_login_settings->mail_max_userip_connections > 0) {
anvil = anvil_client_init("anvil", anvil_reconnect_callback, 0);
if (anvil_client_connect(anvil, TRUE) < 0)
i_fatal("Couldn't connect to anvil");
}
restrict_access_by_env(NULL, TRUE);
if (allow_core_dumps)
restrict_access_allow_coredumps(TRUE);
}
static void main_init(const char *login_socket)
{
/* make sure we can't fork() */
restrict_process_size((unsigned int)-1, 1);
if (restrict_access_get_current_chroot() == NULL) {
if (chdir("login") < 0)
i_fatal("chdir(login) failed: %m");
}
master_service_set_avail_overflow_callback(master_service,
client_destroy_oldest);
master_service_set_die_callback(master_service, login_die);
auth_client = auth_client_init(login_socket, (unsigned int)getpid(),
FALSE);
auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
master_auth = master_auth_init(master_service, login_binary->protocol);
login_binary->init();
login_proxy_init("proxy-notify");
}
static void main_deinit(void)
{
ssl_proxy_deinit();
login_proxy_deinit();
login_binary->deinit();
auth_client_deinit(&auth_client);
master_auth_deinit(&master_auth);
if (anvil != NULL)
anvil_client_deinit(&anvil);
if (auth_client_to != NULL)
timeout_remove(&auth_client_to);
login_settings_deinit();
}
int login_binary_run(const struct login_binary *binary,
int argc, char *argv[])
{
enum master_service_flags service_flags =
MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN |
MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE;
pool_t set_pool;
bool allow_core_dumps = FALSE;
const char *login_socket = DEFAULT_LOGIN_SOCKET;
int c;
login_binary = binary;
master_service = master_service_init(login_binary->process_name,
service_flags, &argc, &argv, "DS");
master_service_init_log(master_service, t_strconcat(
login_binary->process_name, ": ", NULL));
while ((c = master_getopt(master_service)) > 0) {
switch (c) {
case 'D':
allow_core_dumps = TRUE;
break;
case 'S':
ssl_connections = TRUE;
break;
default:
return FATAL_DEFAULT;
}
}
if (argv[optind] != NULL)
login_socket = argv[optind];
login_binary->preinit();
set_pool = pool_alloconly_create("global login settings", 4096);
global_login_settings =
login_settings_read(set_pool, NULL, NULL, NULL,
&global_other_settings);
/* main_preinit() needs to know the client limit, which is set by
this. so call it first. */
master_service_init_finish(master_service);
main_preinit(allow_core_dumps);
main_init(login_socket);
master_service_run(master_service, client_connected);
main_deinit();
pool_unref(&set_pool);
master_service_deinit(&master_service);
return 0;
}