]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Redesigned how login process passes connections to mail processes and changed related...
authorTimo Sirainen <tss@iki.fi>
Tue, 20 Oct 2009 01:42:09 +0000 (21:42 -0400)
committerTimo Sirainen <tss@iki.fi>
Tue, 20 Oct 2009 01:42:09 +0000 (21:42 -0400)
Master process is no longer in the middle.

--HG--
branch : HEAD

46 files changed:
doc/example-config/conf.d/master.conf
src/auth/Makefile.am
src/auth/auth-client-connection.c
src/auth/auth-client-connection.h
src/auth/auth-master-connection.c
src/auth/auth-master-interface.h [deleted file]
src/auth/main.c
src/doveadm/doveadm.c
src/dsync/dsync.c
src/imap/imap-client.c
src/imap/imap-client.h
src/imap/main.c
src/lda/main.c
src/lib-master/Makefile.am
src/lib-master/master-auth.c
src/lib-master/master-auth.h
src/lib-master/master-interface.h
src/lib-master/master-login-auth.c [new file with mode: 0644]
src/lib-master/master-login-auth.h [new file with mode: 0644]
src/lib-master/master-login.c [new file with mode: 0644]
src/lib-master/master-login.h [new file with mode: 0644]
src/lib-master/master-service-private.h
src/lib-master/master-service.c
src/lib-master/master-service.h
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-storage-service.h
src/login-common/client-common.c
src/login-common/common.h
src/login-common/main.c
src/login-common/sasl-server.c
src/master/Makefile.am
src/master/main.c
src/master/master-settings.c
src/master/master-settings.h
src/master/service-auth-server.c [deleted file]
src/master/service-auth-server.h [deleted file]
src/master/service-auth-source.c [deleted file]
src/master/service-auth-source.h [deleted file]
src/master/service-monitor.c
src/master/service-process.c
src/master/service-process.h
src/master/service.c
src/master/service.h
src/pop3/main.c
src/pop3/pop3-client.c
src/pop3/pop3-client.h

index 17ead38999a93c86526d6d4251c216848cd7610d..6926755c02439ae581065be916112718ad624c1c 100644 (file)
@@ -32,7 +32,6 @@ service anvil {
 }
 
 service auth {
-  type = auth
   executable = dovecot-auth
 
   # default
@@ -52,6 +51,11 @@ service auth {
     path = auth-userdb
     mode = 0600
   }
+
+  unix_listener {
+    path = auth-master
+    mode = 0600
+  }
 }
 
 service auth-worker {
@@ -65,9 +69,8 @@ service auth-worker {
 
 service imap-login {
   protocol = imap
-  type = auth-source
+  type = login
   executable = imap-login
-  auth_dest_service = imap
 
   inet_listener {
     port = 143
@@ -104,13 +107,18 @@ service imap {
   # Most of the memory goes to mmap()ing files. You may need to increase this
   # limit if you have huge mailboxes.
   #vsz_limit = 256
+
+  service_count = 1
+  unix_listener {
+    path = login/imap
+    mode = 0666
+  }
 }
 
 service pop3-login {
   protocol = pop3
-  type = auth-source
+  type = login
   executable = pop3-login
-  auth_dest_service = pop3
 
   inet_listener {
     port = 110
@@ -130,6 +138,12 @@ service pop3-login {
 service pop3 {
   protocol = pop3
   executable = pop3
+
+  service_count = 1
+  unix_listener {
+    path = login/pop3
+    mode = 0666
+  }
 }
 
 service lmtp {
index d4948a1953b7be36ce9ee4253a098d71ca586c32..6d95b6f76b6ec7a25ed5141d86608221a0b13a81 100644 (file)
@@ -109,7 +109,6 @@ headers = \
        auth-cache.h \
        auth-client-connection.h \
        auth-common.h \
-       auth-master-interface.h \
        auth-master-connection.h \
        mech-otp-skey-common.h \
        mech-plain-common.h \
index 86a6e8409d4b3167efac3bcf5ebbf2ce88fed1df..7166637287928a0eb30506e92e27dff02f3f5d82 100644 (file)
@@ -116,9 +116,7 @@ auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
         conn->refcount++;
        conn->request_handler =
                auth_request_handler_create(conn->auth,
-                       auth_callback, conn,
-                       array_count(&auth_master_connections) != 0 ?
-                       auth_master_request_callback : NULL);
+                       auth_callback, conn, auth_master_request_callback);
        auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
 
        conn->pid = pid;
index 583617f187e176db96e00859c885b0a740497595..3c035642e0962cb34600db16f743cf04214b52b9 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef AUTH_CLIENT_CONNECTION_H
 #define AUTH_CLIENT_CONNECTION_H
 
-#include "master-interface.h"
+#include "master-auth.h"
 
 struct auth_client_connection {
        struct auth *auth;
index 4c3f6992a29dc1929b8b568f53040efdf220915c..44c3f1a5004fc5d7bdc46018dc0e40fcac2f32a7 100644 (file)
@@ -15,8 +15,8 @@
 #include "master-service.h"
 #include "userdb.h"
 #include "userdb-blocking.h"
+#include "master-interface.h"
 #include "auth-request-handler.h"
-#include "auth-master-interface.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 
@@ -502,7 +502,6 @@ void auth_master_connection_destroy(struct auth_master_connection **_conn)
         struct auth_master_connection *conn = *_conn;
         struct auth_master_connection *const *masters;
        unsigned int i, count;
-       bool service_connection = conn->fd != MASTER_AUTH_FD;
 
        *_conn = NULL;
        if (conn->destroyed)
@@ -529,8 +528,7 @@ void auth_master_connection_destroy(struct auth_master_connection **_conn)
                conn->fd = -1;
        }
 
-       if (service_connection)
-               master_service_client_connection_destroyed(master_service);
+       master_service_client_connection_destroyed(master_service);
        auth_master_connection_unref(&conn);
 }
 
diff --git a/src/auth/auth-master-interface.h b/src/auth/auth-master-interface.h
deleted file mode 100644 (file)
index 05b9a4c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef AUTH_MASTER_INTERFACE_H
-#define AUTH_MASTER_INTERFACE_H
-
-#include "master-interface.h"
-
-/* Major version changes are not backwards compatible,
-   minor version numbers can be ignored. */
-#define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1
-#define AUTH_MASTER_PROTOCOL_MINOR_VERSION 1
-
-#endif
index 64b5ba14feee68e289599e3557be0012531c480c..27b89de2c1c11c822a522afd7134607e3f124c90 100644 (file)
 #include "module-dir.h"
 #include "randgen.h"
 #include "master-service.h"
+#include "master-interface.h"
 #include "password-scheme.h"
 #include "mech.h"
 #include "auth.h"
 #include "auth-request-handler.h"
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
-#include "auth-master-interface.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
@@ -86,9 +86,6 @@ static void main_init(void)
                /* workers have only a single connection from the master
                   auth process */
                master_service_set_client_limit(master_service, 1);
-       } else if (getenv("MASTER_AUTH_FD") != NULL) {
-               (void)auth_master_connection_create(auth, MASTER_AUTH_FD,
-                                                   FALSE);
        }
 }
 
index a36be21a1cc9acb587be13a462874fefb9a2b5e5..b0ce1ac15d2a15bb7d101fab6ca3a7a8e27b5f5b 100644 (file)
@@ -98,6 +98,7 @@ int main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
+       master_service_init_finish(master_service);
        if (!doveadm_try_run(cmd_name, argc, argv) &&
            !doveadm_mail_try_run(cmd_name, argc, argv))
                usage();
index 597b590b45d755e82eb23da56cac2752f1c29f93..c8887035f2a46272a872eaead6be0af43b8e0083 100644 (file)
@@ -112,6 +112,7 @@ int main(int argc, char *argv[])
        }
        if (optind != argc)
                usage();
+       master_service_init_finish(master_service);
 
        memset(&input, 0, sizeof(input));
        input.username = username;
index 02dcf5972b1e5d61e1557ae342caf62675715025..bd81d70d2547333602ecfa3e768a8a558fcd50c1 100644 (file)
@@ -21,7 +21,7 @@
 extern struct mail_storage_callbacks mail_storage_callbacks;
 struct imap_module_register imap_module_register = { 0 };
 
-static struct client *imap_clients = NULL;
+struct client *imap_clients = NULL;
 
 static void client_idle_timeout(struct client *client)
 {
index 1c0c1002544ee332e4e2c63f9e28f2e33086fb37..a7c96ccd67270a36f263bf95c60a1114eb3bd54f 100644 (file)
@@ -146,6 +146,8 @@ struct client {
        unsigned int modseqs_sent_since_sync:1;
 };
 
+extern struct client *imap_clients;
+
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
 struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
index cf70504fbca37bbfee9907301f899df6c09e086d..10eb3a49e24991cd067fbd73fa53add9ac99ced2 100644 (file)
@@ -9,8 +9,9 @@
 #include "restrict-access.h"
 #include "fd-close-on-exec.h"
 #include "process-title.h"
-#include "master-service.h"
 #include "master-interface.h"
+#include "master-service.h"
+#include "master-login.h"
 #include "mail-user.h"
 #include "mail-storage-service.h"
 #include "imap-commands.h"
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("CLIENT_INPUT") == NULL)
+        (getenv(MASTER_UID_ENV) == NULL)
+
+static const struct setting_parser_info *set_roots[] = {
+       &imap_setting_parser_info,
+       NULL
+};
+static struct master_login *master_login = NULL;
+static enum mail_storage_service_flags storage_service_flags = 0;
+static bool user_initialized = FALSE;
 
 void (*hook_client_created)(struct client **client) = NULL;
 
-static void client_add_input(struct client *client, const char *input)
+static void client_add_input(struct client *client, const buffer_t *buf)
 {
-       buffer_t *buf;
+       struct ostream *output;
        const char *tag;
        unsigned int data_pos;
        bool send_untagged_capability = FALSE;
 
-       buf = input == NULL ? NULL : t_base64_decode_str(input);
        if (buf != NULL && buf->used > 0) {
                tag = t_strndup(buf->data, buf->used);
                switch (*tag) {
@@ -55,6 +63,9 @@ static void client_add_input(struct client *client, const char *input)
                tag = getenv("IMAPLOGINTAG");
        }
 
+       output = client->output;
+       o_stream_ref(output);
+       o_stream_cork(output);
        if (tag == NULL) {
                client_send_line(client, t_strconcat(
                        "* PREAUTH [CAPABILITY ",
@@ -63,68 +74,141 @@ static void client_add_input(struct client *client, const char *input)
        } else if (send_untagged_capability) {
                /* client doesn't seem to understand tagged capabilities. send
                   untagged instead and hope that it works. */
-               o_stream_cork(client->output);
                client_send_line(client, t_strconcat("* CAPABILITY ",
                        str_c(client->capability_string), NULL));
                client_send_line(client, t_strconcat(tag, " OK Logged in", NULL));
-               o_stream_uncork(client->output);
        } else {
                client_send_line(client, t_strconcat(
                        tag, " OK [CAPABILITY ",
                        str_c(client->capability_string), "] Logged in", NULL));
        }
        (void)client_handle_input(client);
+       o_stream_uncork(output);
+       o_stream_unref(&output);
 }
 
-static void main_init(const struct imap_settings *set, struct mail_user *user,
-                     bool dump_capability)
+static void
+main_stdio_init_user(const struct imap_settings *set, struct mail_user *user)
 {
        struct client *client;
-       struct ostream *output;
+       buffer_t *input_buf;
+       const char *input_base64;
 
-       if (set->shutdown_clients && !dump_capability)
-               master_service_set_die_with_master(master_service, TRUE);
+       input_base64 = getenv("CLIENT_INPUT");
+       input_buf = input_base64 == NULL ? NULL :
+               t_base64_decode_str(input_base64);
 
-       client = client_create(0, 1, user, set);
+       client = client_create(STDIN_FILENO, STDOUT_FILENO, user, set);
+       client_add_input(client, input_buf);
+}
+
+static void
+main_stdio_run(bool dump_capability)
+{
+       struct mail_storage_service_input input;
+       struct mail_user *mail_user;
+       const struct imap_settings *set;
+       const char *value;
+
+       memset(&input, 0, sizeof(input));
+       input.module = input.service = "imap";
+       input.username = getenv("USER");
+       if (input.username == NULL && IS_STANDALONE())
+               input.username = getlogin();
+       if (input.username == NULL)
+               i_fatal("USER environment missing");
+       if ((value = getenv("IP")) != NULL)
+               net_addr2ip(value, &input.remote_ip);
+       if ((value = getenv("LOCAL_IP")) != NULL)
+               net_addr2ip(value, &input.local_ip);
+
+       user_initialized = TRUE;
+       mail_user = mail_storage_service_init_user(master_service,
+                                                  &input, set_roots,
+                                                  storage_service_flags);
+       set = mail_storage_service_get_settings(master_service);
+       restrict_access_allow_coredumps(TRUE);
+       if (set->shutdown_clients)
+               master_service_set_die_with_master(master_service, TRUE);
 
        if (dump_capability) {
+               struct client *client = client_create(0, 1, mail_user, set);
                printf("%s\n", str_c(client->capability_string));
                exit(0);
        }
 
-       output = client->output;
-       o_stream_ref(output);
-       o_stream_cork(output);
-       client_add_input(client, getenv("CLIENT_INPUT"));
-        o_stream_uncork(output);
-       o_stream_unref(&output);
+       /* fake that we're running, so we know if client was destroyed
+          while handling its initial input */
+       io_loop_set_running(current_ioloop);
+       main_stdio_init_user(set, mail_user);
 }
 
-static void main_deinit(void)
+static void
+login_client_connected(const struct master_login_client *client,
+                      const char *username, const char *const *extra_fields)
 {
-       clients_destroy_all();
+       struct mail_storage_service_input input;
+       struct mail_user *mail_user;
+       struct client *imap_client;
+       const struct imap_settings *set;
+       buffer_t input_buf;
+
+       if (imap_clients != NULL) {
+               i_error("Can't handle more than one connection currently");
+               (void)close(client->fd);
+               return;
+       }
+       i_assert(!user_initialized);
+
+       memset(&input, 0, sizeof(input));
+       input.module = input.service = "imap";
+       input.local_ip = client->auth_req.local_ip;
+       input.remote_ip = client->auth_req.remote_ip;
+       input.username = username;
+       input.userdb_fields = extra_fields;
+
+       if (input.username == NULL) {
+               i_error("login client: Username missing from auth reply");
+               (void)close(client->fd);
+               return;
+       }
+       user_initialized = TRUE;
+       master_login_deinit(&master_login);
+
+       mail_user = mail_storage_service_init_user(master_service,
+                                                  &input, set_roots,
+                                                  storage_service_flags);
+       set = mail_storage_service_get_settings(master_service);
+       restrict_access_allow_coredumps(TRUE);
+       if (set->shutdown_clients)
+               master_service_set_die_with_master(master_service, TRUE);
+
+       /* fake that we're running, so we know if client was destroyed
+          while handling its initial input */
+       io_loop_set_running(current_ioloop);
+
+       buffer_create_const_data(&input_buf, client->data,
+                                client->auth_req.data_size);
+       imap_client = client_create(client->fd, client->fd, mail_user, set);
+       T_BEGIN {
+               client_add_input(imap_client, &input_buf);
+       } T_END;
 }
 
 static void client_connected(const struct master_service_connection *conn)
 {
-       /* FIXME: we can't handle this yet */
-       (void)close(conn->fd);
+       if (master_login == NULL) {
+               /* running standalone, we shouldn't even get here */
+               (void)close(conn->fd);
+       } else {
+               master_login_add(master_login, conn->fd);
+       }
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
-       const struct setting_parser_info *set_roots[] = {
-               &imap_setting_parser_info,
-               NULL
-       };
-       enum master_service_flags service_flags =
-               MASTER_SERVICE_FLAG_STD_CLIENT;
-       enum mail_storage_service_flags storage_service_flags = 0;
-       struct mail_storage_service_input input;
-       struct mail_user *mail_user;
-       const struct imap_settings *set;
+       enum master_service_flags service_flags = 0;
        bool dump_capability;
-       const char *value;
        int c;
 
        if (IS_STANDALONE() && getuid() == 0 &&
@@ -134,12 +218,12 @@ int main(int argc, char *argv[], char *envp[])
                return 1;
        }
 
-       if (IS_STANDALONE())
-               service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
-       else {
+       if (IS_STANDALONE()) {
+               service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+                       MASTER_SERVICE_FLAG_STD_CLIENT;
+       } else {
                storage_service_flags |=
-                       MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
-                       MAIL_STORAGE_SERVICE_FLAG_RESTRICT_BY_ENV;
+                       MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
        }
 
        dump_capability = getenv("DUMP_CAPABILITY") != NULL;
@@ -153,50 +237,31 @@ int main(int argc, char *argv[], char *envp[])
                if (!master_service_parse_option(master_service, c, optarg))
                        exit(FATAL_DEFAULT);
        }
-
-       memset(&input, 0, sizeof(input));
-       input.module = "imap";
-       input.service = "imap";
-       input.username = getenv("USER");
-       if (input.username == NULL && IS_STANDALONE())
-               input.username = getlogin();
-       if (input.username == NULL) {
-               if (getenv(MASTER_UID_ENV) == NULL)
-                       i_fatal("USER environment missing");
-               else {
-                       i_fatal("login_executable setting must be imap-login, "
-                               "not imap");
-               }
-       }
-       if ((value = getenv("IP")) != NULL)
-               net_addr2ip(value, &input.remote_ip);
-       if ((value = getenv("LOCAL_IP")) != NULL)
-               net_addr2ip(value, &input.local_ip);
+        process_title_init(argv, envp);
+       master_service_init_finish(master_service);
 
        /* plugins may want to add commands, so this needs to be called early */
        commands_init();
        imap_fetch_handlers_init();
 
-       mail_user = mail_storage_service_init_user(master_service,
-                                                  &input, set_roots,
-                                                  storage_service_flags);
-       set = mail_storage_service_get_settings(master_service);
-       restrict_access_allow_coredumps(TRUE);
-
-        process_title_init(argv, envp);
-
-       /* fake that we're running, so we know if client was destroyed
-          while initializing */
-       io_loop_set_running(current_ioloop);
+       if (IS_STANDALONE() || dump_capability) {
+               T_BEGIN {
+                       main_stdio_run(dump_capability);
+               } T_END;
+       } else {
+               master_login = master_login_init("auth-master",
+                                                login_client_connected);
+               io_loop_set_running(current_ioloop);
+       }
 
-       T_BEGIN {
-               main_init(set, mail_user, dump_capability);
-       } T_END;
        if (io_loop_is_running(current_ioloop))
                master_service_run(master_service, client_connected);
+       clients_destroy_all();
 
-       main_deinit();
-       mail_storage_service_deinit_user();
+       if (master_login != NULL)
+               master_login_deinit(&master_login);
+       if (user_initialized)
+               mail_storage_service_deinit_user();
        imap_fetch_handlers_deinit();
        commands_deinit();
 
index b840bd8cd6e4f2f939c1f7f4d50c05c8ef166d06..ec0b9d55549a9876743e294254433d07abf90fb0 100644 (file)
@@ -378,6 +378,7 @@ int main(int argc, char *argv[])
                i_fatal_status(EX_USAGE,
                        "destination user parameter (-d user) not given");
        }
+       master_service_init_finish(master_service);
 
        memset(&service_input, 0, sizeof(service_input));
        service_input.module = "lda";
index 5e17346e944cd1417da49a7708e56dbcc32a9ada..251ce41cb2fd6e7b4ddaea56ed8366748659900a 100644 (file)
@@ -10,6 +10,8 @@ AM_CPPFLAGS = \
 
 libmaster_la_SOURCES = \
        master-auth.c \
+       master-login.c \
+       master-login-auth.c \
        master-service.c \
        master-service-settings.c \
        syslog-util.c
@@ -17,6 +19,8 @@ libmaster_la_SOURCES = \
 noinst_HEADERS = \
        master-auth.h \
        master-interface.h \
+       master-login.h \
+       master-login-auth.h \
        master-service.h \
        master-service-private.h \
        master-service-settings.h \
index 087f7a3761b9b10f7154c04f7bd042e2b7c99f6f..e88730e8dc579c993fb5b080a2ab8724cf911ef2 100644 (file)
 #include <unistd.h>
 #include <sys/stat.h>
 
-struct master_auth {
-       struct master_service *service;
-       pool_t pool;
+struct master_auth_connection {
+       struct master_auth *auth;
+       unsigned int tag;
 
        int fd;
        struct io *io;
 
-       unsigned int tag_counter;
-       struct hash_table *requests;
-
        char buf[sizeof(struct master_auth_reply)];
        unsigned int buf_pos;
 
-       /* linked list, node->context is the next pointer */
-       struct master_auth_request_node *free_nodes;
-};
-
-struct master_auth_request_node {
        master_auth_callback_t *callback;
        void *context;
 };
 
-static struct master_auth_request_node aborted_node;
-
-unsigned int master_auth_request(struct master_service *service, int fd,
-                                const struct master_auth_request *request,
-                                const unsigned char *data,
-                                master_auth_callback_t *callback,
-                                void *context)
-{
-       struct master_auth *auth = service->auth;
-        struct master_auth_request_node *node;
-       struct master_auth_request req;
-       buffer_t *buf;
-       struct stat st;
-       ssize_t ret;
-
-       i_assert(request->auth_pid != 0);
-
-       req = *request;
-       req.tag = ++auth->tag_counter;
-       if (req.tag == 0)
-               req.tag = ++auth->tag_counter;
-
-       if (fstat(fd, &st) < 0)
-               i_fatal("fstat(auth dest fd) failed: %m");
-       req.ino = st.st_ino;
+struct master_auth {
+       struct master_service *service;
+       pool_t pool;
 
-       buf = buffer_create_dynamic(pool_datastack_create(),
-                                   sizeof(req) + req.data_size);
-       buffer_append(buf, &req, sizeof(req));
-       buffer_append(buf, data, req.data_size);
+       const char *path;
 
-       ret = fd_send(auth->fd, fd, buf->data, buf->used);
-       if (ret < 0)
-               i_fatal("fd_send(%d) failed: %m", fd);
-       if ((size_t)ret != buf->used) {
-               i_fatal("fd_send() sent only %d of %d bytes",
-                       (int)ret, (int)buf->used);
-       }
+       unsigned int tag_counter;
+       struct hash_table *connections;
+};
 
-       if (auth->free_nodes == NULL)
-               node = p_new(auth->pool, struct master_auth_request_node, 1);
-       else {
-               node = auth->free_nodes;
-                auth->free_nodes = node->context;
-       }
-       node->callback = callback;
-       node->context = context;
+struct master_auth *
+master_auth_init(struct master_service *service, const char *path)
+{
+       struct master_auth *auth;
+       pool_t pool;
 
-       hash_table_insert(auth->requests, POINTER_CAST(req.tag), node);
-       return req.tag;
+       pool = pool_alloconly_create("master auth", 1024);
+       auth = p_new(pool, struct master_auth, 1);
+       auth->pool = pool;
+       auth->service = service;
+       auth->path = p_strdup(pool, path);
+       auth->connections = hash_table_create(default_pool, pool,
+                                             0, NULL, NULL);
+       return auth;
 }
 
-void master_auth_request_abort(struct master_service *service, unsigned int tag)
+static void
+master_auth_connection_deinit(struct master_auth_connection **_conn)
 {
-       struct master_auth *auth = service->auth;
-        struct master_auth_request_node *node;
+       struct master_auth_connection *conn = *_conn;
+       struct master_auth_reply reply;
 
-       node = hash_table_lookup(auth->requests, POINTER_CAST(tag));
-       if (node == NULL)
-               i_panic("master_auth_request_abort(): tag %u not found", tag);
+       *_conn = NULL;
 
-       i_assert(node != &aborted_node);
-       hash_table_update(auth->requests, POINTER_CAST(tag), &aborted_node);
+       if (conn->tag != 0) {
+               hash_table_remove(conn->auth->connections,
+                                 POINTER_CAST(conn->tag));
+       }
 
-       node->callback = NULL;
-       node->context = auth->free_nodes;
-       auth->free_nodes = node;
-}
+       if (conn->callback != NULL) {
+               memset(&reply, 0, sizeof(reply));
+               reply.status = MASTER_AUTH_STATUS_INTERNAL_ERROR;
+               conn->callback(&reply, conn->context);
+       }
 
-static void
-master_notify_have_more_avail_processes(struct master_service *service,
-                                       bool have_more)
-{
-       if (!have_more) {
-               /* make sure we're listening for more connections */
-               master_service_io_listeners_add(service);
+       if (conn->io != NULL)
+               io_remove(&conn->io);
+       if (conn->fd != -1) {
+               if (close(conn->fd) < 0)
+                       i_fatal("close(master auth conn) failed: %m");
+               conn->fd = -1;
        }
-       service->call_avail_overflow = !have_more;
+       i_free(conn);
 }
 
-static void request_handle(struct master_auth *auth,
-                          struct master_auth_reply *reply)
+void master_auth_deinit(struct master_auth **_auth)
 {
-        struct master_auth_request_node *node;
+       struct master_auth *auth = *_auth;
+       struct hash_iterate_context *iter;
+       void *key, *value;
 
-       if (reply->tag == 0) {
-               /* notification from master */
-               master_notify_have_more_avail_processes(auth->service,
-                                                       reply->status == 0);
-               return;
-       }
-
-       node = hash_table_lookup(auth->requests, POINTER_CAST(reply->tag));
-       if (node == NULL)
-               i_error("Master sent reply with unknown tag %u", reply->tag);
+       *_auth = NULL;
 
-       if (node != &aborted_node) {
-               node->callback(reply, node->context);
+       iter = hash_table_iterate_init(auth->connections);
+       while (hash_table_iterate(iter, &key, &value)) {
+               struct master_auth_connection *conn = value;
 
-               /* the callback may have called master_auth_request_abort(),
-                  which would have put the node to free_nodes list already */
-               if (node->callback != NULL) {
-                       node->callback = NULL;
-                       node->context = auth->free_nodes;
-                       auth->free_nodes = node;
-               }
+               conn->tag = 0;
+               master_auth_connection_deinit(&conn);
        }
-
-       hash_table_remove(auth->requests, POINTER_CAST(reply->tag));
+       hash_table_iterate_deinit(&iter);
+       hash_table_destroy(&auth->connections);
+       pool_unref(&auth->pool);
 }
 
-static void master_auth_input(void *context)
+static void master_auth_connection_input(struct master_auth_connection *conn)
 {
-       struct master_auth *auth = context;
+       const struct master_auth_reply *reply;
        int ret;
 
-       ret = net_receive(auth->fd, auth->buf + auth->buf_pos,
-                         sizeof(auth->buf) - auth->buf_pos);
-       if (ret < 0) {
-               /* master died, kill all clients logging in */
-                master_service_stop(auth->service);
+       ret = read(conn->fd, conn->buf + conn->buf_pos,
+                  sizeof(conn->buf) - conn->buf_pos);
+       if (ret <= 0) {
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               return;
+                       i_error("read(master auth conn) failed: %m");
+               } else {
+                       i_error("read(master auth conn) failed: "
+                               "Remote closed connection");
+               }
+               master_auth_connection_deinit(&conn);
                return;
        }
 
-       auth->buf_pos += ret;
-       if (auth->buf_pos < sizeof(auth->buf))
+       conn->buf_pos += ret;
+       if (conn->buf_pos < sizeof(conn->buf))
                return;
 
        /* reply is now read */
-       request_handle(auth, (struct master_auth_reply *) auth->buf);
-       auth->buf_pos = 0;
+       reply = (struct master_auth_reply *)conn->buf;
+       conn->buf_pos = 0;
+
+       if (conn->tag != reply->tag)
+               i_error("Master sent reply with unknown tag %u", reply->tag);
+       else {
+               conn->callback(reply, conn->context);
+               conn->callback = NULL;
+       }
+       master_auth_connection_deinit(&conn);
 }
 
-void master_auth_init(struct master_service *service)
+void master_auth_request(struct master_auth *auth, int fd,
+                        const struct master_auth_request *request,
+                        const unsigned char *data,
+                        master_auth_callback_t *callback,
+                        void *context, unsigned int *tag_r)
 {
-       struct master_auth *auth;
-       struct ip_addr ip;
-       pool_t pool;
+        struct master_auth_connection *conn;
+       struct master_auth_request req;
+       buffer_t *buf;
+       struct stat st;
+       ssize_t ret;
 
-       i_assert(service->auth == NULL);
+       i_assert(request->client_pid != 0);
+       i_assert(request->auth_pid != 0);
 
-       if (getenv("MASTER_AUTH_FD") == NULL)
-               i_fatal("auth_dest_service setting not set");
+       conn = i_new(struct master_auth_connection, 1);
+       conn->auth = auth;
+       conn->callback = callback;
+       conn->context = context;
 
-       if (net_getsockname(MASTER_AUTH_FD, &ip, NULL) < 0 ||
-           ip.family != AF_UNIX)
-               i_fatal("MASTER_AUTH_FD not given");
+       req = *request;
+       req.tag = ++auth->tag_counter;
+       if (req.tag == 0)
+               req.tag = ++auth->tag_counter;
 
-       pool = pool_alloconly_create("master auth", 1024);
-       auth = p_new(pool, struct master_auth, 1);
-       auth->pool = pool;
-       auth->service = service;
-       auth->fd = MASTER_AUTH_FD;
-       auth->requests = hash_table_create(default_pool, pool, 0, NULL, NULL);
-       auth->io = io_add(auth->fd, IO_READ, master_auth_input, auth);
+       if (fstat(fd, &st) < 0)
+               i_fatal("fstat(auth dest fd) failed: %m");
+       req.ino = st.st_ino;
+
+       buf = buffer_create_dynamic(pool_datastack_create(),
+                                   sizeof(req) + req.data_size);
+       buffer_append(buf, &req, sizeof(req));
+       buffer_append(buf, data, req.data_size);
+
+       conn->fd = net_connect_unix(auth->path);
+       if (conn->fd == -1)
+               i_error("net_connect_unix(%s) failed: %m", auth->path);
+
+       ret = conn->fd == -1 ? -1 :
+               fd_send(conn->fd, fd, buf->data, buf->used);
+       if (ret < 0)
+               i_error("fd_send(%d) failed: %m", fd);
+       else if ((size_t)ret != buf->used) {
+               i_error("fd_send() sent only %d of %d bytes",
+                       (int)ret, (int)buf->used);
+               ret = -1;
+       }
+       if (ret < 0) {
+               master_auth_connection_deinit(&conn);
+               return;
+       }
 
-       service->auth = auth;
+       conn->tag = req.tag;
+       conn->io = io_add(conn->fd, IO_READ,
+                         master_auth_connection_input, conn);
+       hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn);
+       *tag_r = req.tag;
 }
 
-void master_auth_deinit(struct master_service *service)
+void master_auth_request_abort(struct master_auth *auth, unsigned int tag)
 {
-       struct master_auth *auth = service->auth;
+        struct master_auth_connection *conn;
 
-       i_assert(service->auth != NULL);
+       conn = hash_table_lookup(auth->connections, POINTER_CAST(tag));
+       if (conn == NULL)
+               i_panic("master_auth_request_abort(): tag %u not found", tag);
 
-       hash_table_destroy(&auth->requests);
-       if (auth->io != NULL)
-               io_remove(&auth->io);
-       if (close(auth->fd) < 0)
-               i_fatal("close(master auth) failed: %m");
-       pool_unref(&auth->pool);
-       service->auth = NULL;
+       conn->callback = NULL;
 }
index 6d1b743d7da8f70baf245b6be39da3e672cdfacf..e4d28d61602d89480a84adee435b12a88181a610 100644 (file)
@@ -1,26 +1,74 @@
 #ifndef MASTER_AUTH_H
 #define MASTER_AUTH_H
 
+#include "network.h"
+
 struct master_service;
-struct master_auth_request;
-struct master_auth_reply;
+
+/* Major version changes are not backwards compatible,
+   minor version numbers can be ignored. */
+#define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1
+#define AUTH_MASTER_PROTOCOL_MINOR_VERSION 1
+
+/* Authentication client process's cookie size */
+#define MASTER_AUTH_COOKIE_SIZE (128/8)
+
+/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
+   to make sure there's space to transfer the command tag  */
+#define MASTER_AUTH_MAX_DATA_SIZE (1024*2)
+
+/* Authentication request. File descriptor may be sent along with the
+   request. */
+struct master_auth_request {
+       /* Request tag. Reply is sent back using same tag. */
+       unsigned int tag;
+
+       /* Authentication process, authentication ID and auth cookie. */
+       pid_t auth_pid;
+       unsigned int auth_id;
+       unsigned int client_pid;
+       uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
+
+       /* Local and remote IPs of the connection. The file descriptor
+          itself may be a local socketpair. */
+       struct ip_addr local_ip, remote_ip;
+
+       /* request follows this many bytes of client input */
+       uint32_t data_size;
+       /* inode of the transferred fd. verified just to be sure that the
+          correct fd is mapped to the correct struct. */
+       ino_t ino;
+};
+
+enum master_auth_status {
+       MASTER_AUTH_STATUS_OK,
+       MASTER_AUTH_STATUS_INTERNAL_ERROR
+};
+
+struct master_auth_reply {
+       /* tag=0 are notifications from master */
+       unsigned int tag;
+       enum master_auth_status status;
+       /* PID of the post-login mail process handling this connection */
+       pid_t mail_pid;
+};
 
 typedef void master_auth_callback_t(const struct master_auth_reply *reply,
                                    void *context);
 
+struct master_auth *
+master_auth_init(struct master_service *service, const char *path);
+void master_auth_deinit(struct master_auth **auth);
+
 /* Send an authentication request. The fd contains the file descriptor to
    transfer, or -1 if no fd is wanted to be transferred. Returns tag which can
    be used to abort the request (ie. ignore the reply from master).
    request->tag is ignored. */
-unsigned int master_auth_request(struct master_service *service, int fd,
-                                const struct master_auth_request *request,
-                                const unsigned char *data,
-                                master_auth_callback_t *callback,
-                                void *context);
-void master_auth_request_abort(struct master_service *service,
-                              unsigned int tag);
-
-void master_auth_init(struct master_service *service);
-void master_auth_deinit(struct master_service *service);
+void master_auth_request(struct master_auth *auth, int fd,
+                        const struct master_auth_request *request,
+                        const unsigned char *data,
+                        master_auth_callback_t *callback,
+                        void *context, unsigned int *tag_r);
+void master_auth_request_abort(struct master_auth *auth, unsigned int tag);
 
 #endif
index fbde493e231cffb21cada6977b1cc71b2177ac93..e15b1bcc827b8655f300c18d75c494bbe863e076 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef MASTER_INTERFACE_H
 #define MASTER_INTERFACE_H
 
-#include "network.h"
-
 /* We are attempting semi-compatibility with Postfix's master process here.
    Whether this is useful or not remains to be seen. */
 
@@ -30,46 +28,9 @@ struct log_service_handshake {
        /* unsigned char prefix[]; */
 };
 
-/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
-   to make sure there's space to transfer the command tag  */
-#define MASTER_AUTH_MAX_DATA_SIZE (1024*2)
-
-/* Authentication client process's cookie size */
-#define MASTER_AUTH_COOKIE_SIZE (128/8)
-
-/* Authentication request. File descriptor may be sent along with the
-   request. */
-struct master_auth_request {
-       /* Request tag. Reply is sent back using same tag. */
-       unsigned int tag;
-
-       /* Authentication process, authentication ID and auth cookie. */
-       pid_t auth_pid;
-       unsigned int auth_id;
-       uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
-
-       /* Local and remote IPs of the connection. The file descriptor
-          itself may be a local socketpair. */
-       struct ip_addr local_ip, remote_ip;
-
-       /* request follows this many bytes of client input */
-       uint32_t data_size;
-       /* inode of the transferred fd. verified just to be sure that the
-          correct fd is mapped to the correct struct. */
-       ino_t ino;
-};
-
-enum master_auth_status {
-       MASTER_AUTH_STATUS_OK,
-       MASTER_AUTH_STATUS_INTERNAL_ERROR
-};
-
-struct master_auth_reply {
-       /* tag=0 are notifications from master */
-       unsigned int tag;
-       enum master_auth_status status;
-       /* PID of the post-login mail process handling this connection */
-       pid_t mail_pid;
+enum master_login_state {
+       MASTER_LOGIN_STATE_NONFULL = 0,
+       MASTER_LOGIN_STATE_FULL
 };
 
 /* getenv(MASTER_UID_ENV) provides master_status.uid value */
@@ -93,13 +54,10 @@ struct master_auth_reply {
    if dovecot was started with -p parameter. */
 #define MASTER_SSL_KEY_PASSWORD_ENV "SSL_KEY_PASSWORD"
 
-/* Write pipe to anvil. Currently available only for auth destination
-   services, for others it's /dev/null. */
+/* Write pipe to anvil. */
 #define MASTER_ANVIL_FD 3
-
-/* Socket for sending master_auth_requests. Also used by auth server process
-   as a master socket. */
-#define MASTER_AUTH_FD 4
+/* Master's "all processes full" notification fd for login processes */
+#define MASTER_LOGIN_NOTIFY_FD 4
 
 /* Shared pipe to master, used to send master_status reports */
 #define MASTER_STATUS_FD 5
diff --git a/src/lib-master/master-login-auth.c b/src/lib-master/master-login-auth.c
new file mode 100644 (file)
index 0000000..d62c33f
--- /dev/null
@@ -0,0 +1,290 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "network.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hex-binary.h"
+#include "hash.h"
+#include "str.h"
+#include "master-auth.h"
+#include "master-login-auth.h"
+
+#include <stdlib.h>
+
+#define AUTH_MAX_INBUF_SIZE 8192
+
+struct master_login_auth_request {
+       master_login_auth_request_callback_t *callback;
+       void *context;
+};
+
+struct master_login_auth {
+       pool_t pool;
+       const char *auth_socket_path;
+       int refcount;
+
+       int fd;
+       struct io *io;
+       struct istream *input;
+       struct ostream *output;
+
+       unsigned int id_counter;
+       struct hash_table *requests;
+
+       unsigned int version_received:1;
+};
+
+struct master_login_auth *master_login_auth_init(const char *auth_socket_path)
+{
+       struct master_login_auth *auth;
+       pool_t pool;
+
+       pool = pool_alloconly_create("master login auth", 1024);
+       auth = p_new(pool, struct master_login_auth, 1);
+       auth->pool = pool;
+       auth->auth_socket_path = p_strdup(pool, auth_socket_path);
+       auth->refcount = 1;
+       auth->fd = -1;
+       auth->requests = hash_table_create(default_pool, pool, 0, NULL, NULL);
+       return auth;
+}
+
+static void master_login_auth_disconnect(struct master_login_auth *auth)
+{
+       struct hash_iterate_context *iter;
+       void *key, *value;
+
+       iter = hash_table_iterate_init(auth->requests);
+       while (hash_table_iterate(iter, &key, &value)) {
+               struct master_login_auth_request *request = value;
+               request->callback(NULL, request->context);
+               i_free(request);
+       }
+       hash_table_iterate_deinit(&iter);
+       hash_table_clear(auth->requests, FALSE);
+
+       if (auth->io != NULL)
+               io_remove(&auth->io);
+       if (auth->fd != -1) {
+               i_stream_destroy(&auth->input);
+               o_stream_destroy(&auth->output);
+
+               net_disconnect(auth->fd);
+               auth->fd = -1;
+       }
+       auth->version_received = FALSE;
+}
+
+static void master_login_auth_unref(struct master_login_auth **_auth)
+{
+       struct master_login_auth *auth = *_auth;
+
+       *_auth = NULL;
+
+       i_assert(auth->refcount > 0);
+       if (--auth->refcount > 0)
+               return;
+
+       hash_table_destroy(&auth->requests);
+       pool_unref(&auth->pool);
+}
+
+void master_login_auth_deinit(struct master_login_auth **_auth)
+{
+       struct master_login_auth *auth = *_auth;
+
+       *_auth = NULL;
+
+       master_login_auth_disconnect(auth);
+       master_login_auth_unref(&auth);
+}
+
+static struct master_login_auth_request *
+master_login_auth_lookup_request(struct master_login_auth *auth,
+                                unsigned int id)
+{
+       struct master_login_auth_request *request;
+
+       request = hash_table_lookup(auth->requests, POINTER_CAST(id));
+       if (request == NULL) {
+               i_error("Auth server sent reply with unknown ID %u", id);
+               return NULL;
+       }
+
+       hash_table_remove(auth->requests, POINTER_CAST(id));
+       return request;
+}
+
+static bool
+master_login_auth_input_user(struct master_login_auth *auth, const char *args)
+{
+       struct master_login_auth_request *request;
+       const char *const *list;
+       unsigned int id;
+
+       /* <id> <userid> [..] */
+
+       list = t_strsplit(args, "\t");
+       if (list[0] == NULL || list[1] == NULL) {
+               i_error("Auth server sent corrupted USER line");
+               return FALSE;
+       }
+       id = (unsigned int)strtoul(list[0], NULL, 10);
+
+       request = master_login_auth_lookup_request(auth, id);
+       if (request != NULL) {
+               request->callback(list + 1, request->context);
+               i_free(request);
+       }
+       return TRUE;
+}
+
+static bool
+master_login_auth_input_notfound(struct master_login_auth *auth,
+                                const char *args)
+{
+       struct master_login_auth_request *request;
+       unsigned int id;
+
+       id = (unsigned int)strtoul(args, NULL, 10);
+       request = master_login_auth_lookup_request(auth, id);
+       if (request != NULL) {
+               i_error("Auth request not found (timed out?): %u", id);
+               request->callback(NULL, request->context);
+               i_free(request);
+       }
+       return TRUE;
+}
+
+static bool
+master_login_auth_input_fail(struct master_login_auth *auth, const char *args)
+{
+       struct master_login_auth_request *request;
+       const char *error;
+       unsigned int id;
+
+       error = strchr(args, '\t');
+       if (error != NULL)
+               error++;
+
+       id = (unsigned int)strtoul(args, NULL, 10);
+       request = master_login_auth_lookup_request(auth, id);
+       if (request != NULL) {
+               request->callback(NULL, request->context);
+               i_free(request);
+       }
+       return TRUE;
+}
+
+static void master_login_auth_input(struct master_login_auth *auth)
+{
+       const char *line;
+       bool ret;
+
+       switch (i_stream_read(auth->input)) {
+       case 0:
+               return;
+       case -1:
+               /* disconnected */
+               master_login_auth_disconnect(auth);
+               return;
+       case -2:
+               /* buffer full */
+               i_error("Auth server sent us too long line");
+               master_login_auth_disconnect(auth);
+               return;
+       }
+
+       if (!auth->version_received) {
+               line = i_stream_next_line(auth->input);
+               if (line == NULL)
+                       return;
+
+               /* make sure the major version matches */
+               if (strncmp(line, "VERSION\t", 8) != 0 ||
+                   atoi(t_strcut(line + 8, '\t')) !=
+                   AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
+                       i_error("Authentication server not compatible with "
+                               "master process (mixed old and new binaries?)");
+                       master_login_auth_disconnect(auth);
+                       return;
+               }
+               auth->version_received = TRUE;
+       }
+
+       auth->refcount++;
+       while ((line = i_stream_next_line(auth->input)) != NULL) {
+               if (strncmp(line, "USER\t", 5) == 0)
+                       ret = master_login_auth_input_user(auth, line + 5);
+               else if (strncmp(line, "NOTFOUND\t", 9) == 0)
+                       ret = master_login_auth_input_notfound(auth, line + 9);
+               else if (strncmp(line, "FAIL\t", 5) == 0)
+                       ret = master_login_auth_input_fail(auth, line + 5);
+               else
+                       ret = TRUE;
+
+               if (!ret || auth->input == NULL) {
+                       master_login_auth_disconnect(auth);
+                       break;
+               }
+       }
+       master_login_auth_unref(&auth);
+}
+
+static int
+master_login_auth_connect(struct master_login_auth *auth)
+{
+       int fd;
+
+       i_assert(auth->fd == -1);
+
+       fd = net_connect_unix(auth->auth_socket_path);
+       if (fd == -1) {
+               i_error("net_connect_unix(%s) failed: %m",
+                       auth->auth_socket_path);
+               return -1;
+       }
+       auth->fd = fd;
+       auth->input = i_stream_create_fd(fd, AUTH_MAX_INBUF_SIZE, FALSE);
+       auth->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+       auth->io = io_add(fd, IO_READ, master_login_auth_input, auth);
+       return 0;
+}
+
+void master_login_auth_request(struct master_login_auth *auth,
+                              const struct master_auth_request *req,
+                              master_login_auth_request_callback_t *callback,
+                              void *context)
+{
+       struct master_login_auth_request *login_req;
+       unsigned int id;
+       string_t *str;
+
+       str = t_str_new(128);
+       if (auth->fd == -1) {
+               if (master_login_auth_connect(auth) < 0) {
+                       callback(NULL, context);
+                       return;
+               }
+               str_printfa(str, "VERSION\t%u\t%u\n",
+                           AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
+                           AUTH_MASTER_PROTOCOL_MINOR_VERSION);
+       }
+
+       id = ++auth->id_counter;
+       if (id == 0)
+               id++;
+
+       str_printfa(str, "REQUEST\t%u\t%u\t%u\t", id,
+                   req->client_pid, req->auth_id);
+       binary_to_hex_append(str, req->cookie, sizeof(req->cookie));
+       str_append_c(str, '\n');
+       o_stream_send(auth->output, str_data(str), str_len(str));
+
+       login_req = i_new(struct master_login_auth_request, 1);
+       login_req->callback = callback;
+       login_req->context = context;
+       hash_table_insert(auth->requests, POINTER_CAST(id), login_req);
+}
diff --git a/src/lib-master/master-login-auth.h b/src/lib-master/master-login-auth.h
new file mode 100644 (file)
index 0000000..3db896d
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef MASTER_LOGIN_AUTH_H
+#define MASTER_LOGIN_AUTH_H
+
+struct master_auth_request;
+
+typedef void
+master_login_auth_request_callback_t(const char *const *auth_args,
+                                    void *context);
+
+struct master_login_auth *master_login_auth_init(const char *auth_socket_path);
+void master_login_auth_deinit(struct master_login_auth **auth);
+
+void master_login_auth_request(struct master_login_auth *auth,
+                              const struct master_auth_request *req,
+                              master_login_auth_request_callback_t *callback,
+                              void *context);
+
+#endif
diff --git a/src/lib-master/master-login.c b/src/lib-master/master-login.c
new file mode 100644 (file)
index 0000000..f7d142a
--- /dev/null
@@ -0,0 +1,205 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "fdpass.h"
+#include "fd-close-on-exec.h"
+#include "llist.h"
+#include "master-login.h"
+#include "master-login-auth.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct master_login_connection {
+       struct master_login_connection *prev, *next;
+
+       struct master_login *login;
+       int fd;
+       struct io *io;
+       struct ostream *output;
+};
+
+struct master_login {
+       master_login_callback_t *callback;
+       struct master_login_connection *conns;
+       struct master_login_auth *auth;
+};
+
+static void master_login_conn_deinit(struct master_login_connection **_conn);
+
+struct master_login *
+master_login_init(const char *auth_socket_path,
+                 master_login_callback_t *callback)
+{
+       struct master_login *login;
+
+       login = i_new(struct master_login, 1);
+       login->callback = callback;
+       login->auth = master_login_auth_init(auth_socket_path);
+       return login;
+}
+
+void master_login_deinit(struct master_login **_login)
+{
+       struct master_login *login = *_login;
+
+       *_login = NULL;
+
+       master_login_auth_deinit(&login->auth);
+       while (login->conns != NULL) {
+               struct master_login_connection *conn = login->conns;
+
+               master_login_conn_deinit(&conn);
+       }
+       i_free(login);
+}
+
+static int
+master_login_conn_read_request(struct master_login_connection *conn,
+                              struct master_auth_request *req_r,
+                              unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
+                              int *client_fd_r)
+{
+       struct stat st;
+       ssize_t ret;
+
+       *client_fd_r = -1;
+
+       ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r);
+       if (ret != sizeof(*req_r)) {
+               if (ret == 0) {
+                       /* disconnected */
+               } else if (ret > 0) {
+                       /* request wasn't fully read */
+                       i_error("fd_read() partial input (%d/%d)",
+                               (int)ret, (int)sizeof(*req_r));
+               } else {
+                       if (errno == EAGAIN)
+                               return 0;
+
+                       i_error("fd_read() failed: %m");
+               }
+               return -1;
+       }
+
+       if (req_r->data_size != 0) {
+               if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
+                       i_error("Too large auth data_size sent");
+                       return -1;
+               }
+               /* @UNSAFE */
+               ret = read(conn->fd, data, req_r->data_size);
+               if (ret != (ssize_t)req_r->data_size) {
+                       if (ret == 0) {
+                               /* disconnected */
+                       } else if (ret > 0) {
+                               /* request wasn't fully read */
+                               i_error("Data read partially %d/%u",
+                                       (int)ret, req_r->data_size);
+                       } else {
+                               i_error("read(data) failed: %m");
+                       }
+                       return -1;
+               }
+       }
+
+       if (*client_fd_r == -1) {
+               i_error("Auth request missing a file descriptor");
+               return -1;
+       }
+
+       if (fstat(*client_fd_r, &st) < 0) {
+               i_error("fstat(fd_recv client) failed: %m");
+               return -1;
+       }
+       if (st.st_ino != req_r->ino) {
+               i_error("Auth request inode mismatch: %s != %s",
+                       dec2str(st.st_ino), dec2str(req_r->ino));
+               return -1;
+       }
+       return 1;
+}
+
+static void
+master_login_auth_callback(const char *const *auth_args, void *context)
+{
+       struct master_login_client *client = context;
+       struct master_auth_reply reply;
+
+       memset(&reply, 0, sizeof(reply));
+       reply.tag = client->auth_req.tag;
+       reply.status = auth_args != NULL ? MASTER_AUTH_STATUS_OK :
+               MASTER_AUTH_STATUS_INTERNAL_ERROR;
+       reply.mail_pid = getpid();
+       o_stream_send(client->conn->output, &reply, sizeof(reply));
+
+       if (auth_args == NULL) {
+               if (close(client->fd) < 0)
+                       i_error("close(fd_recv client) failed: %m");
+               i_free(client);
+               return;
+       }
+
+       client->conn->login->callback(client, auth_args[0], auth_args+1);
+       i_free(client);
+}
+
+static void master_login_conn_input(struct master_login_connection *conn)
+{
+       struct master_auth_request req;
+       struct master_login_client *client;
+       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
+       int ret, client_fd;
+
+       ret = master_login_conn_read_request(conn, &req, data, &client_fd);
+       if (ret <= 0) {
+               if (ret < 0)
+                       master_login_conn_deinit(&conn);
+               if (client_fd != -1) {
+                       if (close(client_fd) < 0)
+                               i_error("close(fd_recv client) failed: %m");
+               }
+               return;
+       }
+       fd_close_on_exec(client_fd, TRUE);
+
+       /* @UNSAFE: we have a request. do userdb lookup for it. */
+       client = i_malloc(sizeof(struct master_login_client) + req.data_size);
+       client->conn = conn;
+       client->fd = client_fd;
+       client->auth_req = req;
+       memcpy(client->data, data, req.data_size);
+
+       master_login_auth_request(conn->login->auth, &req,
+                                 master_login_auth_callback, client);
+}
+
+void master_login_add(struct master_login *login, int fd)
+{
+       struct master_login_connection *conn;
+
+       conn = i_new(struct master_login_connection, 1);
+       conn->login = login;
+       conn->fd = fd;
+       conn->io = io_add(conn->fd, IO_READ, master_login_conn_input, conn);
+       conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+
+       DLLIST_PREPEND(&login->conns, conn);
+}
+
+static void master_login_conn_deinit(struct master_login_connection **_conn)
+{
+       struct master_login_connection *conn = *_conn;
+
+       *_conn = NULL;
+
+       DLLIST_REMOVE(&conn->login->conns, conn);
+
+       io_remove(&conn->io);
+       o_stream_unref(&conn->output);
+       if (close(conn->fd) < 0)
+               i_error("close(master login) failed: %m");
+       i_free(conn);
+}
diff --git a/src/lib-master/master-login.h b/src/lib-master/master-login.h
new file mode 100644 (file)
index 0000000..74def20
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MASTER_LOGIN_H
+#define MASTER_LOGIN_H
+
+#include "master-auth.h"
+
+struct master_login_client {
+       struct master_login_connection *conn;
+       int fd;
+
+       struct master_auth_request auth_req;
+       unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+};
+
+typedef void
+master_login_callback_t(const struct master_login_client *client,
+                       const char *username, const char *const *extra_fields);
+
+struct master_login *
+master_login_init(const char *auth_socket_path,
+                 master_login_callback_t *callback);
+void master_login_deinit(struct master_login **login);
+
+void master_login_add(struct master_login *login, int fd);
+
+#endif
index 24e9523a49a49c49e1f18381b029ecda7fb1b0e6..996b3adc0a8a613a463368955b92cd7792fd7a7f 100644 (file)
@@ -35,8 +35,8 @@ struct master_service {
        struct master_status master_status;
 
        void (*avail_overflow_callback)(void);
+       struct timeout *to_overflow_state;
 
-        struct master_auth *auth;
        master_service_connection_callback_t *callback;
 
        pool_t set_pool;
@@ -48,6 +48,7 @@ struct master_service {
        unsigned int initial_status_sent:1;
        unsigned int die_with_master:1;
        unsigned int call_avail_overflow:1;
+       unsigned int delay_status_updates:1;
 };
 
 void master_service_io_listeners_add(struct master_service *service);
index 4c177cc8501871bf1382e8bcf0dfba8f304692ab..0f425ec5ea41dece7d4c2118ac97f4a35597c24d 100644 (file)
 /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
 #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
 
+/* when we're full of connections, how often to check if login state has
+   changed. we normally notice it immediately because of a signal, so this is
+   just a fallback against race conditions. */
+#define MASTER_SERVICE_STATE_CHECK_MSECS 1000
+
 struct master_service *master_service;
 
+static void master_service_refresh_login_state(struct master_service *service);
 static void io_listeners_remove(struct master_service *service);
 static void master_status_update(struct master_service *service);
 
@@ -51,6 +57,14 @@ static void sig_die(const siginfo_t *si, void *context)
        io_loop_stop(service->ioloop);
 }
 
+static void
+sig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context)
+{
+       struct master_service *service = context;
+
+       master_service_refresh_login_state(service);
+}
+
 static void master_service_verify_version(struct master_service *service)
 {
        if (service->version_string != NULL &&
@@ -261,6 +275,10 @@ void master_service_init_finish(struct master_service *service)
         lib_signals_ignore(SIGALRM, FALSE);
         lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
        lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
+       if ((service->flags & MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE) != 0) {
+               lib_signals_set_handler(SIGUSR1, TRUE,
+                                       sig_state_changed, service);
+       }
 
        if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
                if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
@@ -475,6 +493,47 @@ void master_service_client_connection_destroyed(struct master_service *service)
        }
 }
 
+static void master_service_set_login_state(struct master_service *service,
+                                          enum master_login_state state)
+{
+       if (service->to_overflow_state != NULL)
+               timeout_remove(&service->to_overflow_state);
+
+       switch (state) {
+       case MASTER_LOGIN_STATE_NONFULL:
+               service->call_avail_overflow = FALSE;
+               if (service->master_status.available_count > 0)
+                       return;
+
+               /* some processes should now be able to handle new connections,
+                  although we can't. but there may be race conditions, so
+                  make sure that we'll check again soon if the state has
+                  changed to "full" without our knowledge. */
+               service->to_overflow_state =
+                       timeout_add(MASTER_SERVICE_STATE_CHECK_MSECS,
+                                   master_service_refresh_login_state,
+                                   service);
+               return;
+       case MASTER_LOGIN_STATE_FULL:
+               /* make sure we're listening for more connections */
+               service->call_avail_overflow = TRUE;
+               master_service_io_listeners_add(service);
+               return;
+       }
+       i_error("Invalid master login state: %d", state);
+}
+
+static void master_service_refresh_login_state(struct master_service *service)
+{
+       int ret;
+
+       ret = lseek(MASTER_LOGIN_NOTIFY_FD, 0, SEEK_CUR);
+       if (ret < 0)
+               i_error("lseek(login notify fd) failed: %m");
+       else
+               master_service_set_login_state(service, ret);
+}
+
 static void master_service_close_config_fd(struct master_service *service)
 {
        if (service->config_fd != -1) {
@@ -493,6 +552,8 @@ void master_service_deinit(struct master_service **_service)
        io_listeners_remove(service);
 
        master_service_close_config_fd(service);
+       if (service->to_overflow_state != NULL)
+               timeout_remove(&service->to_overflow_state);
        if (service->io_status_error != NULL)
                io_remove(&service->io_status_error);
        if (service->io_status_write != NULL)
@@ -523,8 +584,11 @@ static void master_service_listen(struct master_service_listener *l)
                /* we are full. stop listening for now, unless overflow
                   callback destroys one of the existing connections */
                if (service->call_avail_overflow &&
-                   service->avail_overflow_callback != NULL)
+                   service->avail_overflow_callback != NULL) {
+                       service->delay_status_updates = TRUE;
                        service->avail_overflow_callback();
+                       service->delay_status_updates = FALSE;
+               }
 
                if (service->master_status.available_count == 0) {
                        io_listeners_remove(service);
@@ -631,7 +695,7 @@ static void master_status_update(struct master_service *service)
 {
        ssize_t ret;
 
-       if (service->master_status.pid == 0)
+       if (service->master_status.pid == 0 || service->delay_status_updates)
                return; /* closed */
 
        ret = write(MASTER_STATUS_FD, &service->master_status,
index b70a537697c748ee2e882a454d575dbbfe0df101..f4cd86752279c258259bda9ce5fedb772331d195 100644 (file)
@@ -17,7 +17,9 @@ enum master_service_flags {
        /* Don't read settings by executing config binary */
        MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS  = 0x10,
        /* Don't read settings from environment */
-       MASTER_SERVICE_FLAG_NO_ENV_SETTINGS     = 0x20
+       MASTER_SERVICE_FLAG_NO_ENV_SETTINGS     = 0x20,
+       /* Use MASTER_LOGIN_NOTIFY_FD to track login overflow state */
+       MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE   = 0x40
 };
 
 struct master_service_connection {
index 134d955f69664db19473f31370e4bc75dd3ec609..d198a74471949800e8ad6de585e3d1ee0f8d2228 100644 (file)
@@ -120,7 +120,7 @@ user_reply_handle(struct setting_parser_context *set_parser,
        for (i = 0; i < count && ret == 0; i++) {
                line = str[i];
                if (strncmp(line, "system_groups_user=", 19) == 0)
-                       *system_groups_user_r = line + 19;
+                       *system_groups_user_r = t_strdup(line + 19);
                else T_BEGIN {
                        if (strncmp(line, "mail=", 5) == 0) {
                                line = t_strconcat("mail_location=",
@@ -149,19 +149,15 @@ user_reply_handle(struct setting_parser_context *set_parser,
 
 static int
 service_auth_userdb_lookup(struct auth_master_connection *conn,
-                          struct setting_parser_context *set_parser,
                           const char *service_name,
                           const struct mail_storage_service_input *input,
                           const struct mail_user_settings *user_set,
-                          const char **user, const char **system_groups_user_r,
+                          pool_t pool, const char **user,
+                          const char *const **fields_r,
                           const char **error_r)
 {
        struct auth_user_info info;
-       struct auth_user_reply reply;
-       const char *system_groups_user, *orig_user = *user;
-       const char *new_username, *const *fields;
-       unsigned int len;
-       pool_t pool;
+       const char *new_username;
        int ret;
 
        memset(&info, 0, sizeof(info));
@@ -169,32 +165,19 @@ service_auth_userdb_lookup(struct auth_master_connection *conn,
        info.local_ip = input->local_ip;
        info.remote_ip = input->remote_ip;
 
-       pool = pool_alloconly_create("userdb lookup", 1024);
        ret = auth_master_user_lookup(conn, *user, &info, pool,
-                                     &new_username, &fields);
+                                     &new_username, fields_r);
        if (ret > 0) {
-               auth_user_fields_parse(fields, pool, &reply);
-               len = reply.chroot == NULL ? 0 : strlen(reply.chroot);
-
-               *user = t_strdup(new_username);
-               if (user_reply_handle(set_parser, user_set, &reply,
-                                     &system_groups_user, error_r) < 0)
-                       ret = -1;
-               *system_groups_user_r = t_strdup(system_groups_user);
-       } else {
-               if (ret == 0)
-                       *error_r = "unknown user";
-               else
-                       *error_r = "userdb lookup failed";
-               *system_groups_user_r = NULL;
-       }
-
-       if (ret > 0 && strcmp(*user, orig_user) != 0) {
-               if (mail_user_set_get_storage_set(user_set)->mail_debug)
-                       i_debug("changed username to %s", *user);
-       }
-
-       pool_unref(&pool);
+               if (strcmp(*user, new_username) != 0) {
+                       if (mail_user_set_get_storage_set(user_set)->mail_debug)
+                               i_debug("changed username to %s", new_username);
+                       *user = t_strdup(new_username);
+               }
+               *user = new_username;
+       } else if (ret == 0)
+               *error_r = "unknown user";
+       else
+               *error_r = "userdb lookup failed";
        return ret;
 }
 
@@ -515,13 +498,14 @@ init_user_real(struct master_service *service,
        struct mail_user *mail_user;
        struct auth_master_connection *conn;
        void **sets;
-       const char *user, *orig_user, *home, *system_groups_user, *error;
+       const char *user, *orig_user, *home, *error;
+       const char *system_groups_user = NULL, *const *userdb_fields = NULL;
        unsigned int len;
        bool userdb_lookup;
+       pool_t userdb_pool = NULL;
 
        io_loop_set_time_moved_callback(current_ioloop,
                                        mail_storage_service_time_moved);
-       master_service_init_finish(service);
 
        userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
        mail_storage_service_init_settings(service, &input, set_roots,
@@ -542,25 +526,36 @@ init_user_real(struct master_service *service,
                orig_user = user = input.username;
                conn = auth_master_init(user_set->auth_socket_path,
                                        mail_set->mail_debug);
-               if (service_auth_userdb_lookup(conn, service->set_parser,
-                                              service->name, &input,
-                                              user_set, &user,
-                                              &system_groups_user,
-                                              &error) <= 0)
+               userdb_pool = pool_alloconly_create("userdb lookup", 1024);
+               if (service_auth_userdb_lookup(conn, service->name, &input,
+                                              user_set, userdb_pool, &user,
+                                              &userdb_fields, &error) <= 0)
                        i_fatal("%s", error);
                auth_master_deinit(&conn);
                input.username = user;
 
                /* set up logging again in case username changed */
                mail_storage_service_init_log(service, &input);
+       } else if (input.userdb_fields != NULL) {
+               userdb_fields = input.userdb_fields;
+               userdb_pool = pool_alloconly_create("userdb fields", 1024);
+       }
+       if (userdb_fields != NULL) {
+               struct auth_user_reply reply;
+
+               auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
+               if (user_reply_handle(service->set_parser, user_set, &reply,
+                                     &system_groups_user, &error) < 0)
+                       i_fatal("%s", error);
        }
+       if (userdb_pool != NULL)
+               pool_unref(&userdb_pool);
 
        /* variable strings are expanded in mail_user_init(),
           but we need the home sooner so do it separately here. */
        home = user_expand_varstr(service, &input, user_set->mail_home);
 
        if (!userdb_lookup) {
-               system_groups_user = NULL;
                if (*home == '\0' && getenv("HOME") != NULL) {
                        home = getenv("HOME");
                        set_keyval(service->set_parser, "mail_home", home);
@@ -638,7 +633,6 @@ mail_storage_service_multi_init(struct master_service *service,
 
        io_loop_set_time_moved_callback(current_ioloop,
                                        mail_storage_service_time_moved);
-       master_service_init_finish(service);
 
        ctx = i_new(struct mail_storage_service_multi_ctx, 1);
        ctx->service = service;
@@ -676,6 +670,33 @@ mail_storage_service_multi_get_auth_conn(struct mail_storage_service_multi_ctx *
        return ctx->conn;
 }
 
+static int multi_userdb_lookup(struct mail_storage_service_multi_ctx *ctx,
+                              struct mail_storage_service_multi_user *user,
+                              pool_t userdb_pool, const char **error_r)
+{
+       struct auth_user_reply reply;
+       const char *username, *system_groups_user, *const *userdb_fields;
+       int ret;
+
+       username = user->input.username;
+       ret = service_auth_userdb_lookup(ctx->conn, ctx->service->name,
+                                        &user->input, user->user_set,
+                                        userdb_pool, &username,
+                                        &userdb_fields, error_r);
+       if (ret <= 0)
+               return ret;
+
+       auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
+       ret = user_reply_handle(ctx->service->set_parser, user->user_set,
+                               &reply, &system_groups_user, error_r);
+       if (ret <= 0)
+               return ret;
+
+       user->input.username = p_strdup(user->pool, username);
+       user->system_groups_user = p_strdup(user->pool, system_groups_user);
+       return 1;
+}
+
 int mail_storage_service_multi_lookup(struct mail_storage_service_multi_ctx *ctx,
                                      const struct mail_storage_service_input *input,
                                      pool_t pool,
@@ -683,9 +704,9 @@ int mail_storage_service_multi_lookup(struct mail_storage_service_multi_ctx *ctx
                                      const char **error_r)
 {
        struct mail_storage_service_multi_user *user;
-       const char *orig_user, *username;
        void **sets;
-       int ret;
+       pool_t userdb_pool;
+       int ret = 1;
 
        user = p_new(pool, struct mail_storage_service_multi_user, 1);
        memset(user_r, 0, sizeof(user_r));
@@ -698,15 +719,9 @@ int mail_storage_service_multi_lookup(struct mail_storage_service_multi_ctx *ctx
        user->user_set = sets[1];
 
        if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
-               orig_user = username = user->input.username;
-               ret = service_auth_userdb_lookup(ctx->conn, user->set_parser,
-                                                ctx->service->name, input,
-                                                user->user_set, &username,
-                                                &user->system_groups_user,
-                                                error_r);
-               if (ret <= 0)
-                       return ret;
-               user->input.username = p_strdup(pool, username);
+               userdb_pool = pool_alloconly_create("userdb lookup", 1024);
+               ret = multi_userdb_lookup(ctx, user, userdb_pool, error_r);
+               pool_unref(&userdb_pool);
        }
        *user_r = user;
        return 1;
index 9c5b2b30a0dcc677a30b8a475a1f66255f342b71..fd8cfe67f2dd7771c654c37523bfc72ffc90ec25 100644 (file)
@@ -25,6 +25,8 @@ struct mail_storage_service_input {
        const char *service;
        const char *username;
        struct ip_addr local_ip, remote_ip;
+
+       const char *const *userdb_fields;
 };
 
 struct setting_parser_info;
index cc2ade78e306316a4a57a2c8638ad3143f16914f..6bc653c549b348378ef22b3f5863279a1afe6d23 100644 (file)
@@ -118,7 +118,7 @@ void client_destroy(struct client *client, const char *reason)
                i_assert(client->authenticating);
                i_assert(client->refcount > 1);
                client->authenticating = FALSE;
-               master_auth_request_abort(master_service, client->master_tag);
+               master_auth_request_abort(master_auth, client->master_tag);
                client->refcount--;
        } else if (client->auth_request != NULL) {
                i_assert(client->authenticating);
index 0dd6fe0152ea5770c8f7765c05c421eb71ec4161..d44cfc0401c775b19eb4338451ef1b0148449c51 100644 (file)
@@ -16,6 +16,7 @@ extern const char *login_protocol, *login_process_name;
 extern unsigned int login_default_port;
 
 extern struct auth_client *auth_client;
+extern struct master_auth *master_auth;
 extern bool closing_down;
 extern int anvil_fd;
 
index a45c719af465ce585d058aa4cffd2d644938fb56..25eebddbdae32071e2d98905980aed3930e527c3 100644 (file)
@@ -19,6 +19,7 @@
 #include <syslog.h>
 
 struct auth_client *auth_client;
+struct master_auth *master_auth;
 bool closing_down;
 int anvil_fd = -1;
 
@@ -139,10 +140,10 @@ static void main_init(void)
 
        auth_client = auth_client_init("auth", (unsigned int)getpid(), FALSE);
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
+       master_auth = master_auth_init(master_service, login_protocol);
 
        clients_init();
        login_proxy_init();
-       master_auth_init(master_service);
 }
 
 static void main_deinit(void)
@@ -153,23 +154,25 @@ static void main_deinit(void)
        if (auth_client != NULL)
                auth_client_deinit(&auth_client);
        clients_deinit();
+       master_auth_deinit(&master_auth);
 
        if (anvil_fd != -1) {
                if (close(anvil_fd) < 0)
                        i_error("close(anvil) failed: %m");
        }
-       master_auth_deinit(master_service);
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
+       enum master_service_flags service_flags =
+               MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN |
+               MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE;
        const char *getopt_str;
        pool_t set_pool;
        int c;
 
-       master_service = master_service_init(login_process_name,
-                                       MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN,
-                                       argc, argv);
+       master_service = master_service_init(login_process_name, service_flags,
+                                            argc, argv);
        master_service_init_log(master_service, t_strconcat(
                login_process_name, ": ", NULL));
 
index c771ed3285debb22834857bf5d3622404f5c260d..a1cb98e116bc645d6006b10fe9c8bae809499fb4 100644 (file)
@@ -114,6 +114,7 @@ master_send_request(struct client *client, struct auth_client_request *request)
        req.auth_id = auth_client_request_get_id(request);
        req.local_ip = client->local_ip;
        req.remote_ip = client->ip;
+       req.client_pid = getpid();
 
        cookie = auth_client_get_cookie(auth_client);
        if (hex_to_binary(cookie, buf) == 0 && buf->used == sizeof(req.cookie))
@@ -127,9 +128,8 @@ master_send_request(struct client *client, struct auth_client_request *request)
        buffer_append(buf, data, size);
        req.data_size = buf->used;
 
-       client->master_tag =
-               master_auth_request(master_service, client->fd, &req, buf->data,
-                                   master_auth_callback, client);
+       master_auth_request(master_auth, client->fd, &req, buf->data,
+                           master_auth_callback, client, &client->master_tag);
 }
 
 static bool anvil_has_too_many_connections(struct client *client)
index 5d183babc3580b2331224aa0c94db6ae9f462973..526b75b18c1a54b651ddfdd156ec4630c9e91d29 100644 (file)
@@ -22,8 +22,6 @@ dovecot_SOURCES = \
        main.c \
        master-settings.c \
        service-anvil.c \
-       service-auth-server.c \
-       service-auth-source.c \
        service-listen.c \
        service-log.c \
        service-monitor.c \
@@ -37,8 +35,6 @@ noinst_HEADERS = \
        dup2-array.h \
        master-settings.h \
        service-anvil.h \
-       service-auth-server.h \
-       service-auth-source.h \
        service-listen.h \
        service-log.h \
        service-monitor.h \
index 1d78e6b38a9a3f74531add60d69162a75a9c8c56..43f9a721b53ccdaf7045601cc9ce33166a41023f 100644 (file)
@@ -200,42 +200,12 @@ static void fatal_log_check(const struct master_settings *set)
                i_error("unlink(%s) failed: %m", path);
 }
 
-static bool
-services_has_name(const struct master_settings *set, const char *name)
-{
-       struct service_settings *const *services;
-       unsigned int i, count;
-
-       services = array_get(&set->services, &count);
-       for (i = 0; i < count; i++) {
-               if (strcmp(services[i]->name, name) == 0)
-                       return TRUE;
-       }
-       return FALSE;
-}
-
-static bool services_have_auth_destinations(const struct master_settings *set)
-{
-       struct service_settings *const *services;
-       unsigned int i, count;
-
-       services = array_get(&set->services, &count);
-       for (i = 0; i < count; i++) {
-               if (strcmp(services[i]->type, "auth-source") == 0) {
-                       if (services_has_name(set, services[i]->auth_dest_service))
-                               return TRUE;
-               }
-       }
-       return FALSE;
-}
-
 static void auth_warning_print(const struct master_settings *set)
 {
        struct stat st;
 
        auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
-       if (!auth_success_written && !set->auth_debug &&
-           services_have_auth_destinations(set)) {
+       if (!auth_success_written && !set->auth_debug) {
                fprintf(stderr,
 "If you have trouble with authentication failures,\n"
 "enable auth_debug setting. See http://wiki.dovecot.org/WhyDoesItNotWork\n"
@@ -334,8 +304,7 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
 
        if (services->config->process_avail == 0) {
                /* we can't reload config if there's no config process. */
-               if (service_process_create(services->config,
-                                          NULL, NULL) == NULL) {
+               if (service_process_create(services->config) == NULL) {
                        i_error("Can't reload configuration because "
                                "we couldn't create a config process");
                        return;
index 619ea61c7f416b6a7d1178720f052c2f49d9f4d6..e8a3f3996e546fb2e63a1481aa65eadc824cc1b9 100644 (file)
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "env-util.h"
 #include "istream.h"
+#include "network.h"
 #include "str.h"
 #include "mkdir-parents.h"
 #include "safe-mkdir.h"
@@ -96,7 +97,6 @@ static struct setting_define service_setting_defines[] = {
        DEF(SET_STR, privileged_group),
        DEF(SET_STR, extra_groups),
        DEF(SET_STR, chroot),
-       DEF(SET_STR, auth_dest_service),
 
        DEF(SET_BOOL, drop_priv_before_exec),
 
@@ -128,7 +128,6 @@ static struct service_settings service_default_settings = {
        MEMBER(privileged_group) "",
        MEMBER(extra_groups) "",
        MEMBER(chroot) "",
-       MEMBER(auth_dest_service) "",
 
        MEMBER(drop_priv_before_exec) FALSE,
 
@@ -240,6 +239,27 @@ static void fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l,
        }
 }
 
+static bool master_settings_parse_type(struct service_settings *set,
+                                      const char **error_r)
+{
+       if (*set->type == '\0')
+               set->parsed_type = SERVICE_TYPE_UNKNOWN;
+       else if (strcmp(set->type, "log") == 0)
+               set->parsed_type = SERVICE_TYPE_LOG;
+       else if (strcmp(set->type, "config") == 0)
+               set->parsed_type = SERVICE_TYPE_CONFIG;
+       else if (strcmp(set->type, "anvil") == 0)
+               set->parsed_type = SERVICE_TYPE_ANVIL;
+       else if (strcmp(set->type, "login") == 0)
+               set->parsed_type = SERVICE_TYPE_LOGIN;
+       else {
+               *error_r = t_strconcat("Unknown service type: ",
+                                      set->type, NULL);
+               return FALSE;
+       }
+       return TRUE;
+}
+
 static bool
 master_settings_verify(void *_set, pool_t pool, const char **error_r)
 {
@@ -274,16 +294,8 @@ master_settings_verify(void *_set, pool_t pool, const char **error_r)
                                "Service #%d is missing name", i);
                        return FALSE;
                }
-               if (*service->type != '\0' &&
-                   strcmp(service->type, "log") != 0 &&
-                   strcmp(service->type, "config") != 0 &&
-                   strcmp(service->type, "anvil") != 0 &&
-                   strcmp(service->type, "auth") != 0 &&
-                   strcmp(service->type, "auth-source") != 0) {
-                       *error_r = t_strconcat("Unknown service type: ",
-                                              service->type, NULL);
+               if (!master_settings_parse_type(service, error_r))
                        return FALSE;
-               }
                for (j = 0; j < i; j++) {
                        if (strcmp(service->name, services[j]->name) == 0) {
                                *error_r = t_strdup_printf(
@@ -342,8 +354,7 @@ login_want_core_dumps(const struct master_settings *set, gid_t *gid_r)
 
        services = array_get(&set->services, &count);
        for (i = 0; i < count; i++) {
-               if (strcmp(services[i]->type, "auth-source") == 0 &&
-                   strstr(services[i]->name, "-login") != NULL) {
+               if (strcmp(services[i]->type, "login") == 0) {
                        if (strstr(services[i]->executable, " -D") != NULL)
                                cores = TRUE;
                        (void)get_uidgid(services[i]->user, &uid, gid_r, &error);
@@ -364,8 +375,7 @@ settings_have_auth_unix_listeners_in(const struct master_settings *set,
 
        services = array_get(&set->services, &count);
        for (i = 0; i < count; i++) {
-               if (strcmp(services[i]->type, "auth") == 0 &&
-                   array_is_created(&services[i]->unix_listeners)) {
+               if (array_is_created(&services[i]->unix_listeners)) {
                        u = array_get(&services[i]->unix_listeners, &count2);
                        for (j = 0; j < count2; j++) {
                                if (strncmp(u[j]->path, dir, strlen(dir)) == 0)
index f1e9e10db19fff567bd2106e5e94a43fae1a834d..98f0c8fbf366424dc47423143e5b7ad16b9ffe21 100644 (file)
@@ -1,6 +1,16 @@
 #ifndef MASTER_SETTINGS_H
 #define MASTER_SETTINGS_H
 
+/* <settings checks> */
+enum service_type {
+       SERVICE_TYPE_UNKNOWN,
+       SERVICE_TYPE_LOG,
+       SERVICE_TYPE_ANVIL,
+       SERVICE_TYPE_CONFIG,
+       SERVICE_TYPE_LOGIN
+};
+/* </settings checks> */
+
 struct file_listener_settings {
        const char *path;
        unsigned int mode;
@@ -27,7 +37,6 @@ struct service_settings {
        const char *privileged_group;
        const char *extra_groups;
        const char *chroot;
-       const char *auth_dest_service;
 
        bool drop_priv_before_exec;
 
@@ -40,6 +49,8 @@ struct service_settings {
        ARRAY_TYPE(file_listener_settings) unix_listeners;
        ARRAY_TYPE(file_listener_settings) fifo_listeners;
        ARRAY_DEFINE(inet_listeners, struct inet_listener_settings *);
+
+       enum service_type parsed_type;
 };
 
 struct master_settings {
diff --git a/src/master/service-auth-server.c b/src/master/service-auth-server.c
deleted file mode 100644 (file)
index cdbff6d..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "ostream.h"
-#include "hash.h"
-#include "service.h"
-#include "service-process.h"
-#include "service-auth-server.h"
-#include "service-auth-source.h"
-#include "../auth/auth-master-interface.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#define AUTH_MAX_INBUF_SIZE 8192
-
-static void
-service_process_auth_request_free(struct service_process_auth_request *request)
-{
-       if (request->fd != -1) {
-               if (close(request->fd) < 0)
-                       i_error("close(auth request fd) failed: %m");
-       }
-       i_free(request);
-}
-
-static void
-service_process_auth_server_close(struct service_process_auth_server *process)
-{
-       struct hash_iterate_context *iter;
-       void *key, *value;
-
-       if (process->auth_requests != NULL) {
-               iter = hash_table_iterate_init(process->auth_requests);
-               while (hash_table_iterate(iter, &key, &value)) {
-                       struct service_process_auth_request *request = value;
-
-                       service_process_unref(&request->process->process);
-                       service_process_auth_request_free(request);
-               }
-               hash_table_iterate_deinit(&iter);
-               hash_table_destroy(&process->auth_requests);
-       }
-
-       if (process->auth_input != NULL)
-               i_stream_close(process->auth_input);
-       if (process->auth_output != NULL)
-               o_stream_close(process->auth_output);
-
-       if (process->io_auth != NULL)
-               io_remove(&process->io_auth);
-       if (process->auth_fd != -1) {
-               if (close(process->auth_fd) < 0)
-                       i_error("close(auth_fd) failed: %m");
-               process->auth_fd = -1;
-       }
-}
-
-static struct service_process_auth_request *
-auth_process_lookup_request(struct service_process_auth_server *process,
-                           unsigned int id)
-{
-        struct service_process_auth_request *request;
-
-       request = hash_table_lookup(process->auth_requests, POINTER_CAST(id));
-       if (request == NULL) {
-               service_error(process->process.service,
-                             "authentication service %s "
-                             "sent reply with unknown ID %u",
-                             dec2str(process->process.pid), id);
-               return NULL;
-       }
-
-       hash_table_remove(process->auth_requests, POINTER_CAST(id));
-       if (!service_process_unref(&request->process->process)) {
-               /* process already died. */
-               service_process_auth_request_free(request);
-               return NULL;
-       }
-
-       return request;
-}
-
-static struct service *
-auth_process_get_dest_service(struct service_process_auth_source *process)
-{
-       struct service *service = process->process.service;
-
-       if (!service->list->destroyed)
-               return service->auth_dest_service;
-
-       service = service_lookup(services, service->set->auth_dest_service);
-       if (service == NULL) {
-               i_warning("service(%s): Lost destination service %s",
-                         service->set->name, service->set->auth_dest_service);
-       }
-       return service;
-}
-
-static int
-auth_process_input_user(struct service_process_auth_server *process, const char *args)
-{
-        struct service_process_auth_request *request;
-       const char *const *list;
-       enum master_auth_status status;
-       unsigned int id;
-
-       /* <id> <userid> [..] */
-
-       list = t_strsplit(args, "\t");
-       if (list[0] == NULL || list[1] == NULL) {
-               i_error("BUG: Auth process %s sent corrupted USER line",
-                       dec2str(process->process.pid));
-               return FALSE;
-       }
-       id = (unsigned int)strtoul(list[0], NULL, 10);
-
-        request = auth_process_lookup_request(process, id);
-       if (request != NULL) {
-               struct service *dest_service;
-               struct service_process *dest_process;
-
-               dest_service = auth_process_get_dest_service(request->process);
-               dest_process = dest_service == NULL ? NULL :
-                       service_process_create(dest_service, list + 1, request);
-               status = dest_process != NULL ?
-                       MASTER_AUTH_STATUS_OK :
-                       MASTER_AUTH_STATUS_INTERNAL_ERROR;
-               service_process_auth_source_send_reply(request->process,
-                                                      request->process_tag,
-                                                      status);
-               service_process_auth_request_free(request);
-       }
-       return TRUE;
-}
-
-static int
-auth_process_input_notfound(struct service_process_auth_server *process,
-                           const char *args)
-{
-        struct service_process_auth_request *request;
-       unsigned int id;
-
-       id = (unsigned int)strtoul(args, NULL, 10);
-
-        request = auth_process_lookup_request(process, id);
-       if (request != NULL) {
-               service_process_auth_source_send_reply(request->process,
-                                       request->process_tag,
-                                       MASTER_AUTH_STATUS_INTERNAL_ERROR);
-               service_process_auth_request_free(request);
-       }
-       return TRUE;
-}
-
-static int
-auth_process_input_fail(struct service_process_auth_server *process,
-                       const char *args)
-{
-        struct service_process_auth_request *request;
-       const char *error;
-       unsigned int id;
-
-       error = strchr(args, '\t');
-       if (error != NULL)
-               error++;
-
-       id = (unsigned int)strtoul(args, NULL, 10);
-
-        request = auth_process_lookup_request(process, id);
-       if (request != NULL) {
-               service_process_auth_source_send_reply(request->process,
-                                       request->process_tag,
-                                       MASTER_AUTH_STATUS_INTERNAL_ERROR);
-               service_process_auth_request_free(request);
-       }
-       return TRUE;
-}
-
-static void
-service_process_auth_server_input(struct service_process_auth_server *process)
-{
-       const char *line;
-       int ret;
-
-       switch (i_stream_read(process->auth_input)) {
-       case 0:
-               return;
-       case -1:
-               /* disconnected */
-               service_process_auth_server_close(process);
-               return;
-       case -2:
-               /* buffer full */
-               service_error(process->process.service,
-                             "authentication server process %s "
-                             "sent us too long line",
-                             dec2str(process->process.pid));
-               service_process_auth_server_close(process);
-               return;
-       }
-
-       if (!process->auth_version_received) {
-               line = i_stream_next_line(process->auth_input);
-               if (line == NULL)
-                       return;
-
-               /* make sure the major version matches */
-               if (strncmp(line, "VERSION\t", 8) != 0 ||
-                   atoi(t_strcut(line + 8, '\t')) !=
-                   AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
-                       service_error(process->process.service,
-                                     "authentication server process %s "
-                                     "not compatible with master process "
-                                     "(mixed old and new binaries?)",
-                                     dec2str(process->process.pid));
-                       service_process_auth_server_close(process);
-                       return;
-               }
-               process->auth_version_received = TRUE;
-       }
-
-       while ((line = i_stream_next_line(process->auth_input)) != NULL) {
-               if (strncmp(line, "USER\t", 5) == 0)
-                       ret = auth_process_input_user(process, line + 5);
-               else if (strncmp(line, "NOTFOUND\t", 9) == 0)
-                       ret = auth_process_input_notfound(process, line + 9);
-               else if (strncmp(line, "FAIL\t", 5) == 0)
-                       ret = auth_process_input_fail(process, line + 5);
-               else
-                       ret = TRUE;
-
-               if (!ret) {
-                       service_process_auth_server_close(process);
-                       break;
-               }
-       }
-}
-
-void service_process_auth_server_init(struct service_process *_process, int fd)
-{
-       struct service_process_auth_server *process =
-               (struct service_process_auth_server *)_process;
-
-       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
-
-       process->auth_fd = fd;
-       process->auth_input = i_stream_create_fd(process->auth_fd,
-                                                AUTH_MAX_INBUF_SIZE, FALSE);
-       process->auth_output =
-               o_stream_create_fd(fd, (size_t)-1, FALSE);
-       process->io_auth =
-               io_add(process->auth_fd, IO_READ,
-                      service_process_auth_server_input, process);
-       process->auth_requests =
-               hash_table_create(default_pool, default_pool, 0, NULL, NULL);
-}
-
-void service_process_auth_server_deinit(struct service_process *_process)
-{
-       struct service_process_auth_server *process =
-               (struct service_process_auth_server *)_process;
-
-       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
-
-       service_process_auth_server_close(process);
-}
diff --git a/src/master/service-auth-server.h b/src/master/service-auth-server.h
deleted file mode 100644 (file)
index a8d8603..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef SERVICE_AUTH_SERVER_H
-#define SERVICE_AUTH_SERVER_H
-
-struct service_process;
-
-void service_process_auth_server_init(struct service_process *process, int fd);
-void service_process_auth_server_deinit(struct service_process *process);
-
-#endif
diff --git a/src/master/service-auth-source.c b/src/master/service-auth-source.c
deleted file mode 100644 (file)
index d6b5c12..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "hash.h"
-#include "str.h"
-#include "hex-binary.h"
-#include "ioloop.h"
-#include "ostream.h"
-#include "fdpass.h"
-#include "fd-close-on-exec.h"
-#include "../auth/auth-master-interface.h"
-#include "service.h"
-#include "service-process.h"
-#include "service-auth-source.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-
-#define AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD (1024 - 256)
-#define AUTH_SERVER_MAX_OUTBUF_SIZE (1024*64)
-#define AUTH_BUSY_LOG_INTERVAL 30
-
-static void
-service_process_auth_source_input(struct service_process_auth_source *process);
-
-static void
-service_process_auth_source_close(struct service_process_auth_source *process)
-{
-       if (process->auth_output != NULL)
-               o_stream_close(process->auth_output);
-       if (process->io_auth != NULL)
-               io_remove(&process->io_auth);
-       if (process->auth_fd != -1) {
-               if (close(process->auth_fd) < 0)
-                       i_error("close(auth_fd) failed: %m");
-               process->auth_fd = -1;
-       }
-}
-
-static int
-process_auth_source_output(struct service_process_auth_source *process)
-{
-       int ret;
-
-       if ((ret = o_stream_flush(process->auth_output)) < 0)
-               return -1;
-
-       if (process->io_auth == NULL &&
-           o_stream_get_buffer_used_size(process->auth_output) <
-           AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
-               /* enable parsing input again */
-               o_stream_unset_flush_callback(process->auth_output);
-               process->io_auth = io_add(process->auth_fd, IO_READ,
-                                         service_process_auth_source_input,
-                                         process);
-       }
-       return ret;
-}
-
-void service_process_auth_source_send_reply(struct service_process_auth_source *process,
-                                           unsigned int tag,
-                                           enum master_auth_status status)
-{
-       struct master_auth_reply reply;
-
-       if (o_stream_get_buffer_used_size(process->auth_output) >=
-           AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
-               /* not reading our output. stop parsing input until it will. */
-               if (process->io_auth != NULL) {
-                       io_remove(&process->io_auth);
-
-                       o_stream_set_flush_callback(process->auth_output,
-                                                   process_auth_source_output,
-                                                   process);
-               }
-       }
-
-       /* Reply to login process */
-       memset(&reply, 0, sizeof(reply));
-       reply.tag = tag;
-       reply.status = status;
-
-       o_stream_send(process->auth_output, &reply, sizeof(reply));
-}
-
-static unsigned int
-auth_server_send_request(struct service_process_auth_server *server_process,
-                        struct service_process_auth_source *source_process,
-                        unsigned int auth_id,
-                        const uint8_t cookie[MASTER_AUTH_COOKIE_SIZE])
-{
-       unsigned int tag = 0;
-       string_t *str;
-
-       while (tag == 0)
-                tag = ++server_process->auth_tag_counter;
-
-       str = t_str_new(256);
-       if (!server_process->auth_version_sent) {
-                server_process->auth_version_sent = TRUE;
-               str_printfa(str, "VERSION\t%u\t%u\n",
-                           AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
-                           AUTH_MASTER_PROTOCOL_MINOR_VERSION);
-               o_stream_send(server_process->auth_output,
-                             str_data(str), str_len(str));
-               str_truncate(str, 0);
-       }
-
-       str_printfa(str, "REQUEST\t%u\t%s\t%u\t",
-                   tag, dec2str(source_process->process.pid), auth_id);
-       binary_to_hex_append(str, cookie, MASTER_AUTH_COOKIE_SIZE);
-       str_append_c(str, '\n');
-
-       o_stream_send(server_process->auth_output, str_data(str), str_len(str));
-       return tag;
-}
-
-static int
-auth_read_request(struct service_process_auth_source *process,
-                 struct master_auth_request *req,
-                 unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
-                 int *client_fd_r)
-{
-       struct service *service = process->process.service;
-       struct stat st;
-       ssize_t ret;
-
-       *client_fd_r = -1;
-
-       ret = fd_read(process->auth_fd, req, sizeof(*req), client_fd_r);
-       if (ret != sizeof(*req)) {
-               if (ret == 0) {
-                       /* disconnected */
-               } else if (ret > 0) {
-                       /* request wasn't fully read */
-                       service_error(service, "fd_read() partial input (%d/%d)",
-                                     (int)ret, (int)sizeof(*req));
-               } else {
-                       if (errno == EAGAIN)
-                               return 0;
-
-                       service_error(service, "fd_read() failed: %m");
-               }
-               return -1;
-       }
-
-       if (req->data_size != 0) {
-               if (req->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
-                       service_error(service, "Too large auth data_size sent");
-                       return -1;
-               }
-               /* @UNSAFE */
-               ret = read(process->auth_fd, data, req->data_size);
-               if (ret != (ssize_t)req->data_size) {
-                       if (ret == 0) {
-                               /* disconnected */
-                       } else if (ret > 0) {
-                               /* request wasn't fully read */
-                               service_error(service,
-                                             "Data read partially %d/%u",
-                                             (int)ret, req->data_size);
-                       } else {
-                               service_error(service, "read(data) failed: %m");
-                       }
-                       return -1;
-               }
-       }
-
-       if (*client_fd_r == -1) {
-               service_error(service, "Auth request missing a file descriptor");
-               return -1;
-       }
-
-       if (fstat(*client_fd_r, &st) < 0) {
-               service_error(service, "fstat(auth dest fd) failed: %m");
-               return -1;
-       }
-       if (st.st_ino != req->ino) {
-               service_error(service, "Auth request inode mismatch: %s != %s",
-                             dec2str(st.st_ino), dec2str(req->ino));
-               return -1;
-       }
-       return 1;
-}
-
-static void
-service_process_auth_source_input(struct service_process_auth_source *process)
-{
-       struct service *service = process->process.service;
-       struct service_process_auth_server *auth_process;
-       struct service_process_auth_request *auth_req;
-       struct master_auth_request req;
-       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
-       unsigned int tag;
-       ssize_t ret;
-       int client_fd;
-
-       ret = auth_read_request(process, &req, data, &client_fd);
-       if (ret == 0)
-               return;
-       if (ret < 0) {
-               if (client_fd != -1) {
-                       if (close(client_fd) < 0)
-                               i_error("login: close(mail client) failed: %m");
-               }
-               service_process_auth_source_close(process);
-               return;
-       }
-       fd_close_on_exec(client_fd, TRUE);
-
-       /* we have a request. check its validity. */
-       auth_process = hash_table_lookup(service_pids, &req.auth_pid);
-       if (auth_process == NULL) {
-               service_error(service, "authentication request for unknown "
-                             "auth server PID %s", dec2str(req.auth_pid));
-               service_process_auth_source_send_reply(process, req.tag,
-                       MASTER_AUTH_STATUS_INTERNAL_ERROR);
-               (void)close(client_fd);
-               return;
-       }
-
-       if (o_stream_get_buffer_used_size(auth_process->auth_output) >=
-           AUTH_SERVER_MAX_OUTBUF_SIZE) {
-               if (auth_process->auth_busy_stamp <=
-                   ioloop_time - AUTH_BUSY_LOG_INTERVAL) {
-                       i_warning("service(%s): authentication server PID "
-                                 "%s too busy",
-                                 auth_process->process.service->set->name,
-                                 dec2str(req.auth_pid));
-                        auth_process->auth_busy_stamp = ioloop_time;
-               }
-               service_process_auth_source_send_reply(process, req.tag,
-                       MASTER_AUTH_STATUS_INTERNAL_ERROR);
-               (void)close(client_fd);
-               return;
-       }
-
-       /* ok, we have a non-busy authentication server.
-          send a request to it. */
-       auth_req = i_malloc(sizeof(*auth_req) + req.data_size);
-       auth_req->process = process;
-       auth_req->process_tag = req.tag;
-       auth_req->fd = client_fd;
-       auth_req->local_ip = req.local_ip;
-       auth_req->remote_ip = req.remote_ip;
-       auth_req->data_size = req.data_size;
-       memcpy(auth_req->data, data, req.data_size);
-
-       tag = auth_server_send_request(auth_process, process, req.auth_id,
-                                      req.cookie);
-
-       service_process_ref(&process->process);
-       hash_table_insert(auth_process->auth_requests,
-                         POINTER_CAST(tag), auth_req);
-}
-
-void service_process_auth_source_init(struct service_process *_process, int fd)
-{
-       struct service_process_auth_source *process =
-               (struct service_process_auth_source *)_process;
-
-       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
-
-       process->auth_fd = fd;
-       process->io_auth = io_add(process->auth_fd, IO_READ,
-                                 service_process_auth_source_input, process);
-       process->auth_output =
-               o_stream_create_fd(fd, (size_t)-1, FALSE);
-}
-
-void service_process_auth_source_deinit(struct service_process *_process)
-{
-       struct service_process_auth_source *process =
-               (struct service_process_auth_source *)_process;
-
-       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
-
-       service_process_auth_source_close(process);
-}
-
-void service_processes_auth_source_notify(struct service *service,
-                                         bool all_processes_created)
-{
-       struct hash_iterate_context *iter;
-       void *key, *value;
-       enum master_auth_status status;
-
-       i_assert(service->type == SERVICE_TYPE_AUTH_SOURCE);
-
-       status = all_processes_created ? 1 : 0;
-
-       iter = hash_table_iterate_init(service_pids);
-       while (hash_table_iterate(iter, &key, &value)) {
-               struct service_process *process = value;
-               struct service_process_auth_source *auth_process;
-
-               if (process->service != service)
-                       continue;
-
-               auth_process = (struct service_process_auth_source *)process;
-               if (auth_process->last_notify_status != (int)status) {
-                       auth_process->last_notify_status = (int)status;
-                       service_process_auth_source_send_reply(auth_process,
-                                                              0, status);
-               }
-       }
-       hash_table_iterate_deinit(&iter);
-}
diff --git a/src/master/service-auth-source.h b/src/master/service-auth-source.h
deleted file mode 100644 (file)
index 273e71f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef SERVICE_AUTH_SOURCE_H
-#define SERVICE_AUTH_SOURCE_H
-
-struct service_process;
-struct service_process_auth_source;
-
-void service_process_auth_source_init(struct service_process *process, int fd);
-void service_process_auth_source_deinit(struct service_process *process);
-
-void service_process_auth_source_send_reply(struct service_process_auth_source *process,
-                                           unsigned int tag,
-                                           enum master_auth_status status);
-
-void service_processes_auth_source_notify(struct service *service,
-                                         bool all_processes_created);
-
-#endif
index 6fd1eeedbda058372db102cc6ca073847766e219..f61088a08424b0c7adad48bda5bab3ff5e4b58e8 100644 (file)
@@ -5,8 +5,9 @@
 #include "ioloop.h"
 #include "fd-close-on-exec.h"
 #include "hash.h"
+#include "str.h"
+#include "safe-mkstemp.h"
 #include "service.h"
-#include "service-auth-source.h"
 #include "service-process.h"
 #include "service-process-notify.h"
 #include "service-anvil.h"
@@ -59,6 +60,11 @@ static void service_status_more(struct service_process *process,
        i_assert(service->process_avail > 0);
        service->process_avail--;
 
+       if (service->type == SERVICE_TYPE_LOGIN &&
+           service->process_avail == 0 &&
+           service->process_count == service->process_limit)
+               service_login_notify(service, TRUE);
+
        /* we may need to start more  */
        service_monitor_start_extra_avail(service);
        service_monitor_listen_start(service);
@@ -90,6 +96,8 @@ static void service_status_less(struct service_process *process,
                                            process);
                }
        }
+       if (service->type == SERVICE_TYPE_LOGIN)
+               service_login_notify(service, FALSE);
 }
 
 static void service_status_input(struct service *service)
@@ -98,6 +106,7 @@ static void service_status_input(struct service *service)
         struct service_process *process;
        ssize_t ret;
 
+       status.pid = 0;
        ret = read(service->status_fd[0], &status, sizeof(status));
        switch (ret) {
        case 0:
@@ -177,11 +186,11 @@ static void service_drop_connections(struct service *service)
        service->listen_pending = TRUE;
        service_monitor_listen_stop(service);
 
-       if (service->type == SERVICE_TYPE_AUTH_SOURCE) {
+       if (service->type == SERVICE_TYPE_LOGIN) {
                /* reached process limit, notify processes that they
                   need to start killing existing connections if they
                   reach connection limit */
-               service_processes_auth_source_notify(service, TRUE);
+               service_login_notify(service, TRUE);
        }
 }
 
@@ -197,7 +206,7 @@ static void service_accept(struct service *service)
        }
 
        /* create a child process and let it accept() this connection */
-       if (service_process_create(service, NULL, NULL) == NULL)
+       if (service_process_create(service) == NULL)
                service_monitor_throttle(service);
        else
                service_monitor_listen_stop(service);
@@ -215,7 +224,7 @@ static void service_monitor_start_extra_avail(struct service *service)
                count = service->process_limit - service->process_count;
 
        for (i = 0; i < count; i++) {
-               if (service_process_create(service, NULL, NULL) == NULL) {
+               if (service_process_create(service) == NULL) {
                        service_monitor_throttle(service);
                        break;
                }
@@ -263,6 +272,38 @@ void service_monitor_listen_stop(struct service *service)
        service->listening = FALSE;
 }
 
+static int service_login_create_notify_fd(struct service *service)
+{
+       int fd;
+
+       if (service->login_notify_fd != -1)
+               return 0;
+
+       T_BEGIN {
+               string_t *prefix = t_str_new(128);
+               const char *path;
+
+               str_append(prefix, "/tmp/dovecot-master");
+
+               fd = safe_mkstemp(prefix, 0600, (uid_t)-1, (gid_t)-1);
+               path = str_c(prefix);
+
+               if (fd == -1) {
+                       service_error(service, "safe_mkstemp(%s) failed: %m",
+                                     path);
+               } else if (unlink(path) < 0) {
+                       service_error(service, "unlink(%s) failed: %m", path);
+               } else {
+                       fd_close_on_exec(fd, TRUE);
+                       service->login_notify_fd = fd;
+               }
+       } T_END;
+
+       if (fd != service->login_notify_fd)
+               (void)close(fd);
+       return fd == -1 ? -1 : 0;
+}
+
 void services_monitor_start(struct service_list *service_list)
 {
        struct service *const *services;
@@ -273,6 +314,10 @@ void services_monitor_start(struct service_list *service_list)
 
        services = array_get(&service_list->services, &count);
        for (i = 0; i < count; i++) {
+               if (services[i]->type == SERVICE_TYPE_LOGIN) {
+                       if (service_login_create_notify_fd(services[i]) < 0)
+                               continue;
+               }
                if (services[i]->status_fd[0] == -1) {
                        /* we haven't yet created status pipe */
                        if (pipe(services[i]->status_fd) < 0) {
@@ -289,14 +334,13 @@ void services_monitor_start(struct service_list *service_list)
                                io_add(services[i]->status_fd[0], IO_READ,
                                       service_status_input, services[i]);
                }
-
                if (services[i]->status_fd[0] != -1) {
                        service_monitor_start_extra_avail(services[i]);
                        service_monitor_listen_start(services[i]);
                }
        }
 
-       if (service_process_create(service_list->log, NULL, NULL) != NULL)
+       if (service_process_create(service_list->log) != NULL)
                service_monitor_listen_stop(service_list->log);
 }
 
@@ -316,6 +360,15 @@ void service_monitor_stop(struct service *service)
                        service->status_fd[i] = -1;
                }
        }
+       if (service->login_notify_fd != -1) {
+               if (close(service->login_notify_fd) < 0) {
+                       service_error(service,
+                                     "close(login notify fd) failed: %m");
+               }
+               service->login_notify_fd = -1;
+       }
+       if (service->to_login_notify != NULL)
+               timeout_remove(&service->to_login_notify);
        service_monitor_listen_stop(service);
 
        if (service->to_throttle != NULL)
index de35c59578c3cd72404dcb96ab192045628e7aa4..918374b0381225c664542d465249804d529ba1a8 100644 (file)
@@ -23,8 +23,6 @@
 #include "service.h"
 #include "service-anvil.h"
 #include "service-log.h"
-#include "service-auth-server.h"
-#include "service-auth-source.h"
 #include "service-process-notify.h"
 #include "service-process.h"
 
@@ -44,8 +42,7 @@
 #define CHDIR_WARN_SECS 10
 
 static void
-service_dup_fds(struct service *service, int auth_fd, int std_fd,
-               bool give_anvil_fd)
+service_dup_fds(struct service *service)
 {
        struct service_listener *const *listeners;
        ARRAY_TYPE(dup2) dups;
@@ -105,33 +102,14 @@ service_dup_fds(struct service *service, int auth_fd, int std_fd,
                }
        }
 
-       if (!give_anvil_fd)
-               dup2_append(&dups, null_fd, MASTER_ANVIL_FD);
-       else {
-               dup2_append(&dups, service->list->blocking_anvil_fd[1],
-                           MASTER_ANVIL_FD);
+       if (service->login_notify_fd != -1) {
+               dup2_append(&dups, service->login_notify_fd,
+                           MASTER_LOGIN_NOTIFY_FD);
        }
+       dup2_append(&dups, service->list->blocking_anvil_fd[1],
+                   MASTER_ANVIL_FD);
        dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
 
-       switch (service->type) {
-       case SERVICE_TYPE_AUTH_SOURCE:
-       case SERVICE_TYPE_AUTH_SERVER:
-               i_assert(auth_fd != -1);
-               dup2_append(&dups, auth_fd, MASTER_AUTH_FD);
-               env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
-               break;
-       default:
-               i_assert(auth_fd == -1);
-               dup2_append(&dups, null_fd, MASTER_AUTH_FD);
-               break;
-       }
-
-       if (std_fd != -1) {
-               dup2_append(&dups, std_fd, STDIN_FILENO);
-               dup2_append(&dups, std_fd, STDOUT_FILENO);
-               env_put("LOGGED_IN=1");
-       }
-
        if (service->type != SERVICE_TYPE_LOG) {
                /* set log file to stderr. dup2() here immediately so that
                   we can set up logging to it without causing any log messages
@@ -158,194 +136,11 @@ service_dup_fds(struct service *service, int auth_fd, int std_fd,
 }
 
 static void
-validate_uid_gid(struct master_settings *set,
-                uid_t uid, gid_t gid, const char *user,
-                const struct service_process_auth_request *request)
-{
-       struct service_process *request_process =
-               request == NULL ? NULL : &request->process->process;
-
-       if (uid == 0) {
-               i_fatal("User %s not allowed to log in using UNIX UID 0 "
-                       "(root logins are never allowed)", user);
-       }
-
-       if (request != NULL && request_process->service->uid == uid &&
-           master_uid != uid) {
-               struct passwd *pw;
-
-               pw = getpwuid(uid);
-               i_fatal("User %s not allowed to log in using %s's "
-                       "UNIX UID %s%s (see http://wiki.dovecot.org/UserIds)",
-                       user, request_process->service->set->name,
-                       dec2str(uid), pw == NULL ? "" :
-                       t_strdup_printf("(%s)", pw->pw_name));
-       }
-
-       if (uid < (uid_t)set->first_valid_uid ||
-           (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
-               struct passwd *pw;
-               bool low = uid < (uid_t)set->first_valid_uid;
-
-               pw = getpwuid(uid);
-               i_fatal("User %s not allowed to log in using too %s "
-                       "UNIX UID %s%s (see %s in config file)",
-                       user, low ? "low" : "high",
-                       dec2str(uid), pw == NULL ? "" :
-                       t_strdup_printf("(%s)", pw->pw_name),
-                       low ? "first_valid_uid" : "last_valid_uid");
-       }
-
-       if (gid < (gid_t)set->first_valid_gid ||
-           (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
-               struct group *gr;
-               bool low = gid < (gid_t)set->first_valid_gid;
-
-               gr = getgrgid(gid);
-               i_fatal("User %s not allowed to log in using too %s primary "
-                       "UNIX group ID %s%s (see %s in config file)",
-                       user, low ? "low" : "high",
-                       dec2str(gid), gr == NULL ? "" :
-                       t_strdup_printf("(%s)", gr->gr_name),
-                       low ? "first_valid_gid" : "last_valid_gid");
-       }
-}
-
-static void auth_args_apply(const char *const *args,
-                           struct restrict_access_settings *rset,
-                           const char **home)
-{
-       const char *key, *value;
-       string_t *expanded_vars;
-
-       expanded_vars = t_str_new(128);
-       str_append(expanded_vars, "VARS_EXPANDED=");
-       for (; *args != NULL; args++) {
-               if (strncmp(*args, "uid=", 4) == 0)
-                       rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
-               else if (strncmp(*args, "gid=", 4) == 0)
-                       rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
-               else if (strncmp(*args, "home=", 5) == 0) {
-                       *home = *args + 5;
-                       env_put(t_strconcat("HOME=", *args + 5, NULL));
-               } else if (strncmp(*args, "chroot=", 7) == 0)
-                       rset->chroot_dir = *args + 7;
-               else if (strncmp(*args, "system_groups_user=", 19) == 0)
-                       rset->system_groups_user = *args + 19;
-               else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
-                       rset->extra_groups =
-                               rset->extra_groups == NULL ? *args + 19 :
-                               t_strconcat(*args + 19, ",",
-                                           rset->extra_groups, NULL);
-               } else {
-                       /* unknown, set as environment */
-                       value = strchr(*args, '=');
-                       if (value == NULL) {
-                               /* boolean */
-                               key = *args;
-                               value = "=1";
-                       } else {
-                               key = t_strdup_until(*args, value);
-                               if (strcmp(key, "mail") == 0) {
-                                       /* FIXME: kind of ugly to have it
-                                          here.. */
-                                       key = "mail_location";
-                               }
-                       }
-                       str_append(expanded_vars, key);
-                       str_append_c(expanded_vars, ' ');
-                       env_put(t_strconcat(t_str_ucase(key), value, NULL));
-               }
-       }
-       env_put(str_c(expanded_vars));
-}        
-
-static void auth_success_write(void)
-{
-       int fd;
-
-       if (auth_success_written)
-               return;
-
-       fd = creat(AUTH_SUCCESS_PATH, 0666);
-       if (fd == -1)
-               i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
-       else
-               (void)close(fd);
-       auth_success_written = TRUE;
-}
-
-static void chdir_to_home(const struct restrict_access_settings *rset,
-                         const char *user, const char *home)
-{
-       unsigned int left;
-       int ret, chdir_errno;
-
-       if (*home != '/') {
-               i_fatal("user %s: Relative home directory paths not supported: "
-                       "%s", user, home);
-       }
-
-       /* if home directory is NFS-mounted, we might not have access to it as
-          root. Change the effective UID and GID temporarily to make it
-          work. */
-       if (rset->uid != master_uid) {
-               if (setegid(rset->gid) < 0)
-                       i_fatal("setegid(%s) failed: %m", dec2str(rset->gid));
-               if (seteuid(rset->uid) < 0)
-                       i_fatal("seteuid(%s) failed: %m", dec2str(rset->uid));
-       }
-
-       alarm(CHDIR_TIMEOUT);
-       ret = chdir(home);
-       chdir_errno = errno;
-       if ((left = alarm(0)) < CHDIR_TIMEOUT - CHDIR_WARN_SECS) {
-               i_warning("user %s: chdir(%s) blocked for %u secs",
-                         user, home, CHDIR_TIMEOUT - left);
-       }
-
-       errno = chdir_errno;
-       if (ret == 0) {
-               /* chdir succeeded */
-       } else if ((errno == ENOENT || errno == ENOTDIR || errno == EINTR) &&
-                  rset->chroot_dir == NULL) {
-               /* Not chrooted, fallback to using /tmp.
-
-                  ENOENT: No home directory yet, but it might be automatically
-                    created by the service process, so don't complain.
-                  ENOTDIR: This check is mainly for /dev/null home directory.
-                  EINTR: chdir() timed out. */
-       } else if (errno == EACCES) {
-               i_fatal("user %s: %s", user, eacces_error_get("chdir", home));
-       } else {
-               i_fatal("user %s: chdir(%s) failed with uid %s: %m",
-                       user, home, dec2str(rset->uid));
-       }
-       /* Change UID back. No need to change GID back, it doesn't
-          really matter. */
-       if (rset->uid != master_uid && seteuid(master_uid) < 0)
-               i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
-
-       if (ret < 0) {
-               /* We still have to change to some directory where we have
-                  rx-access. /tmp should exist everywhere. */
-               if (chdir("/tmp") < 0)
-                       i_fatal("chdir(/tmp) failed: %m");
-       }
-}
-
-static void
-drop_privileges(struct service *service, const char *const *auth_args,
-               const struct service_process_auth_request *request)
+drop_privileges(struct service *service)
 {
-       struct master_settings *master_set = service->set->master_set;
        struct restrict_access_settings rset;
-       const char *user, *home = NULL;
        bool disallow_root;
 
-       if (auth_args != NULL && service->set->master_set->mail_debug)
-               env_put("DEBUG=1");
-
        if (service->vsz_limit != 0)
                restrict_process_size(service->vsz_limit, -1U);
 
@@ -357,31 +152,9 @@ drop_privileges(struct service *service, const char *const *auth_args,
                service->set->chroot;
        rset.extra_groups = service->extra_gids;
 
-       if (auth_args == NULL) {
-               /* non-authenticating service. don't use *_valid_gid checks */
-       } else {
-               i_assert(auth_args[0] != NULL);
-
-               rset.first_valid_gid = master_set->first_valid_gid;
-               rset.last_valid_gid = master_set->last_valid_gid;
-
-               user = auth_args[0];
-               env_put(t_strconcat("USER=", user, NULL));
-
-               auth_success_write();
-               auth_args_apply(auth_args + 1, &rset, &home);
-
-               validate_uid_gid(master_set, rset.uid, rset.gid, user,
-                                request);
-       }
-
-       if (home != NULL)
-               chdir_to_home(&rset, user, home);
-
        if (service->set->drop_priv_before_exec) {
-               disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
-                       service->type == SERVICE_TYPE_AUTH_SOURCE;
-               restrict_access(&rset, home, disallow_root);
+               disallow_root = service->type == SERVICE_TYPE_LOGIN;
+               restrict_access(&rset, NULL, disallow_root);
        } else {
                restrict_access_set_env(&rset);
        }
@@ -451,120 +224,38 @@ static void service_process_status_timeout(struct service_process *process)
        timeout_remove(&process->to_status);
 }
 
-static void
-handle_request(const struct service_process_auth_request *request)
-{
-       string_t *str;
-
-       if (request == NULL)
-               return;
-
-       if (request->data_size > 0) {
-               str = t_str_new(request->data_size*3);
-               str_append(str, "CLIENT_INPUT=");
-               base64_encode(request->data, request->data_size, str);
-               env_put(str_c(str));
-       }
-
-       env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
-       env_put(t_strconcat("IP=", net_ip2addr(&request->remote_ip), NULL));
-}
-
-static const char **
-get_extra_args(struct service *service,
-              const struct service_process_auth_request *request,
-              const char *const *auth_args)
-{
-       const char **extra;
-
-       if (!service->set->master_set->verbose_proctitle || request == NULL)
-               return NULL;
-
-       extra = t_new(const char *, 2);
-       extra[0] = t_strdup_printf("[%s %s]", auth_args[0],
-                                  net_ip2addr(&request->remote_ip));
-       return extra;
-}
-
-struct service_process *
-service_process_create(struct service *service, const char *const *auth_args,
-                      const struct service_process_auth_request *request)
+struct service_process *service_process_create(struct service *service)
 {
        static unsigned int uid_counter = 0;
        struct service_process *process;
        unsigned int uid = ++uid_counter;
-       int fd[2];
        pid_t pid;
 
        if (service->to_throttle != NULL) {
                /* throttling service, don't create new processes */
                return NULL;
        }
-       if (service->process_count >= service->process_limit) {
-               /* we should get here only with auth dest services */
-               i_warning("service(%s): process_limit reached, "
-                         "dropping this client connection",
-                         service->set->name);
-               return NULL;
-       }
-
-       switch (service->type) {
-       case SERVICE_TYPE_AUTH_SOURCE:
-       case SERVICE_TYPE_AUTH_SERVER:
-               if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-                       service_error(service, "socketpair() failed: %m");
-                       return NULL;
-               }
-               fd_close_on_exec(fd[0], TRUE);
-               fd_close_on_exec(fd[1], TRUE);
-               break;
-       default:
-               fd[0] = fd[1] = -1;
-               break;
-       }
 
        pid = fork();
        if (pid < 0) {
                service_error(service, "fork() failed: %m");
-               if (fd[0] != -1) {
-                       (void)close(fd[0]);
-                       (void)close(fd[1]);
-               }
                return NULL;
        }
        if (pid == 0) {
                /* child */
-               if (fd[0] != -1)
-                       (void)close(fd[0]);
                service_process_setup_environment(service, uid);
-               handle_request(request);
-               service_dup_fds(service, fd[1], request == NULL ? -1 :
-                               request->fd, auth_args != NULL);
-               drop_privileges(service, auth_args, request);
-               process_exec(service->executable,
-                            get_extra_args(service, request, auth_args));
+               service_dup_fds(service);
+               drop_privileges(service);
+               process_exec(service->executable, NULL);
        }
 
        switch (service->type) {
-       case SERVICE_TYPE_AUTH_SERVER:
-               process = i_malloc(sizeof(struct service_process_auth_server));
-               process->service = service;
-               service_process_auth_server_init(process, fd[0]);
-               (void)close(fd[1]);
-               break;
-       case SERVICE_TYPE_AUTH_SOURCE:
-               process = i_malloc(sizeof(struct service_process_auth_source));
-               process->service = service;
-               service_process_auth_source_init(process, fd[0]);
-               (void)close(fd[1]);
-               break;
        case SERVICE_TYPE_ANVIL:
                service_anvil_process_created(service);
                /* fall through */
        default:
                process = i_new(struct service_process, 1);
                process->service = service;
-               i_assert(fd[0] == -1);
                break;
        }
 
@@ -604,12 +295,6 @@ void service_process_destroy(struct service_process *process)
                timeout_remove(&process->to_idle);
 
        switch (process->service->type) {
-       case SERVICE_TYPE_AUTH_SERVER:
-               service_process_auth_server_deinit(process);
-               break;
-       case SERVICE_TYPE_AUTH_SOURCE:
-               service_process_auth_source_deinit(process);
-               break;
        case SERVICE_TYPE_ANVIL:
                service_anvil_process_destroyed(service);
                break;
@@ -624,8 +309,8 @@ void service_process_destroy(struct service_process *process)
        service_process_unref(process);
 
        if (service->process_count < service->process_limit &&
-           service->type == SERVICE_TYPE_AUTH_SOURCE)
-               service_processes_auth_source_notify(service, FALSE);
+           service->type == SERVICE_TYPE_LOGIN)
+               service_login_notify(service, FALSE);
 
        service_list_unref(service_list);
 }
index 1d780c5b153f8490061b9ea8d7a912f4557208ff..4eefe65a02f4ec1b58b53aac82647fa9ea8ff175 100644 (file)
@@ -26,50 +26,10 @@ struct service_process {
        unsigned int destroyed:1;
 };
 
-struct service_process_auth_server {
-       struct service_process process;
+#define SERVICE_PROCESS_IS_INITIALIZED(process) \
+       ((process)->to_status == NULL)
 
-       int auth_fd;
-       struct io *io_auth;
-       struct ostream *auth_output;
-       struct istream *auth_input;
-
-       /* pending authentication requests that are being verified from
-          auth server. */
-       struct hash_table *auth_requests;
-       /* Last time we wrote "authentication server is too busy" to log */
-       time_t auth_busy_stamp;
-       /* Tag counter for outgoing requests */
-       unsigned int auth_tag_counter;
-
-       unsigned int auth_version_sent:1;
-       unsigned int auth_version_received:1;
-};
-
-struct service_process_auth_source {
-       struct service_process process;
-
-       int last_notify_status;
-
-       int auth_fd;
-       struct io *io_auth;
-       struct ostream *auth_output;
-};
-
-struct service_process_auth_request {
-       struct service_process_auth_source *process;
-
-       unsigned int process_tag;
-       int fd;
-
-       struct ip_addr local_ip, remote_ip;
-       unsigned int data_size;
-       unsigned char data[FLEXIBLE_ARRAY_MEMBER];
-};
-
-struct service_process *
-service_process_create(struct service *service, const char *const *auth_args,
-                      const struct service_process_auth_request *request);
+struct service_process *service_process_create(struct service *service);
 void service_process_destroy(struct service_process *process);
 
 void service_process_ref(struct service_process *process);
index 0c186482796462a491e58a1be3a3846ba5b90ff9..a3b180e7dec4623cbe6246f2fbcb2c1ae9ef96a0 100644 (file)
@@ -17,6 +17,7 @@
 #include <signal.h>
 
 #define SERVICE_DIE_TIMEOUT_MSECS (1000*10)
+#define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2
 
 struct hash_table *service_pids;
 
@@ -177,23 +178,7 @@ service_create(pool_t pool, const struct service_settings *set,
 
        service->vsz_limit = set->vsz_limit != 0 ? set->vsz_limit :
                set->master_set->default_vsz_limit;
-
-       service->type = SERVICE_TYPE_UNKNOWN;
-       if (*set->type != '\0') {
-               if (strcmp(set->type, "log") == 0)
-                       service->type = SERVICE_TYPE_LOG;
-               else if (strcmp(set->type, "config") == 0)
-                       service->type = SERVICE_TYPE_CONFIG;
-               else if (strcmp(set->type, "anvil") == 0)
-                       service->type = SERVICE_TYPE_ANVIL;
-               else if (strcmp(set->type, "auth") == 0)
-                       service->type = SERVICE_TYPE_AUTH_SERVER;
-               else if (strcmp(set->type, "auth-source") == 0)
-                       service->type = SERVICE_TYPE_AUTH_SOURCE;
-       }
-
-       if (*set->auth_dest_service != '\0')
-               service->type = SERVICE_TYPE_AUTH_SOURCE;
+       service->type = service->set->parsed_type;
 
        if (set->process_limit == 0) {
                /* unlimited */
@@ -258,6 +243,7 @@ service_create(pool_t pool, const struct service_settings *set,
        service->status_fd[0] = -1;
        service->status_fd[1] = -1;
        service->log_process_internal_fd = -1;
+       service->login_notify_fd = -1;
 
        if (array_is_created(&set->unix_listeners))
                unix_listeners = array_get(&set->unix_listeners, &unix_count);
@@ -358,7 +344,7 @@ int services_create(const struct master_settings *set,
                    struct service_list **services_r, const char **error_r)
 {
        struct service_list *service_list;
-       struct service *service, *const *services;
+       struct service *service;
        struct service_settings *const *service_settings;
        pool_t pool;
        const char *error;
@@ -411,23 +397,6 @@ int services_create(const struct master_settings *set,
                array_append(&service_list->services, &service, 1);
        }
 
-       /* resolve service dependencies */
-       services = array_get(&service_list->services, &count);
-       for (i = 0; i < count; i++) {
-               if (services[i]->type == SERVICE_TYPE_AUTH_SOURCE) {
-                       const char *dest_service =
-                               services[i]->set->auth_dest_service;
-                       services[i]->auth_dest_service =
-                               service_lookup(service_list, dest_service);
-                       if (services[i]->auth_dest_service == NULL) {
-                               *error_r = t_strdup_printf(
-                                       "auth_dest_service doesn't exist: %s",
-                                       dest_service);
-                               return -1;
-                       }
-               }
-       }
-
        if (service_list->log == NULL) {
                *error_r = "log service not specified";
                return -1;
@@ -452,6 +421,12 @@ void service_signal(struct service *service, int signo)
        for (; process != NULL; process = process->next) {
                i_assert(process->service == service);
 
+               if (!SERVICE_PROCESS_IS_INITIALIZED(process) &&
+                   signo != SIGKILL) {
+                       /* too early to signal it */
+                       continue;
+               }
+                   
                if (kill(process->pid, signo) < 0 && errno != ESRCH) {
                        service_error(service, "kill(%s, %d) failed: %m",
                                      dec2str(process->pid), signo);
@@ -459,6 +434,51 @@ void service_signal(struct service *service, int signo)
        }
 }
 
+static void service_login_notify_send(struct service *service)
+{
+       service->last_login_notify_time = ioloop_time;
+       if (service->to_login_notify != NULL)
+               timeout_remove(&service->to_login_notify);
+
+       service_signal(service, SIGUSR1);
+}
+
+static void service_login_notify_timeout(struct service *service)
+{
+       service_login_notify_send(service);
+}
+
+void service_login_notify(struct service *service, bool all_processes_full)
+{
+       enum master_login_state state;
+       int diff;
+
+       if (service->last_login_full_notify == all_processes_full ||
+           service->login_notify_fd == -1)
+               return;
+
+       /* change the state always immediately. it's cheap. */
+       service->last_login_full_notify = all_processes_full;
+       state = all_processes_full ? MASTER_LOGIN_STATE_FULL :
+               MASTER_LOGIN_STATE_NONFULL;
+       if (lseek(service->login_notify_fd, state, SEEK_SET) < 0)
+               service_error(service, "lseek(notify fd) failed: %m");
+
+       /* but don't send signal to processes too often */
+       diff = ioloop_time - service->last_login_notify_time;
+       if (diff < SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS) {
+               if (service->to_login_notify != NULL)
+                       return;
+
+               diff = (SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS - diff) * 1000;
+               service->to_login_notify =
+                       timeout_add(diff, service_login_notify_timeout,
+                                   service);
+       } else {
+               service_login_notify_send(service);
+       }
+}
+
 static void services_kill_timeout(struct service_list *service_list)
 {
        struct service *const *services, *log_service;
index 1a17ec106b215457b1565ee192bdda6755ee759f..8a9d6df285735d55459fcdf438ba240faf891cc5 100644 (file)
@@ -2,22 +2,12 @@
 #define SERVICE_H
 
 #include "network.h"
-
-struct master_settings;
+#include "master-settings.h"
 
 /* If a service process doesn't send its first status notification in
    this many seconds, kill the process */
 #define SERVICE_FIRST_STATUS_TIMEOUT_SECS 30
 
-enum service_type {
-       SERVICE_TYPE_UNKNOWN,
-       SERVICE_TYPE_LOG,
-       SERVICE_TYPE_ANVIL,
-       SERVICE_TYPE_CONFIG,
-       SERVICE_TYPE_AUTH_SERVER,
-       SERVICE_TYPE_AUTH_SOURCE
-};
-
 enum service_listener_type {
        SERVICE_LISTENER_UNIX,
        SERVICE_LISTENER_FIFO,
@@ -87,14 +77,16 @@ struct service {
        int status_fd[2];
        struct io *io_status;
 
+       /* Login process's notify fd. We change its seek position to
+          communicate state to login processes. */
+       int login_notify_fd;
+       time_t last_login_notify_time;
+       struct timeout *to_login_notify;
+
        /* if a process fails before servicing its first request, assume it's
           broken and start throtting new process creations */
        struct timeout *to_throttle;
 
-       /* SERVICE_TYPE_AUTH_SOURCE: Destination service to run after
-          successful authentication. */
-       struct service *auth_dest_service;
-
        /* Last time a "dropping client connections" warning was logged */
        time_t last_drop_warning;
 
@@ -104,6 +96,8 @@ struct service {
        unsigned int listening:1;
        /* TRUE if service has at least one inet_listener */
        unsigned int have_inet_listeners:1;
+       /* service_login_notify()'s last notification state */
+       unsigned int last_login_full_notify:1;
 };
 
 struct service_list {
@@ -122,7 +116,7 @@ struct service_list {
        int master_log_fd[2];
        struct service_process_notify *log_byes;
 
-       /* passed to auth destination processes */
+       /* passed to child processes */
        int blocking_anvil_fd[2];
        /* used by master process to notify about dying processes */
        int nonblocking_anvil_fd[2];
@@ -154,6 +148,10 @@ const char *services_get_config_socket_path(struct service_list *service_list);
 
 /* Send a signal to all processes in a given service */
 void service_signal(struct service *service, int signo);
+/* Notify all processes (if necessary) that no more connections can be handled
+   by the service without killing existing connections (TRUE) or that they
+   can be (FALSE). */
+void service_login_notify(struct service *service, bool all_processes_full);
 
 /* Prevent service from launching new processes for a while. */
 void service_throttle(struct service *service, unsigned int secs);
index 4e0523bdb79e959838e0032df6bc8360e3332741..d6f909ae7c3e896fa16814b0de431673c13cd434 100644 (file)
@@ -2,12 +2,14 @@
 
 #include "pop3-common.h"
 #include "ioloop.h"
-#include "istream.h"
 #include "buffer.h"
+#include "istream.h"
+#include "ostream.h"
 #include "base64.h"
 #include "restrict-access.h"
 #include "process-title.h"
 #include "master-service.h"
+#include "master-login.h"
 #include "master-interface.h"
 #include "var-expand.h"
 #include "mail-storage-service.h"
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("LOGGED_IN") == NULL)
+        (getenv(MASTER_UID_ENV) == NULL)
+
+static const struct setting_parser_info *set_roots[] = {
+       &pop3_setting_parser_info,
+       NULL
+};
+static struct master_login *master_login = NULL;
+static enum mail_storage_service_flags storage_service_flags = 0;
+static bool user_initialized = FALSE;
 
 void (*hook_client_created)(struct client **client) = NULL;
 
-static bool main_init(const struct pop3_settings *set, struct mail_user *user)
+static void client_add_input(struct client *client, const buffer_t *buf)
 {
-       struct client *client;
-       const char *str;
-       bool ret = TRUE;
-
-       if (set->shutdown_clients)
-               master_service_set_die_with_master(master_service, TRUE);
+       struct ostream *output;
 
-       client = client_create(0, 1, user, set);
-       if (client == NULL)
-               return FALSE;
+       if (buf != NULL && buf->used > 0) {
+               if (!i_stream_add_data(client->input, buf->data, buf->used))
+                       i_panic("Couldn't add client input to stream");
+       }
 
+       output = client->output;
+       o_stream_ref(output);
+       o_stream_cork(output);
        if (!IS_STANDALONE())
                client_send_line(client, "+OK Logged in.");
-
-       str = getenv("CLIENT_INPUT");
-       if (str != NULL) T_BEGIN {
-               buffer_t *buf = t_base64_decode_str(str);
-               if (buf->used > 0) {
-                       if (!i_stream_add_data(client->input, buf->data,
-                                              buf->used))
-                               i_panic("Couldn't add client input to stream");
-                       ret = client_handle_input(client);
-               }
-       } T_END;
-       return ret;
+       (void)client_handle_input(client);
+       o_stream_uncork(output);
+       o_stream_unref(&output);
 }
 
-static void main_deinit(void)
+static void
+main_stdio_init_user(const struct pop3_settings *set, struct mail_user *user)
 {
-       clients_destroy_all();
-}
+       struct client *client;
+       buffer_t *input_buf;
+       const char *input_base64;
 
-static void client_connected(const struct master_service_connection *conn)
-{
-       /* we can't handle this yet */
-       (void)close(conn->fd);
+       input_base64 = getenv("CLIENT_INPUT");
+       input_buf = input_base64 == NULL ? NULL :
+               t_base64_decode_str(input_base64);
+
+       client = client_create(STDIN_FILENO, STDOUT_FILENO, user, set);
+       client_add_input(client, input_buf);
 }
 
-int main(int argc, char *argv[], char *envp[])
+static void main_stdio_run(void)
 {
-       const struct setting_parser_info *set_roots[] = {
-               &pop3_setting_parser_info,
-               NULL
-       };
-       enum master_service_flags service_flags =
-               MASTER_SERVICE_FLAG_STD_CLIENT;
-       enum mail_storage_service_flags storage_service_flags = 0;
        struct mail_storage_service_input input;
        struct mail_user *mail_user;
        const struct pop3_settings *set;
        const char *value;
-       int c;
-
-       if (IS_STANDALONE() && getuid() == 0 &&
-           net_getpeername(1, NULL, NULL) == 0) {
-               printf("-ERR pop3 binary must not be started from "
-                      "inetd, use pop3-login instead.\n");
-               return 1;
-       }
-
-       if (IS_STANDALONE())
-               service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
-       else {
-               storage_service_flags |=
-                       MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
-                       MAIL_STORAGE_SERVICE_FLAG_RESTRICT_BY_ENV;
-       }
-
-       master_service = master_service_init("pop3", service_flags, argc, argv);
-       while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
-               if (!master_service_parse_option(master_service, c, optarg))
-                       exit(FATAL_DEFAULT);
-       }
 
        memset(&input, 0, sizeof(input));
-       input.module = "pop3";
-       input.service = "pop3";
+       input.module = input.service = "pop3";
        input.username = getenv("USER");
        if (input.username == NULL && IS_STANDALONE())
                input.username = getlogin();
-       if (input.username == NULL) {
-               if (getenv(MASTER_UID_ENV) == NULL)
-                       i_fatal("USER environment missing");
-               else {
-                       i_fatal("login_executable setting must be pop3-login, "
-                               "not pop3");
-               }
-       }
+       if (input.username == NULL)
+               i_fatal("USER environment missing");
        if ((value = getenv("IP")) != NULL)
                net_addr2ip(value, &input.remote_ip);
        if ((value = getenv("LOCAL_IP")) != NULL)
                net_addr2ip(value, &input.local_ip);
 
+       user_initialized = TRUE;
        mail_user = mail_storage_service_init_user(master_service,
                                                   &input, set_roots,
                                                   storage_service_flags);
        set = mail_storage_service_get_settings(master_service);
        restrict_access_allow_coredumps(TRUE);
+       if (set->shutdown_clients)
+               master_service_set_die_with_master(master_service, TRUE);
 
-        process_title_init(argv, envp);
+       /* fake that we're running, so we know if client was destroyed
+          while handling its initial input */
+       io_loop_set_running(current_ioloop);
+       main_stdio_init_user(set, mail_user);
+}
+
+static void
+login_client_connected(const struct master_login_client *client,
+                      const char *username, const char *const *extra_fields)
+{
+       struct mail_storage_service_input input;
+       struct mail_user *mail_user;
+       struct client *pop3_client;
+       const struct pop3_settings *set;
+       buffer_t input_buf;
+
+       if (pop3_clients != NULL) {
+               i_error("Can't handle more than one connection currently");
+               (void)close(client->fd);
+               return;
+       }
+       i_assert(!user_initialized);
+
+       memset(&input, 0, sizeof(input));
+       input.module = input.service = "pop3";
+       input.local_ip = client->auth_req.local_ip;
+       input.remote_ip = client->auth_req.remote_ip;
+       input.username = username;
+       input.userdb_fields = extra_fields;
+
+       if (input.username == NULL) {
+               i_error("login client: Username missing from auth reply");
+               (void)close(client->fd);
+               return;
+       }
+       user_initialized = TRUE;
+       master_login_deinit(&master_login);
+
+       mail_user = mail_storage_service_init_user(master_service,
+                                                  &input, set_roots,
+                                                  storage_service_flags);
+       set = mail_storage_service_get_settings(master_service);
+       restrict_access_allow_coredumps(TRUE);
+       if (set->shutdown_clients)
+               master_service_set_die_with_master(master_service, TRUE);
 
        /* fake that we're running, so we know if client was destroyed
-          while initializing */
+          while handling its initial input */
        io_loop_set_running(current_ioloop);
 
-       if (main_init(set, mail_user))
+       buffer_create_const_data(&input_buf, client->data,
+                                client->auth_req.data_size);
+       pop3_client = client_create(client->fd, client->fd, mail_user, set);
+       T_BEGIN {
+               client_add_input(pop3_client, &input_buf);
+       } T_END;
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+       if (master_login == NULL) {
+               /* running standalone, we shouldn't even get here */
+               (void)close(conn->fd);
+       } else {
+               master_login_add(master_login, conn->fd);
+       }
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       enum master_service_flags service_flags = 0;
+       int c;
+
+       if (IS_STANDALONE() && getuid() == 0 &&
+           net_getpeername(1, NULL, NULL) == 0) {
+               printf("-ERR pop3 binary must not be started from "
+                      "inetd, use pop3-login instead.\n");
+               return 1;
+       }
+
+       if (IS_STANDALONE()) {
+               service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+                       MASTER_SERVICE_FLAG_STD_CLIENT;
+       } else {
+               storage_service_flags |=
+                       MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
+       }
+
+       master_service = master_service_init("pop3", service_flags, argc, argv);
+       while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
+               if (!master_service_parse_option(master_service, c, optarg))
+                       exit(FATAL_DEFAULT);
+       }
+        process_title_init(argv, envp);
+       master_service_init_finish(master_service);
+
+       if (IS_STANDALONE()) {
+               T_BEGIN {
+                       main_stdio_run();
+               } T_END;
+       } else {
+               master_login = master_login_init("auth-master",
+                                                login_client_connected);
+               io_loop_set_running(current_ioloop);
+       }
+
+       if (io_loop_is_running(current_ioloop))
                master_service_run(master_service, client_connected);
+       clients_destroy_all();
 
-       main_deinit();
-       mail_storage_service_deinit_user();
+       if (master_login != NULL)
+               master_login_deinit(&master_login);
+       if (user_initialized)
+               mail_storage_service_deinit_user();
        master_service_deinit(&master_service);
        return 0;
 }
index f00293c73c68f283d08f6aea058875d90c1daa19..5638f219efb315bd05d3d8b9f36b34c20debdf6b 100644 (file)
@@ -35,7 +35,7 @@
    transaction. This allows the mailbox to become unlocked. */
 #define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
 
-static struct client *pop3_clients;
+struct client *pop3_clients;
 
 static void client_input(struct client *client);
 static int client_output(struct client *client);
index a70b1bc278e9e272d8ff917cde5051cdaba95dca..7af4149c9babe8a7084af959c6e630247b6f81ce 100644 (file)
@@ -60,6 +60,8 @@ struct client {
        unsigned int anvil_sent:1;
 };
 
+extern struct client *pop3_clients;
+
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
 struct client *client_create(int fd_in, int fd_out, struct mail_user *user,