summaryrefslogtreecommitdiffstats
path: root/src/auth/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth/main.c')
-rw-r--r--src/auth/main.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/auth/main.c b/src/auth/main.c
new file mode 100644
index 0000000..5f09fca
--- /dev/null
+++ b/src/auth/main.c
@@ -0,0 +1,398 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+#include "auth-common.h"
+#include "array.h"
+#include "ioloop.h"
+#include "net.h"
+#include "lib-signals.h"
+#include "restrict-access.h"
+#include "child-wait.h"
+#include "sql-api.h"
+#include "module-dir.h"
+#include "randgen.h"
+#include "process-title.h"
+#include "settings-parser.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "master-interface.h"
+#include "dict.h"
+#include "password-scheme.h"
+#include "passdb-cache.h"
+#include "mech.h"
+#include "otp.h"
+#include "mech-otp-common.h"
+#include "auth.h"
+#include "auth-penalty.h"
+#include "auth-token.h"
+#include "auth-request-handler.h"
+#include "auth-request-stats.h"
+#include "auth-worker-server.h"
+#include "auth-worker-client.h"
+#include "auth-master-connection.h"
+#include "auth-client-connection.h"
+#include "auth-policy.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define AUTH_PENALTY_ANVIL_PATH "anvil-auth-penalty"
+
+enum auth_socket_type {
+ AUTH_SOCKET_UNKNOWN = 0,
+ AUTH_SOCKET_CLIENT,
+ AUTH_SOCKET_LOGIN_CLIENT,
+ AUTH_SOCKET_MASTER,
+ AUTH_SOCKET_USERDB,
+ AUTH_SOCKET_TOKEN,
+ AUTH_SOCKET_TOKEN_LOGIN
+};
+
+struct auth_socket_listener {
+ enum auth_socket_type type;
+ struct stat st;
+ char *path;
+};
+
+bool worker = FALSE, worker_restart_request = FALSE;
+time_t process_start_time;
+struct auth_penalty *auth_penalty;
+
+static pool_t auth_set_pool;
+static struct module *modules = NULL;
+static struct mechanisms_register *mech_reg;
+static ARRAY(struct auth_socket_listener) listeners;
+
+void auth_refresh_proctitle(void)
+{
+ if (!global_auth_settings->verbose_proctitle || worker)
+ return;
+
+ process_title_set(t_strdup_printf(
+ "[%u wait, %u passdb, %u userdb]",
+ auth_request_state_count[AUTH_REQUEST_STATE_NEW] +
+ auth_request_state_count[AUTH_REQUEST_STATE_MECH_CONTINUE] +
+ auth_request_state_count[AUTH_REQUEST_STATE_FINISHED],
+ auth_request_state_count[AUTH_REQUEST_STATE_PASSDB],
+ auth_request_state_count[AUTH_REQUEST_STATE_USERDB]));
+}
+
+static const char *const *read_global_settings(void)
+{
+ struct master_service_settings_output set_output;
+ const char **services;
+ unsigned int i, count;
+
+ auth_set_pool = pool_alloconly_create("auth settings", 8192);
+ global_auth_settings =
+ auth_settings_read(NULL, auth_set_pool, &set_output);
+
+ /* strdup() the service names, because they're allocated from
+ set parser pool, and we'll later clear it. */
+ count = str_array_length(set_output.specific_services);
+ services = p_new(auth_set_pool, const char *, count + 1);
+ for (i = 0; i < count; i++) {
+ services[i] = p_strdup(auth_set_pool,
+ set_output.specific_services[i]);
+ }
+ return services;
+}
+
+static enum auth_socket_type
+auth_socket_type_get(const char *path)
+{
+ const char *name, *suffix;
+
+ name = strrchr(path, '/');
+ if (name == NULL)
+ name = path;
+ else
+ name++;
+
+ suffix = strrchr(name, '-');
+ if (suffix == NULL)
+ suffix = name;
+ else
+ suffix++;
+
+ if (strcmp(suffix, "login") == 0)
+ return AUTH_SOCKET_LOGIN_CLIENT;
+ else if (strcmp(suffix, "master") == 0)
+ return AUTH_SOCKET_MASTER;
+ else if (strcmp(suffix, "userdb") == 0)
+ return AUTH_SOCKET_USERDB;
+ else if (strcmp(suffix, "token") == 0)
+ return AUTH_SOCKET_TOKEN;
+ else if (strcmp(suffix, "tokenlogin") == 0)
+ return AUTH_SOCKET_TOKEN_LOGIN;
+ else
+ return AUTH_SOCKET_CLIENT;
+}
+
+static void listeners_init(void)
+{
+ unsigned int i, n;
+ const char *path;
+
+ i_array_init(&listeners, 8);
+ n = master_service_get_socket_count(master_service);
+ for (i = 0; i < n; i++) {
+ int fd = MASTER_LISTEN_FD_FIRST + i;
+ struct auth_socket_listener *l;
+
+ l = array_idx_get_space(&listeners, fd);
+ if (net_getunixname(fd, &path) < 0) {
+ if (errno != ENOTSOCK)
+ i_fatal("getunixname(%d) failed: %m", fd);
+ /* not a unix socket, set its name and type lazily */
+ } else {
+ l->type = auth_socket_type_get(path);
+ l->path = i_strdup(path);
+ if (l->type == AUTH_SOCKET_USERDB) {
+ if (stat(path, &l->st) < 0)
+ i_error("stat(%s) failed: %m", path);
+ }
+ }
+ }
+}
+
+static bool auth_module_filter(const char *name, void *context ATTR_UNUSED)
+{
+ if (str_begins(name, "authdb_") ||
+ str_begins(name, "mech_")) {
+ /* this is lazily loaded */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void main_preinit(void)
+{
+ struct module_dir_load_settings mod_set;
+ const char *const *services;
+
+ /* Load built-in SQL drivers (if any) */
+ sql_drivers_init();
+ sql_drivers_register_all();
+
+ /* Initialize databases so their configuration files can be readable
+ only by root. Also load all modules here. */
+ passdbs_init();
+ userdbs_init();
+ /* init schemes before plugins are loaded */
+ password_schemes_init();
+
+ services = read_global_settings();
+
+ i_zero(&mod_set);
+ mod_set.abi_version = DOVECOT_ABI_VERSION;
+ mod_set.require_init_funcs = TRUE;
+ mod_set.debug = global_auth_settings->debug;
+ mod_set.filter_callback = auth_module_filter;
+
+ modules = module_dir_load(AUTH_MODULE_DIR, NULL, &mod_set);
+ module_dir_init(modules);
+
+ if (!worker)
+ auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
+ auth_request_stats_init();
+ mech_init(global_auth_settings);
+ mech_reg = mech_register_init(global_auth_settings);
+ dict_drivers_register_builtin();
+ auths_preinit(global_auth_settings, auth_set_pool,
+ mech_reg, services);
+
+ listeners_init();
+ if (!worker)
+ auth_token_init();
+
+ /* Password lookups etc. may require roots, allow it. */
+ restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL);
+ restrict_access_allow_coredumps(TRUE);
+}
+
+void auth_module_load(const char *names)
+{
+ struct module_dir_load_settings mod_set;
+
+ i_zero(&mod_set);
+ mod_set.abi_version = DOVECOT_ABI_VERSION;
+ mod_set.require_init_funcs = TRUE;
+ mod_set.debug = global_auth_settings->debug;
+ mod_set.ignore_missing = TRUE;
+
+ modules = module_dir_load_missing(modules, AUTH_MODULE_DIR, names,
+ &mod_set);
+ module_dir_init(modules);
+}
+
+static void main_init(void)
+{
+ process_start_time = ioloop_time;
+
+ /* If auth caches aren't used, just ignore these signals */
+ lib_signals_ignore(SIGHUP, TRUE);
+ lib_signals_ignore(SIGUSR2, TRUE);
+
+ /* set proctitles before init()s, since they may set them to error */
+ auth_refresh_proctitle();
+ auth_worker_refresh_proctitle("");
+
+ child_wait_init();
+ auth_worker_server_init();
+ auths_init();
+ auth_request_handler_init();
+ auth_policy_init();
+
+ if (worker) {
+ /* workers have only a single connection from the master
+ auth process */
+ master_service_set_client_limit(master_service, 1);
+ auth_worker_set_max_service_count(
+ master_service_get_service_count(master_service));
+ /* make sure this process cycles if auth connection drops */
+ master_service_set_service_count(master_service, 1);
+ } else {
+ /* caching is handled only by the main auth process */
+ passdb_cache_init(global_auth_settings);
+ }
+}
+
+static void main_deinit(void)
+{
+ struct auth_socket_listener *l;
+
+ if (auth_penalty != NULL) {
+ /* cancel all pending anvil penalty lookups */
+ auth_penalty_deinit(&auth_penalty);
+ }
+ /* deinit auth workers, which aborts pending requests */
+ auth_worker_server_deinit();
+ /* deinit passdbs and userdbs. it aborts any pending async requests. */
+ auths_deinit();
+ /* flush pending requests */
+ auth_request_handler_deinit();
+ /* there are no more auth requests */
+ auths_free();
+ dict_drivers_unregister_builtin();
+
+ auth_token_deinit();
+
+ auth_client_connections_destroy_all();
+ auth_master_connections_destroy_all();
+ auth_worker_connections_destroy_all();
+
+ auth_policy_deinit();
+ mech_register_deinit(&mech_reg);
+ mech_otp_deinit();
+ mech_deinit(global_auth_settings);
+
+ /* allow modules to unregister their dbs/drivers/etc. before freeing
+ the whole data structures containing them. */
+ module_dir_unload(&modules);
+
+ userdbs_deinit();
+ passdbs_deinit();
+ passdb_cache_deinit();
+ password_schemes_deinit();
+ auth_request_stats_deinit();
+
+ sql_drivers_deinit();
+ child_wait_deinit();
+
+ array_foreach_modifiable(&listeners, l)
+ i_free(l->path);
+ array_free(&listeners);
+ pool_unref(&auth_set_pool);
+}
+
+static void worker_connected(struct master_service_connection *conn)
+{
+ if (auth_worker_has_client()) {
+ i_error("Auth workers can handle only a single client");
+ return;
+ }
+
+ master_service_client_connection_accept(conn);
+ (void)auth_worker_client_create(auth_default_service(), conn);
+}
+
+static void client_connected(struct master_service_connection *conn)
+{
+ struct auth_socket_listener *l;
+ struct auth *auth;
+
+ l = array_idx_modifiable(&listeners, conn->listen_fd);
+ if (l->type == AUTH_SOCKET_UNKNOWN) {
+ /* first connection from inet socket, figure out its type
+ from the listener name */
+ l->type = auth_socket_type_get(conn->name);
+ l->path = i_strdup(conn->name);
+ }
+ auth = auth_default_service();
+ switch (l->type) {
+ case AUTH_SOCKET_MASTER:
+ (void)auth_master_connection_create(auth, conn->fd,
+ l->path, NULL, FALSE);
+ break;
+ case AUTH_SOCKET_USERDB:
+ (void)auth_master_connection_create(auth, conn->fd,
+ l->path, &l->st, TRUE);
+ break;
+ case AUTH_SOCKET_LOGIN_CLIENT:
+ auth_client_connection_create(auth, conn->fd, TRUE, FALSE);
+ break;
+ case AUTH_SOCKET_CLIENT:
+ auth_client_connection_create(auth, conn->fd, FALSE, FALSE);
+ break;
+ case AUTH_SOCKET_TOKEN_LOGIN:
+ auth_client_connection_create(auth, conn->fd, TRUE, TRUE);
+ break;
+ case AUTH_SOCKET_TOKEN:
+ auth_client_connection_create(auth, conn->fd, FALSE, TRUE);
+ break;
+ default:
+ i_unreached();
+ }
+ master_service_client_connection_accept(conn);
+}
+
+static void auth_die(void)
+{
+ if (!worker) {
+ /* do nothing. auth clients should disconnect soon. */
+ } else {
+ /* ask auth master to disconnect us */
+ auth_worker_client_send_shutdown();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ enum master_service_flags service_flags =
+ MASTER_SERVICE_FLAG_NO_SSL_INIT;
+
+ master_service = master_service_init("auth", service_flags, &argc, &argv, "w");
+ master_service_init_log(master_service);
+
+ while ((c = master_getopt(master_service)) > 0) {
+ switch (c) {
+ case 'w':
+ master_service_init_log_with_pid(master_service);
+ worker = TRUE;
+ break;
+ default:
+ return FATAL_DEFAULT;
+ }
+ }
+
+ main_preinit();
+ master_service_set_die_callback(master_service, auth_die);
+ main_init();
+ master_service_init_finish(master_service);
+ master_service_run(master_service, worker ? worker_connected :
+ client_connected);
+ main_deinit();
+ master_service_deinit(&master_service);
+ return 0;
+}