view src/login-common/main.c @ 2027:dc5d0da1abe9 HEAD

Added ssl_require_client_cert auth-specific setting. Hide ssl_verify_client_cert from default config file as it's automatically set if needed and there's not much point in forcing it.
author Timo Sirainen <tss@iki.fi>
date Mon, 17 May 2004 04:32:16 +0300
parents c7c19f5071c3
children 4e77cb0aff21
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "lib-signals.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
#include "process-title.h"
#include "fd-close-on-exec.h"
#include "master.h"
#include "client-common.h"
#include "auth-client.h"
#include "ssl-proxy.h"

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

int disable_plaintext_auth, process_per_connection, verbose_proctitle;
int verbose_ssl;
unsigned int max_logging_users;
unsigned int login_process_uid;
struct auth_client *auth_client;

static struct ioloop *ioloop;
static struct io *io_listen, *io_ssl_listen;
static int main_refcount;
static int is_inetd, closing_down;

void main_ref(void)
{
	main_refcount++;
}

void main_unref(void)
{
	if (--main_refcount == 0) {
		/* nothing to do, quit */
		io_loop_stop(ioloop);
	} else if (closing_down && clients_get_count() == 0) {
		/* last login finished, close all communications
		   to master process */
		master_close();
	}
}

void main_close_listen(void)
{
	if (closing_down)
		return;

	if (io_listen != NULL) {
		if (close(LOGIN_LISTEN_FD) < 0)
			i_fatal("close(listen) failed: %m");

		io_remove(io_listen);
		io_listen = NULL;
	}

	if (io_ssl_listen != NULL) {
		if (close(LOGIN_SSL_LISTEN_FD) < 0)
			i_fatal("close(ssl_listen) failed: %m");

		io_remove(io_ssl_listen);
		io_ssl_listen = NULL;
	}

	closing_down = TRUE;
	master_notify_finished();
}

static void sig_quit(int signo __attr_unused__)
{
	io_loop_stop(ioloop);
}

static void login_accept(void *context __attr_unused__)
{
	struct ip_addr ip;
	int fd;

	fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL);
	if (fd < 0) {
		if (fd < -1)
			i_fatal("accept() failed: %m");
		return;
	}

	if (process_per_connection)
		main_close_listen();

	(void)client_create(fd, &ip, FALSE);
}

static void login_accept_ssl(void *context __attr_unused__)
{
	struct ip_addr ip;
	struct client *client;
	struct ssl_proxy *proxy;
	int fd, fd_ssl;

	fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL);
	if (fd < 0) {
		if (fd < -1)
			i_fatal("accept() failed: %m");
		return;
	}

	if (process_per_connection)
		main_close_listen();

	fd_ssl = ssl_proxy_new(fd, &ip, &proxy);
	if (fd_ssl == -1)
		net_disconnect(fd);
	else {
		client = client_create(fd_ssl, &ip, TRUE);
		client->proxy = proxy;
	}
}

static void auth_connect_notify(struct auth_client *client __attr_unused__,
				int connected, void *context __attr_unused__)
{
	if (connected)
                clients_notify_auth_connected();
}

static void drop_privileges()
{
	i_set_failure_internal();

	/* Initialize SSL proxy so it can read certificate and private
	   key file. */
	ssl_proxy_init();

	/* Refuse to run as root - we should never need it and it's
	   dangerous with SSL. */
	restrict_access_by_env(TRUE);

	/* make sure we can't fork() */
	restrict_process_size((unsigned int)-1, 1);
}

static void main_init(void)
{
	const char *value;

	lib_init_signals(sig_quit);

	disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
	verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;
        verbose_ssl = getenv("VERBOSE_SSL") != NULL;

	value = getenv("MAX_LOGGING_USERS");
	max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10);

	value = getenv("PROCESS_UID");
	if (value == NULL)
		i_fatal("BUG: PROCESS_UID environment not given");
        login_process_uid = strtoul(value, NULL, 10);
	if (login_process_uid == 0)
		i_fatal("BUG: PROCESS_UID environment is 0");

        closing_down = FALSE;
	main_refcount = 0;

	auth_client = auth_client_new((unsigned int)getpid());
        auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
	clients_init();

	io_listen = io_ssl_listen = NULL;

	if (!is_inetd) {
		if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) {
			io_listen = io_add(LOGIN_LISTEN_FD, IO_READ,
					   login_accept, NULL);
		}

		if (net_getsockname(LOGIN_SSL_LISTEN_FD, NULL, NULL) == 0) {
			if (!ssl_initialized) {
				/* this shouldn't happen, master should have
				   disabled the ssl socket.. */
				i_fatal("BUG: SSL initialization parameters "
					"not given while they should have "
					"been");
			}

			io_ssl_listen = io_add(LOGIN_SSL_LISTEN_FD, IO_READ,
					       login_accept_ssl, NULL);
		}

		/* initialize master last - it sends the "we're ok"
		   notification */
		master_init(LOGIN_MASTER_SOCKET_FD, TRUE);
	}
}

static void main_deinit(void)
{
        if (lib_signal_kill != 0)
		i_warning("Killed with signal %d", lib_signal_kill);

	if (io_listen != NULL) io_remove(io_listen);
	if (io_ssl_listen != NULL) io_remove(io_ssl_listen);

	ssl_proxy_deinit();

	auth_client_free(auth_client);
	clients_deinit();
	master_deinit();

	closelog();
}

int main(int argc __attr_unused__, char *argv[], char *envp[])
{
	const char *name, *group_name;
	struct ip_addr ip;
	struct ssl_proxy *proxy = NULL;
	struct client *client;
	int i, fd = -1, master_fd = -1;

	is_inetd = getenv("DOVECOT_MASTER") == NULL;

#ifdef DEBUG
	if (!is_inetd)
		fd_debug_verify_leaks(4, 1024);
#endif
	/* NOTE: we start rooted, so keep the code minimal until
	   restrict_access_by_env() is called */
	lib_init();

	if (is_inetd) {
		/* running from inetd. create master process before
		   dropping privileges. */
		group_name = strrchr(argv[0], '/');
		group_name = group_name == NULL ? argv[0] : group_name+1;
		group_name = t_strcut(group_name, '-');

		for (i = 1; i < argc; i++) {
			if (strncmp(argv[i], "--group=", 8) == 0) {
				group_name = argv[1]+8;
				break;
			}
		}

		master_fd = master_connect(group_name);
	}

	name = strrchr(argv[0], '/');
	drop_privileges();

	process_title_init(argv, envp);
	ioloop = io_loop_create(system_pool);
	main_init();

	if (is_inetd) {
		if (net_getsockname(1, &ip, NULL) < 0) {
			i_fatal("%s can be started only through dovecot "
				"master process, inetd or equilevant", argv[0]);
		}

		fd = 1;
		for (i = 1; i < argc; i++) {
			if (strcmp(argv[i], "--ssl") == 0) {
				fd = ssl_proxy_new(fd, &ip, &proxy);
				if (fd == -1)
					i_fatal("SSL initialization failed");
			} else if (strncmp(argv[i], "--group=", 8) != 0)
				i_fatal("Unknown parameter: %s", argv[i]);
		}

		master_init(master_fd, FALSE);
		closing_down = TRUE;
	}

	if (fd != -1) {
		client = client_create(fd, &ip, TRUE);
		client->proxy = proxy;
	}

	io_loop_run(ioloop);
	main_deinit();

	io_loop_destroy(ioloop);
	lib_deinit();

        return 0;
}