Mercurial > dovecot > original-hg > dovecot-1.2
changeset 613:1906116a62ce HEAD
Finally support for handling each login connection in it's own process.
Enabled by default. Also a few bugfixes to master process.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 16 Nov 2002 07:21:21 +0200 |
parents | 7c91f579ebed |
children | e60620644af3 |
files | dovecot-example.conf src/login/client.c src/login/client.h src/login/common.h src/login/main.c src/login/master.c src/login/master.h src/login/ssl-proxy.c src/master/login-process.c src/master/settings.c src/master/settings.h |
diffstat | 11 files changed, 229 insertions(+), 43 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Sat Nov 16 07:19:03 2002 +0200 +++ b/dovecot-example.conf Sat Nov 16 07:21:21 2002 +0200 @@ -57,11 +57,20 @@ # is if you wish to run the whole imapd without roots. #login_chroot = yes -# Number of imap-login processes to use, one or two is enough -#login_processes_count = 1 +# Should each login be processed in it's own process (yes), or should one +# login process be allowed to process multiple connections (no)? Yes is more +# secure, espcially with SSL/TLS enabled. No is faster since there's no need +# to create processes all the time. +#login_process_per_connection = yes + +# Number of imap-login processes to create. If login_process_per_user is +# yes, this is the number of extra processes waiting for users to log in. +#login_processes_count = 3 # Maximum number of connections allowed in login state. When this limit is -# reached, the oldest connections are dropped. +# reached, the oldest connections are dropped. If login_process_per_user +# is no, this is a per-process value, so the absolute maximum number of users +# logging in actually login_processes_count * max_logging_users. #max_logging_users = 256 ##
--- a/src/login/client.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/client.c Sat Nov 16 07:21:21 2002 +0200 @@ -271,6 +271,8 @@ client->last_input = ioloop_time; hash_insert(clients, client, client); + main_ref(); + client_send_line(client, "* OK " PACKAGE " ready."); return client; } @@ -312,6 +314,8 @@ i_free(client->tag); i_free(client->plain_login); i_free(client); + + main_unref(); return FALSE; } @@ -353,10 +357,9 @@ hash_foreach(clients, client_hash_check_idle, NULL); } -void clients_init(void) +unsigned int clients_get_count(void) { - clients = hash_create(default_pool, 128, NULL, NULL); - to_idle = timeout_add(1000, idle_timeout, NULL); + return hash_size(clients); } static void client_hash_destroy(void *key, void *value __attr_unused__, @@ -365,9 +368,20 @@ client_destroy(key, NULL); } +void clients_destroy_all(void) +{ + hash_foreach(clients, client_hash_destroy, NULL); +} + +void clients_init(void) +{ + clients = hash_create(default_pool, 128, NULL, NULL); + to_idle = timeout_add(1000, idle_timeout, NULL); +} + void clients_deinit(void) { - hash_foreach(clients, client_hash_destroy, NULL); + clients_destroy_all(); hash_destroy(clients); timeout_remove(to_idle);
--- a/src/login/client.h Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/client.h Sat Nov 16 07:21:21 2002 +0200 @@ -37,6 +37,9 @@ int client_read(Client *client); void client_input(void *context, int fd, IO io); +unsigned int clients_get_count(void); +void clients_destroy_all(void); + void clients_init(void); void clients_deinit(void);
--- a/src/login/common.h Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/common.h Sat Nov 16 07:21:21 2002 +0200 @@ -7,8 +7,10 @@ typedef struct _Client Client; typedef struct _AuthRequest AuthRequest; -extern IOLoop ioloop; extern int disable_plaintext_auth; extern unsigned int max_logging_users; +void main_ref(void); +void main_unref(void); + #endif
--- a/src/login/main.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/main.c Sat Nov 16 07:21:21 2002 +0200 @@ -10,13 +10,33 @@ #include "ssl-proxy.h" #include <stdlib.h> +#include <unistd.h> #include <syslog.h> -IOLoop ioloop; int disable_plaintext_auth; unsigned int max_logging_users; +static IOLoop ioloop; static IO io_imap, io_imaps; +static int main_refcount; +static int process_per_connection, 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(); + } +} static void sig_quit(int signo __attr_unused__) { @@ -33,6 +53,23 @@ if (fd == -1) return; + if (process_per_connection) { + if (close(listen_fd) < 0) + i_fatal("can't close() listen handle"); + + if (io_imap != NULL) { + io_remove(io_imap); + io_imap = NULL; + } + if (io_imaps != NULL) { + io_remove(io_imaps); + io_imaps = NULL; + } + + closing_down = TRUE; + master_notify_finished(); + } + (void)client_create(fd, &addr); } @@ -79,10 +116,14 @@ } disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL; + process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL; value = getenv("MAX_LOGGING_USERS"); max_logging_users = value == NULL ? 0 : strtoul(value, NULL, 10); + closing_down = FALSE; + main_refcount = 0; + /* Initialize SSL proxy before dropping privileges so it can read the certificate and private key file. */ ssl_proxy_init();
--- a/src/login/master.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/master.c Sat Nov 16 07:21:21 2002 +0200 @@ -5,6 +5,9 @@ #include "network.h" #include "fdpass.h" #include "master.h" +#include "client.h" + +#include <unistd.h> typedef struct _WaitingRequest WaitingRequest; @@ -86,6 +89,29 @@ push_request(req.id, callback, context); } +void master_notify_finished(void) +{ + MasterRequest req; + + memset(&req, 0, sizeof(req)); + + /* sending -1 as fd does the notification */ + if (fd_send(LOGIN_MASTER_SOCKET_FD, + -1, &req, sizeof(req)) != sizeof(req)) + i_fatal("fd_send() failed: %m"); +} + +void master_close(void) +{ + clients_destroy_all(); + + (void)close(LOGIN_MASTER_SOCKET_FD); + io_remove(io_master); + io_master = NULL; + + main_unref(); +} + static void master_input(void *context __attr_unused__, int fd, IO io __attr_unused__) { @@ -94,8 +120,8 @@ ret = net_receive(fd, master_buf + master_pos, sizeof(master_buf) - master_pos); if (ret < 0) { - /* master died, kill ourself too */ - io_loop_stop(ioloop); + /* master died, kill all clients logging in */ + master_close(); return; } @@ -110,6 +136,8 @@ void master_init(void) { + main_ref(); + requests = NULL; next_request = &requests; @@ -126,5 +154,7 @@ i_free(requests); requests = next; } - io_remove(io_master); + + if (io_master != NULL) + io_remove(io_master); }
--- a/src/login/master.h Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/master.h Sat Nov 16 07:21:21 2002 +0200 @@ -10,6 +10,12 @@ unsigned char cookie[AUTH_COOKIE_SIZE], IPADDR *ip, MasterCallback callback, void *context); +/* Notify master that we're not listening for new connections anymore. */ +void master_notify_finished(void); + +/* Close connection to master process */ +void master_close(void); + void master_init(void); void master_deinit(void);
--- a/src/login/ssl-proxy.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/login/ssl-proxy.c Sat Nov 16 07:21:21 2002 +0200 @@ -125,6 +125,8 @@ io_remove(proxy->io_plain); i_free(proxy); + + main_unref(); return FALSE; } @@ -230,26 +232,6 @@ proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy); } -static GNUTLS_STATE initialize_state(void) -{ - GNUTLS_STATE state; - - gnutls_init(&state, GNUTLS_SERVER); - - gnutls_protocol_set_priority(state, protocol_priority); - gnutls_cipher_set_priority(state, cipher_priority); - gnutls_compression_set_priority(state, comp_priority); - gnutls_kx_set_priority(state, kx_priority); - gnutls_mac_set_priority(state, mac_priority); - - gnutls_cred_set(state, GNUTLS_CRD_CERTIFICATE, x509_cred); - - /*gnutls_certificate_server_set_request(state, GNUTLS_CERT_REQUEST);*/ - - gnutls_dh_set_prime_bits(state, DH_BITS); - return state; -} - static void ssl_handshake(void *context, int fd __attr_unused__, IO io __attr_unused__) { @@ -284,6 +266,26 @@ } } +static GNUTLS_STATE initialize_state(void) +{ + GNUTLS_STATE state; + + gnutls_init(&state, GNUTLS_SERVER); + + gnutls_protocol_set_priority(state, protocol_priority); + gnutls_cipher_set_priority(state, cipher_priority); + gnutls_compression_set_priority(state, comp_priority); + gnutls_kx_set_priority(state, kx_priority); + gnutls_mac_set_priority(state, mac_priority); + + gnutls_cred_set(state, GNUTLS_CRD_CERTIFICATE, x509_cred); + + /*gnutls_certificate_server_set_request(state, GNUTLS_CERT_REQUEST);*/ + + gnutls_dh_set_prime_bits(state, DH_BITS); + return state; +} + int ssl_proxy_new(int fd) { SSLProxy *proxy; @@ -316,6 +318,7 @@ if (!ssl_proxy_destroy(proxy)) return -1; + main_ref(); return sfd[1]; }
--- a/src/master/login-process.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/master/login-process.c Sat Nov 16 07:21:21 2002 +0200 @@ -14,15 +14,19 @@ #include <unistd.h> #include <syslog.h> -typedef struct { +typedef struct _LoginProcess LoginProcess; + +struct _LoginProcess { + LoginProcess *prev_nonlisten, *next_nonlisten; int refcount; pid_t pid; int fd; IO io; OBuffer *outbuf; + unsigned int listening:1; unsigned int destroyed:1; -} LoginProcess; +}; typedef struct { LoginProcess *process; @@ -36,7 +40,10 @@ static int auth_id_counter; static Timeout to; -static HashTable *processes = NULL; + +static HashTable *processes; +static LoginProcess *oldest_nonlisten_process, *newest_nonlisten_process; +static unsigned int listening_processes; static void login_process_destroy(LoginProcess *p); static void login_process_unref(LoginProcess *p); @@ -103,6 +110,28 @@ return; } + if (client_fd == -1) { + /* just a notification that the login process isn't + listening for new connections anymore */ + if (!p->listening) { + i_error("login: received another \"not listening\" " + "notification"); + } else { + p->listening = FALSE; + listening_processes--; + + p->prev_nonlisten = newest_nonlisten_process; + + if (newest_nonlisten_process != NULL) + newest_nonlisten_process->next_nonlisten = p; + newest_nonlisten_process = p; + + if (oldest_nonlisten_process == NULL) + oldest_nonlisten_process = p; + } + return; + } + /* login process isn't trusted, validate all data to make sure it's not trying to exploit us */ if (!VALIDATE_STR(req.login_tag)) { @@ -125,7 +154,7 @@ if (auth_process == NULL) { i_error("login: Authentication process %u doesn't exist", req.auth_process); - auth_callback(NULL, &authreq); + auth_callback(NULL, authreq); } else { auth_process_request(auth_process, authreq->auth_id, req.cookie, auth_callback, authreq); @@ -142,26 +171,50 @@ p->refcount = 1; p->pid = pid; p->fd = fd; + p->listening = TRUE; p->io = io_add(fd, IO_READ, login_process_input, p); p->outbuf = o_buffer_create_file(fd, default_pool, sizeof(MasterReply)*10, IO_PRIORITY_DEFAULT, FALSE); hash_insert(processes, POINTER_CAST(pid), p); + listening_processes++; return p; } +void login_process_remove_from_lists(LoginProcess *p) +{ + if (p == oldest_nonlisten_process) + oldest_nonlisten_process = p->next_nonlisten; + else + p->prev_nonlisten->next_nonlisten = p->next_nonlisten; + + if (p == newest_nonlisten_process) + newest_nonlisten_process = p->prev_nonlisten; + else + p->next_nonlisten->prev_nonlisten = p->prev_nonlisten; + + p->next_nonlisten = p->prev_nonlisten = NULL; +} + static void login_process_destroy(LoginProcess *p) { if (p->destroyed) return; p->destroyed = TRUE; + if (p->listening) + listening_processes--; + o_buffer_close(p->outbuf); io_remove(p->io); (void)close(p->fd); + if (!p->listening) + login_process_remove_from_lists(p); + hash_remove(processes, POINTER_CAST(p->pid)); + login_process_unref(p); } @@ -180,6 +233,12 @@ pid_t pid; int fd[2]; + if (set_login_process_per_connection && + hash_size(processes)-listening_processes >= set_max_logging_users) { + if (oldest_nonlisten_process != NULL) + login_process_destroy(oldest_nonlisten_process); + } + if (set_login_uid == 0) i_fatal("Login process must not run as root"); @@ -249,8 +308,13 @@ if (set_disable_plaintext_auth) putenv("DISABLE_PLAINTEXT_AUTH=1"); - putenv((char *) t_strdup_printf("MAX_LOGGING_USERS=%d", - set_max_logging_users)); + if (set_login_process_per_connection) { + putenv("PROCESS_PER_CONNECTION=1"); + putenv("MAX_LOGGING_USERS=1"); + } else { + putenv((char *) t_strdup_printf("MAX_LOGGING_USERS=%d", + set_max_logging_users)); + } /* hide the path, it's ugly */ argv[0] = strrchr(set_login_executable, '/'); @@ -280,14 +344,17 @@ { /* create max. one process every second, that way if it keeps dying all the time we don't eat all cpu with fork()ing. */ - if (hash_size(processes) < set_login_processes_count) + if (listening_processes < set_login_processes_count) (void)create_login_process(); } void login_processes_init(void) { auth_id_counter = 0; - processes = hash_create(default_pool, 128, NULL, NULL); + listening_processes = 0; + oldest_nonlisten_process = newest_nonlisten_process = NULL; + + processes = hash_create(default_pool, 128, NULL, NULL); to = timeout_add(1000, login_processes_start_missing, NULL); }
--- a/src/master/settings.c Sat Nov 16 07:19:03 2002 +0200 +++ b/src/master/settings.c Sat Nov 16 07:21:21 2002 +0200 @@ -39,6 +39,8 @@ { "login_user", SET_STR, &set_login_user }, { "login_dir", SET_STR, &set_login_dir }, { "login_chroot", SET_BOOL,&set_login_chroot }, + { "login_process_per_connection", + SET_BOOL,&set_login_process_per_connection }, { "login_processes_count", SET_INT, &set_login_processes_count }, { "max_logging_users", SET_INT, &set_max_logging_users }, @@ -88,7 +90,8 @@ char *set_login_dir = PKG_RUNDIR; int set_login_chroot = TRUE; -unsigned int set_login_processes_count = 1; +int set_login_process_per_connection = TRUE; +unsigned int set_login_processes_count = 3; unsigned int set_max_logging_users = 256; uid_t set_login_uid; /* generated from set_login_user */ @@ -176,11 +179,18 @@ if (access(set_login_dir, X_OK) < 0) i_fatal("Can't access login directory %s: %m", set_login_dir); + if (set_max_imap_processes < 1) + i_fatal("max_imap_processes must be at least 1"); if (set_login_processes_count < 1) i_fatal("login_processes_count must be at least 1"); - if (set_first_valid_uid < set_last_valid_uid) + if (set_max_logging_users < 1) + i_fatal("max_logging_users must be at least 1"); + + if (set_last_valid_uid != 0 && + set_first_valid_uid > set_last_valid_uid) i_fatal("first_valid_uid can't be larger than last_valid_uid"); - if (set_first_valid_gid < set_last_valid_gid) + if (set_last_valid_gid != 0 && + set_first_valid_gid > set_last_valid_gid) i_fatal("first_valid_gid can't be larger than last_valid_gid"); auth_settings_verify();
--- a/src/master/settings.h Sat Nov 16 07:19:03 2002 +0200 +++ b/src/master/settings.h Sat Nov 16 07:21:21 2002 +0200 @@ -20,6 +20,7 @@ extern char *set_login_user; extern char *set_login_dir; extern int set_login_chroot; +extern int set_login_process_per_connection; extern unsigned int set_login_processes_count; extern unsigned int set_max_logging_users;