]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap: Send tagged login reply before finalizing user initialization
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 21 May 2021 13:11:45 +0000 (16:11 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 25 May 2021 12:41:36 +0000 (12:41 +0000)
Broken by 5fc66f182ff6941639d30372b414c1b39ae1e67e

src/imap/imap-client.c
src/imap/imap-client.h
src/imap/imap-master-client.c
src/imap/main.c

index 36db6fce45525fc9157d8b2aac9ac5eaab4d932d..da56eb21eb3eeb50ded40edd8d276ab0c3260adf 100644 (file)
@@ -219,19 +219,21 @@ struct client *client_create(int fd_in, int fd_out,
        return client;
 }
 
-int client_create_finish(struct client *client, const char **error_r)
+void client_create_finish_io(struct client *client)
 {
-       if (mail_namespaces_init(client->user, error_r) < 0)
-               return -1;
-       mail_namespaces_set_storage_callbacks(client->user->namespaces,
-                                             &mail_storage_callbacks, client);
-
        if (client->set->rawlog_dir[0] != '\0') {
                (void)iostream_rawlog_create(client->set->rawlog_dir,
                                             &client->input, &client->output);
        }
        client->io = io_add_istream(client->input, client_input, client);
+}
 
+int client_create_finish(struct client *client, const char **error_r)
+{
+       if (mail_namespaces_init(client->user, error_r) < 0)
+               return -1;
+       mail_namespaces_set_storage_callbacks(client->user->namespaces,
+                                             &mail_storage_callbacks, client);
        client->v.init(client);
        return 0;
 }
index 153320919f7bf1dd04e909f71ac40d2eea5cb2ac..24c22b23b35bd3f58ff974e8e31165f68f8d4353 100644 (file)
@@ -266,6 +266,7 @@ struct client *client_create(int fd_in, int fd_out,
                             struct mail_storage_service_user *service_user,
                             const struct imap_settings *set,
                             const struct smtp_submit_settings *smtp_set);
+void client_create_finish_io(struct client *client);
 /* Finish creating the client. Returns 0 if ok, -1 if there's an error. */
 int client_create_finish(struct client *client, const char **error_r);
 void client_add_istream_prefix(struct client *client,
index ffc7fb9535de30171d47c06867339b29588e5a8b..181d6bb4f93fe21c1e13abccc579c513ae49f388 100644 (file)
@@ -287,6 +287,7 @@ imap_master_client_input_args(struct connection *conn, const char *const *args,
                                          master_input.client_input->used);
        }
 
+       client_create_finish_io(imap_client);
        if (client_create_finish(imap_client, &error) < 0) {
                event_add_str(event, "error", error);
                e_error(event, "imap-master: %s", error);
index e16bac02cdc68d06b7366c346366407ff93024b5..5e8526d98f3baa432881e67303e39495661a0bd0 100644 (file)
@@ -352,11 +352,12 @@ static void main_stdio_run(const char *username)
                                 &request);
        }
 
-       if (client_create_finish(client, &error) < 0)
-               i_fatal("%s", error);
+       client_create_finish_io(client);
        client_send_login_reply(client->output,
                                str_c(client->capability_string),
                                client->user->username, &request);
+       if (client_create_finish(client, &error) < 0)
+               i_fatal("%s", error);
        client_add_input_finalize(client);
        /* client may be destroyed now */
 }
@@ -365,6 +366,7 @@ static void
 login_client_connected(const struct master_login_client *login_client,
                       const char *username, const char *const *extra_fields)
 {
+#define MSG_BYE_INTERNAL_ERROR "* BYE "MAIL_ERRSTR_CRITICAL_MSG"\r\n"
        struct mail_storage_service_input input;
        struct client *client;
        struct imap_login_request request;
@@ -390,6 +392,7 @@ login_client_connected(const struct master_login_client *login_client,
                int fd = login_client->fd;
                struct ostream *output =
                        o_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE);
+               i_zero(&request);
                client_send_login_reply(output, NULL, NULL, &request);
                o_stream_destroy(&output);
 
@@ -402,18 +405,27 @@ login_client_connected(const struct master_login_client *login_client,
        client_add_input(client, login_client->data,
                         login_client->auth_req.data_size, &request);
 
-       /* finish initializing the user (see comment in main()) */
+       /* The order here is important:
+          1. Finish setting up rawlog, so all input/output is written there.
+          2. Send tagged reply to login before any potentially long-running
+             work (during which client could disconnect due to timeout).
+          3. Finish initializing user, which can potentially take a long time.
+       */
+       client_create_finish_io(client);
+       client_send_login_reply(client->output,
+                               str_c(client->capability_string),
+                               NULL, &request);
        if (client_create_finish(client, &error) < 0) {
-               /* Even though client initialization failed, send the login
-                  OK reply so client doesn't think that the login failed. */
-               client_send_login_reply(client->output, NULL, NULL, &request);
+               if (write_full(login_client->fd, MSG_BYE_INTERNAL_ERROR,
+                              strlen(MSG_BYE_INTERNAL_ERROR)) < 0)
+                       if (errno != EAGAIN && errno != EPIPE)
+                               e_error(client->event,
+                                       "write_full(client) failed: %m");
+
                e_error(client->event, "%s", error);
                client_destroy(client, error);
                return;
        }
-       client_send_login_reply(client->output,
-                               str_c(client->capability_string),
-                               NULL, &request);
 
        client_add_input_finalize(client);
        /* client may be destroyed now */