Mercurial > dovecot > original-hg > dovecot-1.2
changeset 5846:21e529b8a701 HEAD
Initial implementation for mail_max_user_connections setting.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 30 Jun 2007 19:14:08 +0300 |
parents | 9265c13c4103 |
children | 55699bbeaec2 |
files | dovecot-example.conf src/imap-login/client-authenticate.c src/login-common/master.c src/login-common/master.h src/login-common/sasl-server.c src/master/child-process.c src/master/child-process.h src/master/dict-process.c src/master/login-process.c src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/master-login-interface.h src/master/master-settings-defs.c src/master/master-settings.c src/master/master-settings.h src/master/ssl-init.c src/pop3-login/client-authenticate.c |
diffstat | 18 files changed, 228 insertions(+), 57 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Sat Jun 30 19:01:06 2007 +0300 +++ b/dovecot-example.conf Sat Jun 30 19:14:08 2007 +0300 @@ -334,6 +334,13 @@ # new users aren't allowed to log in. #max_mail_processes = 1024 +# Maximum number of connections allowed for a user. The limits are enforced +# separately for IMAP and POP3 connections, so you can move this setting +# inside protocol {} to have separate settings for them. NOTE: The user names +# are compared case-sensitively, so make sure your userdb returns usernames +# always using the same casing so users can't bypass this limit! +#mail_max_user_connections = 10 + # Set max. process size in megabytes. Most of the memory goes to mmap()ing # files, so it shouldn't harm much even if this limit is set pretty high. #mail_process_size = 256
--- a/src/imap-login/client-authenticate.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/imap-login/client-authenticate.c Sat Jun 30 19:14:08 2007 +0300 @@ -222,7 +222,13 @@ } break; case SASL_SERVER_REPLY_MASTER_FAILED: - client_destroy_internal_failure(client); + if (data == NULL) + client_destroy_internal_failure(client); + else { + client_send_tagline(client, + t_strconcat("NO ", data, NULL)); + client_destroy(client, data); + } break; case SASL_SERVER_REPLY_CONTINUE: data_len = strlen(data);
--- a/src/login-common/master.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/login-common/master.c Sat Jun 30 19:14:08 2007 +0300 @@ -22,7 +22,8 @@ static char master_buf[sizeof(struct master_login_reply)]; static struct client destroyed_client; -static void client_call_master_callback(struct client *client, bool success) +static void client_call_master_callback(struct client *client, + enum master_login_status status) { master_callback_t *master_callback; @@ -30,7 +31,7 @@ client->master_tag = 0; client->master_callback = NULL; - master_callback(client, success); + master_callback(client, status); } static void request_handle(struct master_login_reply *reply) @@ -50,7 +51,7 @@ hash_remove(master_requests, POINTER_CAST(reply->tag)); if (client != &destroyed_client) { - client_call_master_callback(client, reply->success); + client_call_master_callback(client, reply->status); /* NOTE: client may be destroyed now */ } }
--- a/src/login-common/master.h Sat Jun 30 19:01:06 2007 +0300 +++ b/src/login-common/master.h Sat Jun 30 19:14:08 2007 +0300 @@ -5,7 +5,8 @@ #include "../master/master-login-interface.h" -typedef void master_callback_t(struct client *client, bool success); +typedef void master_callback_t(struct client *client, + enum master_login_status status); void master_request_login(struct client *client, master_callback_t *callback, unsigned int auth_pid, unsigned int auth_id);
--- a/src/login-common/sasl-server.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/login-common/sasl-server.c Sat Jun 30 19:14:08 2007 +0300 @@ -37,11 +37,24 @@ /* NOTE: client may be destroyed now */ } -static void master_callback(struct client *client, bool success) +static void +master_callback(struct client *client, enum master_login_status status) { + enum sasl_server_reply reply = SASL_SERVER_REPLY_MASTER_FAILED; + const char *data = NULL; + client->authenticating = FALSE; - call_client_callback(client, success ? SASL_SERVER_REPLY_SUCCESS : - SASL_SERVER_REPLY_MASTER_FAILED, NULL, NULL); + switch (status) { + case MASTER_LOGIN_STATUS_OK: + reply = SASL_SERVER_REPLY_SUCCESS; + break; + case MASTER_LOGIN_STATUS_INTERNAL_ERROR: + break; + case MASTER_LOGIN_STATUS_MAX_CONNECTIONS: + data = "Maximum number of connections exceeded"; + break; + } + call_client_callback(client, reply, data, NULL); } static void authenticate_callback(struct auth_request *request, int status,
--- a/src/master/child-process.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/child-process.c Sat Jun 30 19:14:08 2007 +0300 @@ -153,8 +153,10 @@ WTERMSIG(status)); } - if (destroy_callbacks[process_type] != NULL) - destroy_callbacks[process_type](process, abnormal_exit); + if (destroy_callbacks[process_type] != NULL) { + destroy_callbacks[process_type](process, pid, + abnormal_exit); + } } if (pid == -1 && errno != EINTR && errno != ECHILD)
--- a/src/master/child-process.h Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/child-process.h Sat Jun 30 19:14:08 2007 +0300 @@ -18,8 +18,8 @@ enum process_type type; }; -typedef void child_process_destroy_callback_t(struct child_process *, - bool abnormal_exit); +typedef void child_process_destroy_callback_t(struct child_process *process, + pid_t pid, bool abnormal_exit); extern const char *process_names[]; extern struct hash_table *processes;
--- a/src/master/dict-process.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/dict-process.c Sat Jun 30 19:14:08 2007 +0300 @@ -159,6 +159,7 @@ static void dict_process_destroyed(struct child_process *process, + pid_t pid __attr_unused__, bool abnormal_exit __attr_unused__) { struct dict_process *p = (struct dict_process *)process;
--- a/src/master/login-process.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/login-process.c Sat Jun 30 19:14:08 2007 +0300 @@ -94,12 +94,12 @@ memset(&master_reply, 0, sizeof(master_reply)); if (user == NULL) - master_reply.success = FALSE; + master_reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR; else { struct login_group *group = request->process->group; t_push(); - master_reply.success = + master_reply.status = create_mail_process(group->mail_process_type, group->set, request->fd, &request->local_ip, @@ -683,7 +683,8 @@ } static void -login_process_destroyed(struct child_process *process, bool abnormal_exit) +login_process_destroyed(struct child_process *process, + pid_t pid __attr_unused__, bool abnormal_exit) { struct login_process *p = (struct login_process *)process; @@ -910,8 +911,6 @@ void login_processes_deinit(void) { - login_processes_destroy_all(); - if (to != NULL) timeout_remove(&to); if (io_listen != NULL)
--- a/src/master/mail-process.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/mail-process.c Sat Jun 30 19:14:08 2007 +0300 @@ -1,7 +1,8 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "array.h" +#include "hash.h" #include "fd-close-on-exec.h" #include "env-util.h" #include "str.h" @@ -12,6 +13,7 @@ #include "home-expand.h" #include "var-expand.h" #include "mail-process.h" +#include "master-login-interface.h" #include "login-process.h" #include "log.h" @@ -31,10 +33,76 @@ many seconds to finish. */ #define CHDIR_WARN_SECS 10 -static struct child_process imap_child_process = { PROCESS_TYPE_IMAP }; -static struct child_process pop3_child_process = { PROCESS_TYPE_POP3 }; +struct mail_process_group { + /* process.type / user identifies this process group */ + struct child_process process; + char *user; + + /* processes array acts also as refcount */ + ARRAY_DEFINE(processes, pid_t); +}; + +/* type+user -> struct mail_process_group */ +static struct hash_table *mail_process_groups; +static unsigned int mail_process_count = 0; + +static unsigned int mail_process_group_hash(const void *p) +{ + const struct mail_process_group *group = p; + + return str_hash(group->user) ^ group->process.type; +} + +static int mail_process_group_cmp(const void *p1, const void *p2) +{ + const struct mail_process_group *group1 = p1, *group2 = p2; + int ret; + + ret = strcmp(group1->user, group2->user); + if (ret == 0) + ret = group1->process.type - group2->process.type; + return ret; +} -static unsigned int mail_process_count = 0; +static struct mail_process_group * +mail_process_group_lookup(enum process_type type, const char *user) +{ + struct mail_process_group lookup_group; + + lookup_group.process.type = type; + lookup_group.user = t_strdup_noconst(user); + + return hash_lookup(mail_process_groups, &lookup_group); +} + +static struct mail_process_group * +mail_process_group_create(enum process_type type, const char *user) +{ + struct mail_process_group *group; + + group = i_new(struct mail_process_group, 1); + group->process.type = type; + group->user = i_strdup(user); + + i_array_init(&group->processes, 10); + hash_insert(mail_process_groups, group, group); + return group; +} + +static void +mail_process_group_add(struct mail_process_group *group, pid_t pid) +{ + mail_process_count++; + array_append(&group->processes, &pid, 1); + child_process_add(pid, &group->process); +} + +static void mail_process_group_free(struct mail_process_group *group) +{ + array_free(&group->processes); + i_free(group->user); + i_free(group); +} static bool validate_uid_gid(struct settings *set, uid_t uid, gid_t gid, const char *user) @@ -422,15 +490,17 @@ "If you're sure this check was wrong, set nfs_check=no.", path); } -bool create_mail_process(enum process_type process_type, struct settings *set, - int socket, const struct ip_addr *local_ip, - const struct ip_addr *remote_ip, - const char *user, const char *const *args, - bool dump_capability) +enum master_login_status +create_mail_process(enum process_type process_type, struct settings *set, + int socket, const struct ip_addr *local_ip, + const struct ip_addr *remote_ip, + const char *user, const char *const *args, + bool dump_capability) { const struct var_expand_table *var_expand_table; const char *p, *addr, *mail, *chroot_dir, *home_dir, *full_home_dir; const char *system_user; + struct mail_process_group *process_group; char title[1024]; struct log_io *log; string_t *str; @@ -438,19 +508,26 @@ uid_t uid; gid_t gid; ARRAY_DEFINE(extra_args, const char *); - unsigned int i, count, left; + unsigned int i, count, left, process_count; int ret, log_fd, nice, chdir_errno; bool home_given, nfs_check; i_assert(process_type == PROCESS_TYPE_IMAP || process_type == PROCESS_TYPE_POP3); - // FIXME: per-group if (mail_process_count == set->max_mail_processes) { i_error("Maximum number of mail processes exceeded"); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } + /* check process limit for this user */ + process_group = mail_process_group_lookup(process_type, user); + process_count = process_group == NULL ? 0 : + array_count(&process_group->processes); + if (process_count >= set->mail_max_user_connections && + set->mail_max_user_connections != 0) + return MASTER_LOGIN_STATUS_MAX_CONNECTIONS; + t_array_init(&extra_args, 16); mail = home_dir = chroot_dir = system_user = ""; uid = gid = 0; nice = 0; @@ -471,7 +548,7 @@ if (uid != 0) { i_error("uid specified multiple times for %s", user); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } uid = (uid_t)strtoul(*args + 4, NULL, 10); } else if (strncmp(*args, "gid=", 4) == 0) @@ -494,7 +571,7 @@ if (!dump_capability) { if (!validate_uid_gid(set, uid, gid, user)) - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } if (*chroot_dir == '\0' && *set->mail_chroot != '\0') @@ -505,26 +582,26 @@ i_error("Invalid chroot directory '%s' (user %s) " "(see valid_chroot_dirs in config file)", chroot_dir, user); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } if (set->mail_drop_priv_before_exec) { i_error("Can't chroot to directory '%s' (user %s) " "with mail_drop_priv_before_exec=yes", chroot_dir, user); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } } if (!dump_capability) { log_fd = log_create_pipe(&log, set->mail_log_max_lines_per_sec); if (log_fd == -1) - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } else { log = NULL; log_fd = dup(STDERR_FILENO); if (log_fd == -1) { i_error("dup() failed: %m"); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } fd_close_on_exec(log_fd, TRUE); } @@ -542,7 +619,7 @@ if (pid < 0) { i_error("fork() failed: %m"); (void)close(log_fd); - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } var_expand_table = @@ -557,16 +634,15 @@ /* master */ var_expand(str, set->mail_log_prefix, var_expand_table); - mail_process_count++; - if (!dump_capability) { + if (!dump_capability) log_set_prefix(log, str_c(str)); - child_process_add(pid, - process_type == PROCESS_TYPE_IMAP ? - &imap_child_process : - &pop3_child_process); + if (process_group == NULL) { + process_group = mail_process_group_create(process_type, + user); } + mail_process_group_add(process_group, pid); (void)close(log_fd); - return TRUE; + return MASTER_LOGIN_STATUS_OK; } #ifdef HAVE_SETPRIORITY @@ -715,20 +791,58 @@ set->mail_executable); /* not reached */ - return FALSE; + return MASTER_LOGIN_STATUS_INTERNAL_ERROR; } static void -mail_process_destroyed(struct child_process *process __attr_unused__, - bool abnormal_exit __attr_unused__) +mail_process_destroyed(struct child_process *process, + pid_t pid, bool abnormal_exit __attr_unused__) { + struct mail_process_group *group = (struct mail_process_group *)process; + const pid_t *pids; + unsigned int i, count; + + pids = array_get(&group->processes, &count); + if (count == 1) { + /* last process in this group */ + i_assert(pids[0] == pid); + hash_remove(mail_process_groups, group); + mail_process_group_free(group); + } else { + for (i = 0; i < count; i++) { + if (pids[i] == pid) + break; + } + i_assert(i != count); + array_delete(&group->processes, i, 1); + } + mail_process_count--; } void mail_processes_init(void) { + mail_process_groups = hash_create(default_pool, default_pool, 0, + mail_process_group_hash, + mail_process_group_cmp); + child_process_set_destroy_callback(PROCESS_TYPE_IMAP, mail_process_destroyed); child_process_set_destroy_callback(PROCESS_TYPE_POP3, mail_process_destroyed); } + +void mail_processes_deinit(void) +{ + struct hash_iterate_context *iter; + void *key, *value; + + iter = hash_iterate_init(mail_process_groups); + while (hash_iterate(iter, &key, &value)) { + struct mail_process_group *group = value; + mail_process_group_free(group); + } + hash_iterate_deinit(iter); + + hash_destroy(mail_process_groups); +}
--- a/src/master/mail-process.h Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/mail-process.h Sat Jun 30 19:14:08 2007 +0300 @@ -9,12 +9,15 @@ void mail_process_exec(const char *protocol, const char *section) __attr_noreturn__; -bool create_mail_process(enum process_type process_type, struct settings *set, - int socket, const struct ip_addr *local_ip, - const struct ip_addr *remote_ip, - const char *user, const char *const *args, - bool dump_capability); + +enum master_login_status +create_mail_process(enum process_type process_type, struct settings *set, + int socket, const struct ip_addr *local_ip, + const struct ip_addr *remote_ip, + const char *user, const char *const *args, + bool dump_capability); void mail_processes_init(void); +void mail_processes_deinit(void); #endif
--- a/src/master/main.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/main.c Sat Jun 30 19:14:08 2007 +0300 @@ -461,11 +461,15 @@ (void)unlink(t_strconcat(settings_root->defaults->base_dir, "/master.pid", NULL)); + login_processes_destroy_all(); + /* call process destroy handlers first */ + child_processes_deinit(); + + mail_processes_deinit(); login_processes_deinit(); auth_processes_deinit(); dict_process_deinit(); ssl_deinit(); - child_processes_deinit(); if (close(null_fd) < 0) i_error("close(null_fd) failed: %m");
--- a/src/master/master-login-interface.h Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/master-login-interface.h Sat Jun 30 19:14:08 2007 +0300 @@ -36,9 +36,16 @@ struct ip_addr local_ip, remote_ip; }; +enum master_login_status { + MASTER_LOGIN_STATUS_OK, + MASTER_LOGIN_STATUS_INTERNAL_ERROR, + /* user reached max. simultaneous connections */ + MASTER_LOGIN_STATUS_MAX_CONNECTIONS +}; + struct master_login_reply { unsigned int tag; - bool success; + enum master_login_status status; }; #endif
--- a/src/master/master-settings-defs.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/master-settings-defs.c Sat Jun 30 19:14:08 2007 +0300 @@ -55,6 +55,7 @@ DEF_STR(valid_chroot_dirs), DEF_STR(mail_chroot), DEF_INT(max_mail_processes), + DEF_INT(mail_max_user_connections), DEF_BOOL(verbose_proctitle), DEF_INT(first_valid_uid),
--- a/src/master/master-settings.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/master-settings.c Sat Jun 30 19:14:08 2007 +0300 @@ -10,6 +10,7 @@ #include "unlink-directory.h" #include "syslog-util.h" #include "mail-process.h" +#include "master-login-interface.h" #include "settings.h" #include <stdio.h> @@ -205,6 +206,7 @@ MEMBER(valid_chroot_dirs) "", MEMBER(mail_chroot) "", MEMBER(max_mail_processes) 1024, + MEMBER(mail_max_user_connections) 10, MEMBER(verbose_proctitle) FALSE, MEMBER(first_valid_uid) 500, @@ -546,6 +548,7 @@ "gid=65534", NULL }; + enum master_login_status login_status; struct ip_addr ip; char buf[4096]; int fd[2], status; @@ -577,8 +580,10 @@ } fd_close_on_exec(fd[0], TRUE); fd_close_on_exec(fd[1], TRUE); - if (!create_mail_process(PROCESS_TYPE_IMAP, set, fd[1], - &ip, &ip, "dump-capability", args, TRUE)) { + login_status = create_mail_process(PROCESS_TYPE_IMAP, set, fd[1], + &ip, &ip, "dump-capability", + args, TRUE); + if (login_status != MASTER_LOGIN_STATUS_OK) { (void)close(fd[0]); (void)close(fd[1]); return FALSE;
--- a/src/master/master-settings.h Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/master-settings.h Sat Jun 30 19:14:08 2007 +0300 @@ -61,6 +61,7 @@ const char *valid_chroot_dirs; const char *mail_chroot; unsigned int max_mail_processes; + unsigned int mail_max_user_connections; bool verbose_proctitle; unsigned int first_valid_uid, last_valid_uid;
--- a/src/master/ssl-init.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/master/ssl-init.c Sat Jun 30 19:14:08 2007 +0300 @@ -67,7 +67,7 @@ static void ssl_parameter_process_destroyed(struct child_process *process __attr_unused__, - bool abnormal_exit) + pid_t pid __attr_unused__, bool abnormal_exit) { if (!abnormal_exit) { if (file_copy(SSL_PARAMETERS_PERM_PATH,
--- a/src/pop3-login/client-authenticate.c Sat Jun 30 19:01:06 2007 +0300 +++ b/src/pop3-login/client-authenticate.c Sat Jun 30 19:14:08 2007 +0300 @@ -194,7 +194,13 @@ } break; case SASL_SERVER_REPLY_MASTER_FAILED: - client_destroy_internal_failure(client); + if (data == NULL) + client_destroy_internal_failure(client); + else { + client_send_line(client, + t_strconcat("-ERR [IN-USE] ", data, NULL)); + client_destroy(client, data); + } break; case SASL_SERVER_REPLY_CONTINUE: data_len = strlen(data);