]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added support for userdb lookup to fail with a reason (many API changes).
authorTimo Sirainen <tss@iki.fi>
Wed, 31 Mar 2010 16:21:09 +0000 (19:21 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 31 Mar 2010 16:21:09 +0000 (19:21 +0300)
--HG--
branch : HEAD

20 files changed:
doc/auth-protocol.txt
src/doveadm/doveadm-auth.c
src/doveadm/doveadm-mail.c
src/dsync/dsync.c
src/imap/main.c
src/lib-auth/auth-master.c
src/lib-auth/auth-master.h
src/lib-master/master-auth.c
src/lib-master/master-auth.h
src/lib-master/master-login-auth.c
src/lib-master/master-login-auth.h
src/lib-master/master-login.c
src/lib-master/master-login.h
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-storage-service.h
src/lmtp/commands.c
src/login-common/client-common-auth.c
src/login-common/sasl-server.c
src/login-common/sasl-server.h
src/pop3/main.c

index 77abb9202da32022de809e0efd3da281beadb4a1..00201392fce8fea146198fb6fb087476d452aa41 100644 (file)
@@ -164,7 +164,7 @@ Master Requests
  M: "USER" TAB <id> TAB <userid> TAB service=<service> [TAB <parameters>]
 
  S: "NOTFOUND" TAB <id>
- S: "FAIL" TAB <id> TAB <error message>
+ S: "FAIL" TAB <id> [TAB <parameters>]
  S: "USER" TAB <id> TAB <userid> [TAB <parameters>]
 
 Master commands can request information about existing authentication
@@ -180,7 +180,10 @@ NOTFOUND reply means that the user wasn't found.
 
 FAIL reply means an internal error occurred. Usually either a configuration
 mistake or temporary error caused by lost resource (eg. database down).
-Also unknown request IDs are reported as FAILs.
+Also unknown request IDs are reported as FAILs. Currently the only
+specified parameter is "reason", which is used when user is wanted to be
+put into "temporarily disabled" state and the reason string will be shown
+to user on login or to LMTP RCPT TO reply.
 
 USER reply is sent if request succeeded. It can return parameters:
 
index 89e30723063bb683efe20f3bde7abde571c82cb5..99aa68f1a6a10ba640de3791f783543c4ffbc030 100644 (file)
@@ -37,9 +37,14 @@ cmd_user_input(const char *auth_socket_path, const struct authtest_input *input)
        conn = auth_master_init(auth_socket_path, 0);
        ret = auth_master_user_lookup(conn, input->username, &input->info,
                                      pool, &username, &fields);
-       if (ret < 0)
-               i_fatal("userdb lookup failed");
-       else if (ret == 0) {
+       if (ret < 0) {
+               if (fields[0] == NULL)
+                       i_fatal("userdb lookup failed for %s", input->username);
+               else {
+                       i_fatal("userdb lookup failed for %s: %s",
+                               input->username, fields[0]);
+               }
+       } else if (ret == 0) {
                printf("userdb lookup: user %s doesn't exist\n",
                       input->username);
        } else {
index 7c82a48a30b392ac7708401fd3b16f4ae2f65238..2198ea2d0b331ed196df7e9a3cde337425c575fb 100644 (file)
@@ -99,11 +99,12 @@ doveadm_mail_next_user(doveadm_mail_command_t *cmd,
                return ret;
        }
 
-       if (mail_storage_service_next(storage_service, service_user,
-                                     &mail_user, &error) < 0) {
-               *error_r = t_strdup_printf("User init failed: %s", error);
+       ret = mail_storage_service_next(storage_service, service_user,
+                                       &mail_user);
+       if (ret < 0) {
+               *error_r = "User init failed";
                mail_storage_service_user_free(&service_user);
-               return -2;
+               return ret;
        }
 
        cmd(mail_user, args);
index ecb2e76045e0a74a0eeec0382367c411a9725452..108b2e79aea77539c39f552d68878a18402a1cb2 100644 (file)
@@ -147,8 +147,8 @@ int main(int argc, char *argv[])
                                        &service_user, &error) <= 0)
                i_fatal("User lookup failed: %s", error);
        if (mail_storage_service_next(storage_service, service_user,
-                                     &mail_user, &error) < 0)
-               i_fatal("User init failed: %s", error);
+                                     &mail_user) < 0)
+               i_fatal("User init failed");
 
        if (mirror_cmd != NULL) {
                /* user initialization may exec doveconf, so do our forking
@@ -170,8 +170,8 @@ int main(int argc, char *argv[])
                if (settings_parse_line(set_parser, set_line) < 0)
                        i_unreached();
                if (mail_storage_service_next(storage_service, service_user,
-                                             &mail_user2, &error) < 0)
-                       i_fatal("User init failed: %s", error);
+                                             &mail_user2) < 0)
+                       i_fatal("User init failed");
 
                worker2 = dsync_worker_init_local(mail_user2, alt_char);
 
index 321399b7a87791cc99d9e72854ef60f0ba918ffe..ac47c70c94bc10368a37fcbab2128e5912c139ea 100644 (file)
@@ -15,6 +15,7 @@
 #include "master-login.h"
 #include "mail-user.h"
 #include "mail-storage-service.h"
+#include "imap-resp-code.h"
 #include "imap-commands.h"
 #include "imap-fetch.h"
 
@@ -104,52 +105,72 @@ static void imap_die(void)
        }
 }
 
+struct client_input {
+       const char *tag;
+
+       const unsigned char *input;
+       unsigned int input_size;
+       bool send_untagged_capability;
+};
+
+static void
+client_parse_input(const unsigned char *data, unsigned int len,
+                  struct client_input *input_r)
+{
+       unsigned int taglen;
+
+       i_assert(len > 0);
+
+       memset(input_r, 0, sizeof(*input_r));
+
+       if (data[0] == '1')
+               input_r->send_untagged_capability = TRUE;
+       data++; len--;
+
+       input_r->tag = t_strndup(data, len);
+       taglen = strlen(input_r->tag) + 1;
+
+       if (len > taglen) {
+               input_r->input = data + taglen;
+               input_r->input_size = len - taglen;
+       }
+}
+
 static void client_add_input(struct client *client, const buffer_t *buf)
 {
        struct ostream *output;
-       const char *tag;
-       unsigned int data_pos;
-       bool send_untagged_capability = FALSE;
+       struct client_input input;
 
        if (buf != NULL && buf->used > 0) {
-               tag = t_strndup(buf->data, buf->used);
-               switch (*tag) {
-               case '0':
-                       tag++;
-                       break;
-               case '1':
-                       send_untagged_capability = TRUE;
-                       tag++;
-                       break;
-               }
-               data_pos = strlen(tag) + 1;
-               if (data_pos > buf->used &&
-                   !i_stream_add_data(client->input,
-                                      CONST_PTR_OFFSET(buf->data, data_pos),
-                                      buf->used - data_pos))
+               client_parse_input(buf->data, buf->used, &input);
+               if (input.input_size > 0 &&
+                   !i_stream_add_data(client->input, input.input,
+                                      input.input_size))
                        i_panic("Couldn't add client input to stream");
        } else {
                /* IMAPLOGINTAG environment is compatible with mailfront */
-               tag = getenv("IMAPLOGINTAG");
+               memset(&input, 0, sizeof(input));
+               input.tag = getenv("IMAPLOGINTAG");
        }
 
        output = client->output;
        o_stream_ref(output);
        o_stream_cork(output);
-       if (tag == NULL) {
+       if (input.tag == NULL) {
                client_send_line(client, t_strconcat(
                        "* PREAUTH [CAPABILITY ",
                        str_c(client->capability_string), "] "
                        "Logged in as ", client->user->username, NULL));
-       } else if (send_untagged_capability) {
+       } else if (input.send_untagged_capability) {
                /* client doesn't seem to understand tagged capabilities. send
                   untagged instead and hope that it works. */
                client_send_line(client, t_strconcat("* CAPABILITY ",
                        str_c(client->capability_string), NULL));
-               client_send_line(client, t_strconcat(tag, " OK Logged in", NULL));
+               client_send_line(client,
+                                t_strconcat(input.tag, " OK Logged in", NULL));
        } else {
                client_send_line(client, t_strconcat(
-                       tag, " OK [CAPABILITY ",
+                       input.tag, " OK [CAPABILITY ",
                        str_c(client->capability_string), "] Logged in", NULL));
        }
        (void)client_handle_input(client);
@@ -241,6 +262,18 @@ login_client_connected(const struct master_login_client *client,
        }
 }
 
+static void login_client_failed(const struct master_login_client *client,
+                               const char *errormsg)
+{
+       struct client_input input;
+       const char *msg;
+
+       client_parse_input(client->data, client->auth_req.data_size, &input);
+       msg = t_strdup_printf("%s NO ["IMAP_RESP_CODE_UNAVAILABLE"] %s\r\n",
+                             input.tag, errormsg);
+       (void)write(client->fd, msg, strlen(msg));
+}
+
 static void client_connected(const struct master_service_connection *conn)
 {
        if (master_login == NULL) {
@@ -315,7 +348,8 @@ int main(int argc, char *argv[])
        } else {
                master_login = master_login_init(master_service, "auth-master",
                                                 postlogin_socket_path,
-                                                login_client_connected);
+                                                login_client_connected,
+                                                login_client_failed);
                io_loop_set_running(current_ioloop);
        }
 
index f7e0746cce811ac0ed4373ade091b546aaedf80f..045f3ece3eef1869f62051eea464d06718b39f6d 100644 (file)
@@ -50,6 +50,7 @@ struct auth_master_connection {
 
 struct auth_master_lookup_ctx {
        struct auth_master_connection *conn;
+       const char *user;
        const char *expected_reply;
        int return_value;
 
@@ -135,19 +136,21 @@ static int auth_input_handshake(struct auth_master_connection *conn)
        return 0;
 }
 
-static int parse_reply(struct auth_master_connection *conn,
-                      const char *cmd, const char *const *args,
-                      const char *expected_reply)
+static int parse_reply(const char *cmd, const char *const *args,
+                      const char *expected_reply, const char *user, bool debug)
 {
-       io_loop_stop(conn->ioloop);
-
        if (strcmp(cmd, expected_reply) == 0)
                return 1;
        if (strcmp(cmd, "NOTFOUND") == 0)
                return 0;
        if (strcmp(cmd, "FAIL") == 0) {
-               i_error("Lookup failed: %s",
-                       *args != NULL ? *args : "Internal failure");
+               if (*args == NULL) {
+                       i_error("user %s: Auth %s lookup failed",
+                               user, expected_reply);
+               } else if (debug) {
+                       i_debug("user %s: Auth %s lookup returned temporary failure: %s",
+                               user, expected_reply, *args);
+               }
                return -1;
        }
        i_error("Unknown reply: %s", cmd);
@@ -159,17 +162,31 @@ static bool auth_lookup_reply_callback(const char *cmd, const char *const *args,
 {
        struct auth_master_lookup_ctx *ctx = context;
        unsigned int i, len;
+       bool debug = (ctx->conn->flags & AUTH_MASTER_FLAG_DEBUG) != 0;
+
+       io_loop_stop(ctx->conn->ioloop);
 
        ctx->return_value =
-               parse_reply(ctx->conn, cmd, args, ctx->expected_reply);
-       if (ctx->return_value > 0) {
-               len = str_array_length(args);
+               parse_reply(cmd, args, ctx->expected_reply, ctx->user, debug);
+
+       len = str_array_length(args);
+       if (ctx->return_value >= 0) {
                ctx->fields = p_new(ctx->pool, const char *, len + 1);
                for (i = 0; i < len; i++)
                        ctx->fields[i] = p_strdup(ctx->pool, args[i]);
-               if ((ctx->conn->flags & AUTH_MASTER_FLAG_DEBUG) != 0)
-                       i_debug("auth input: %s", t_strarray_join(args, " "));
+       } else {
+               /* put the reason string into first field */
+               ctx->fields = p_new(ctx->pool, const char *, 2);
+               for (i = 0; i < len; i++) {
+                       if (strncmp(args[i], "reason=", 7) == 0) {
+                               ctx->fields[0] =
+                                       p_strdup(ctx->pool, args[i] + 7);
+                               break;
+                       }
+               }
        }
+       if (debug)
+               i_debug("auth input: %s", t_strarray_join(args, " "));
        return TRUE;
 }
 
@@ -398,6 +415,8 @@ int auth_master_user_lookup(struct auth_master_connection *conn,
 
        if (!is_valid_string(user) || !is_valid_string(info->service)) {
                /* non-allowed characters, the user can't exist */
+               *username_r = NULL;
+               *fields_r = NULL;
                return 0;
        }
 
@@ -406,6 +425,7 @@ int auth_master_user_lookup(struct auth_master_connection *conn,
        ctx.return_value = -1;
        ctx.pool = pool;
        ctx.expected_reply = "USER";
+       ctx.user = user;
 
        conn->reply_callback = auth_lookup_reply_callback;
        conn->reply_context = &ctx;
@@ -422,7 +442,7 @@ int auth_master_user_lookup(struct auth_master_connection *conn,
 
        if (ctx.return_value <= 0 || ctx.fields[0] == NULL) {
                *username_r = NULL;
-               *fields_r = NULL;
+               *fields_r = ctx.fields;
                if (ctx.return_value > 0) {
                        i_error("Userdb lookup didn't return username");
                        ctx.return_value = -1;
@@ -467,6 +487,7 @@ int auth_master_pass_lookup(struct auth_master_connection *conn,
 
        if (!is_valid_string(user) || !is_valid_string(info->service)) {
                /* non-allowed characters, the user can't exist */
+               *fields_r = NULL;
                return 0;
        }
 
@@ -475,6 +496,7 @@ int auth_master_pass_lookup(struct auth_master_connection *conn,
        ctx.return_value = -1;
        ctx.pool = pool;
        ctx.expected_reply = "PASS";
+       ctx.user = user;
 
        conn->reply_callback = auth_lookup_reply_callback;
        conn->reply_context = &ctx;
index b6c8a1835341981a18923c293bd6677df3694dc4..1af1525dffa2f175fa1c3c90f236ec219432e93c 100644 (file)
@@ -27,7 +27,9 @@ struct auth_master_connection *
 auth_master_init(const char *auth_socket_path, enum auth_master_flags flags);
 void auth_master_deinit(struct auth_master_connection **conn);
 
-/* Do a USER lookup. Returns -1 = error, 0 = user not found, 1 = ok */
+/* Do a USER lookup. Returns -1 = error, 0 = user not found, 1 = ok.
+   When returning -1 and fields[0] isn't NULL, it contains an error message
+   that should be shown to user. */
 int auth_master_user_lookup(struct auth_master_connection *conn,
                            const char *user, const struct auth_user_info *info,
                            pool_t pool, const char **username_r,
index 699a26f5c5a9602ea46a8d8b4ef743f331e0e409..97b23819f83c0ece453c6b5404d22a4cb635a7ba 100644 (file)
@@ -58,7 +58,6 @@ static void
 master_auth_connection_deinit(struct master_auth_connection **_conn)
 {
        struct master_auth_connection *conn = *_conn;
-       struct master_auth_reply reply;
 
        *_conn = NULL;
 
@@ -67,11 +66,8 @@ master_auth_connection_deinit(struct master_auth_connection **_conn)
                                  POINTER_CAST(conn->tag));
        }
 
-       if (conn->callback != NULL) {
-               memset(&reply, 0, sizeof(reply));
-               reply.status = MASTER_AUTH_STATUS_INTERNAL_ERROR;
-               conn->callback(&reply, conn->context);
-       }
+       if (conn->callback != NULL)
+               conn->callback(NULL, conn->context);
 
        if (conn->io != NULL)
                io_remove(&conn->io);
index 1f729f8d7d97732e095bdd7311ee569280c1501e..9b6bc1031845dd037aeaf14f648f210dfbf8b357 100644 (file)
@@ -17,6 +17,9 @@ struct master_service;
    to make sure there's space to transfer the command tag  */
 #define MASTER_AUTH_MAX_DATA_SIZE (1024*2)
 
+#define MASTER_AUTH_ERRMSG_INTERNAL_FAILURE \
+       "Internal error occurred. Refer to server log for more information."
+
 enum mail_auth_request_flags {
        /* Connection has TLS compression enabled */
        MAIL_AUTH_REQUEST_FLAG_TLS_COMPRESSION  = 0x01
@@ -60,6 +63,7 @@ struct master_auth_reply {
        pid_t mail_pid;
 };
 
+/* reply=NULL if the auth lookup was cancelled due to some error */
 typedef void master_auth_callback_t(const struct master_auth_reply *reply,
                                    void *context);
 
index b22f7b6be1408d46eb2d9b76002eeacdf9eb3946..5ea151178555d77da700ad1601cb99d7bafa0c7a 100644 (file)
@@ -59,7 +59,8 @@ void master_login_auth_disconnect(struct master_login_auth *auth)
        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);
+               request->callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE,
+                                 request->context);
                i_free(request);
        }
        hash_table_iterate_deinit(&iter);
@@ -135,7 +136,7 @@ master_login_auth_input_user(struct master_login_auth *auth, const char *args)
 
        request = master_login_auth_lookup_request(auth, id);
        if (request != NULL) {
-               request->callback(list + 1, request->context);
+               request->callback(list + 1, NULL, request->context);
                i_free(request);
        }
        return TRUE;
@@ -152,28 +153,39 @@ master_login_auth_input_notfound(struct master_login_auth *auth,
        request = master_login_auth_lookup_request(auth, id);
        if (request != NULL) {
                i_error("Authenticated user not found from userdb");
-               request->callback(NULL, request->context);
+               request->callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE,
+                                 request->context);
                i_free(request);
        }
        return TRUE;
 }
 
 static bool
-master_login_auth_input_fail(struct master_login_auth *auth, const char *args)
+master_login_auth_input_fail(struct master_login_auth *auth,
+                            const char *args_line)
 {
        struct master_login_auth_request *request;
-       const char *error;
-       unsigned int id;
+       const char *const *args, *error = NULL;
+       unsigned int i, id;
 
-       error = strchr(args, '\t');
-       if (error != NULL)
-               error++;
+       args = t_strsplit(args_line, "\t");
+       if (args[0] == NULL) {
+               i_error("Auth server sent broken FAIL line");
+               return FALSE;
+       }
+       for (i = 1; args[i] != NULL; i++) {
+               if (strncmp(args[i], "reason=", 7) == 0)
+                       error = args[i] + 7;
+       }
 
-       id = (unsigned int)strtoul(args, NULL, 10);
+       id = (unsigned int)strtoul(args[0], NULL, 10);
        request = master_login_auth_lookup_request(auth, id);
        if (request != NULL) {
-               i_error("Internal auth failure");
-               request->callback(NULL, request->context);
+               if (error != NULL)
+                       i_error("Internal auth failure");
+               request->callback(NULL, error != NULL ? error :
+                                 MASTER_AUTH_ERRMSG_INTERNAL_FAILURE,
+                                 request->context);
                i_free(request);
        }
        return TRUE;
@@ -266,7 +278,8 @@ void master_login_auth_request(struct master_login_auth *auth,
        str = t_str_new(128);
        if (auth->fd == -1) {
                if (master_login_auth_connect(auth) < 0) {
-                       callback(NULL, context);
+                       callback(NULL, MASTER_AUTH_ERRMSG_INTERNAL_FAILURE,
+                                context);
                        return;
                }
                str_printfa(str, "VERSION\t%u\t%u\n",
index 785e73f589690cf31735598eeac50d5d8281f55a..f541561bc738d377341dd6ff8bdba1d9011cd941 100644 (file)
@@ -5,7 +5,7 @@ struct master_auth_request;
 
 typedef void
 master_login_auth_request_callback_t(const char *const *auth_args,
-                                    void *context);
+                                    const char *errormsg, 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);
index ed693aed5e46ff72d0967f10c50365aa8c3d25e9..cc4392fd6e105dfd281ae92c4a7f4839fbdfc11b 100644 (file)
@@ -39,6 +39,7 @@ struct master_login_postlogin {
 struct master_login {
        struct master_service *service;
        master_login_callback_t *callback;
+       master_login_failure_callback_t *failure_callback;
        struct master_login_connection *conns;
        struct master_login_auth *auth;
        char *postlogin_socket_path;
@@ -52,13 +53,15 @@ static void master_login_conn_unref(struct master_login_connection **_conn);
 struct master_login *
 master_login_init(struct master_service *service, const char *auth_socket_path,
                  const char *postlogin_socket_path,
-                 master_login_callback_t *callback)
+                 master_login_callback_t *callback,
+                 master_login_failure_callback_t *failure_callback)
 {
        struct master_login *login;
 
        login = i_new(struct master_login, 1);
        login->service = service;
        login->callback = callback;
+       login->failure_callback = failure_callback;
        login->auth = master_login_auth_init(auth_socket_path);
        login->postlogin_socket_path = i_strdup(postlogin_socket_path);
 
@@ -316,7 +319,8 @@ static int master_login_postlogin(struct master_login_client *client,
 }
 
 static void
-master_login_auth_callback(const char *const *auth_args, void *context)
+master_login_auth_callback(const char *const *auth_args, const char *errormsg,
+                          void *context)
 {
        struct master_login_client *client = context;
        struct master_auth_reply reply;
@@ -324,14 +328,17 @@ master_login_auth_callback(const char *const *auth_args, void *context)
 
        memset(&reply, 0, sizeof(reply));
        reply.tag = client->auth_req.tag;
-       reply.status = auth_args != NULL ? MASTER_AUTH_STATUS_OK :
+       reply.status = errormsg == 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 || auth_args[0] == NULL) {
-               if (auth_args != NULL)
+       if (errormsg != NULL || auth_args[0] == NULL) {
+               if (auth_args != NULL) {
                        i_error("login client: Username missing from auth reply");
+                       errormsg = MASTER_AUTH_ERRMSG_INTERNAL_FAILURE;
+               }
+               client->conn->login->failure_callback(client, errormsg);
                master_login_client_free(&client);
                return;
        }
index 6603dfc046d84d8e7aacdf94af46b93768ef12c0..9031b9f341745a5962ad779b6d1e09e6a5f2be73 100644 (file)
@@ -14,11 +14,15 @@ struct master_login_client {
 typedef void
 master_login_callback_t(const struct master_login_client *client,
                        const char *username, const char *const *extra_fields);
+typedef void
+master_login_failure_callback_t(const struct master_login_client *client,
+                               const char *errormsg);
 
 struct master_login *
 master_login_init(struct master_service *service, const char *auth_socket_path,
                  const char *postlogin_socket_path,
-                 master_login_callback_t *callback);
+                 master_login_callback_t *callback,
+                 master_login_failure_callback_t *failure_callback);
 void master_login_deinit(struct master_login **login);
 
 void master_login_add(struct master_login *login, int fd);
index c2a708cb6ddff7de79c405e12d0c356c1e66d1ff..8fda54dabb38ed5b9e091f70a2e9372c54dc9e90 100644 (file)
@@ -36,6 +36,9 @@
 #define MAX_TIME_BACKWARDS_SLEEP 5
 #define MAX_NOWARN_FORWARD_SECS 10
 
+#define ERRSTR_INVALID_USER_SETTINGS \
+       "Invalid user settings. Refer to server log for more information."
+
 struct mail_storage_service_ctx {
        pool_t pool;
        struct master_service *service;
@@ -213,9 +216,13 @@ service_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
                }
                *user = new_username;
        } else if (ret == 0)
-               *error_r = "unknown user";
-       else
-               *error_r = "userdb lookup failed";
+               *error_r = "Unknown user";
+       else if (**fields_r != NULL) {
+               *error_r = t_strdup(**fields_r);
+               ret = -2;
+       } else {
+               *error_r = MAIL_ERRSTR_CRITICAL_MSG;
+       }
        return ret;
 }
 
@@ -670,8 +677,11 @@ int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
                                          dyn_parsers);
                if (master_service_settings_cache_read(ctx->set_cache,
                                                       &set_input,
-                                                      parser_r, error_r) < 0)
+                                                      parser_r, error_r) < 0) {
+                       *error_r = t_strdup_printf(
+                               "Error reading configuration: %s", *error_r);
                        return -1;
+               }
        } else {
                dyn_parsers_update_parent(pool, &set_input.roots, dyn_parsers);
                if (master_service_settings_read(ctx->service, &set_input,
@@ -746,7 +756,7 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
        const char *username = input->username;
        const struct setting_parser_info *user_info;
        const struct mail_user_settings *user_set;
-       const char *const *userdb_fields;
+       const char *const *userdb_fields, *error;
        struct auth_user_reply reply;
        const struct setting_parser_context *set_parser;
        pool_t user_pool, temp_pool;
@@ -756,8 +766,10 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
 
        if (mail_storage_service_read_settings(ctx, input, user_pool,
                                               &user_info, &set_parser,
-                                              error_r) < 0) {
+                                              &error) < 0) {
+               i_error("user %s: %s", username, error);
                pool_unref(&user_pool);
+               *error_r = MAIL_ERRSTR_CRITICAL_MSG;
                return -1;
        }
        user_set = settings_parser_get_list(set_parser)[1];
@@ -790,7 +802,7 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
        user->user_info = user_info;
 
        user->set_parser = settings_parser_dup(set_parser, user_pool);
-       if (!settings_parser_check(user->set_parser, user_pool, error_r))
+       if (!settings_parser_check(user->set_parser, user_pool, &error))
                i_unreached();
 
        user->user_set = settings_parser_get_list(user->set_parser)[1];
@@ -803,8 +815,12 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
 
        if (userdb_fields != NULL) {
                auth_user_fields_parse(userdb_fields, temp_pool, &reply);
-               if (user_reply_handle(user, &reply, error_r) < 0)
+               if (user_reply_handle(user, &reply, &error) < 0) {
+                       i_error("user %s: Invalid settings in userdb: %s",
+                               username, error);
+                       *error_r = ERRSTR_INVALID_USER_SETTINGS;
                        ret = -2;
+               }
        }
        pool_unref(&temp_pool);
 
@@ -820,8 +836,7 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
 
 int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                              struct mail_storage_service_user *user,
-                             struct mail_user **mail_user_r,
-                             const char **error_r)
+                             struct mail_user **mail_user_r)
 {
        const struct mail_user_settings *user_set = user->user_set;
        const char *home, *chroot, *error;
@@ -839,10 +854,10 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                                    user_set->mail_chroot);
 
        if (*home != '/' && *home != '\0') {
-               *error_r = t_strdup_printf("user %s: "
+               i_error("user %s: "
                        "Relative home directory paths not supported: %s",
                        user->input.username, home);
-               return -1;
+               return -2;
        }
 
        if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
@@ -852,8 +867,8 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                if (service_drop_privileges(user_set, user->system_groups_user,
                                            home, chroot, disallow_root,
                                            temp_priv_drop, FALSE, &error) < 0) {
-                       *error_r = t_strdup_printf(
-                               "Couldn't drop privileges: %s", error);
+                       i_error("user %s: Couldn't drop privileges: %s",
+                               user->input.username, error);
                        return -1;
                }
                if (!temp_priv_drop ||
@@ -884,8 +899,11 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                        t_strconcat(chroot, "/", home, NULL));
        }
        if (mail_storage_service_init_post(ctx, user, home,
-                                          mail_user_r, error_r) < 0)
-               return -1;
+                                          mail_user_r, &error) < 0) {
+               i_error("user %s: Initialization failed: %s",
+                       user->input.username, error);
+               return -2;
+       }
        return 0;
 }
 
@@ -913,18 +931,18 @@ int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
                                     const char **error_r)
 {
        struct mail_storage_service_user *user;
-       const char *error;
        int ret;
 
-       ret = mail_storage_service_lookup(ctx, input, &user, &error);
-       if (ret <= 0) {
-               *error_r = t_strdup_printf("User lookup failed: %s", error);
+       ret = mail_storage_service_lookup(ctx, input, &user, error_r);
+       if (ret <= 0)
                return ret;
-       }
-       if (mail_storage_service_next(ctx, user, mail_user_r, &error) < 0) {
+
+       ret = mail_storage_service_next(ctx, user, mail_user_r);
+       if (ret < 0) {
                mail_storage_service_user_free(&user);
-               *error_r = t_strdup_printf("User init failed: %s", error);
-               return -2;
+               *error_r = ret == -2 ? ERRSTR_INVALID_USER_SETTINGS :
+                       MAIL_ERRSTR_CRITICAL_MSG;
+               return ret;
        }
        *user_r = user;
        return 1;
index 8b040506e16e6456c71637d9b3e3a4e934a35692..632efd993ea88410c053f4022bf9790b974f4d2a 100644 (file)
@@ -61,20 +61,19 @@ int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
 void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
                                        const struct mail_storage_service_input *input);
 /* Returns 1 if ok, 0 if user wasn't found, -1 if fatal error,
-   -2 if user had invalid settings. */
+   -2 if error is user-specific (e.g. invalid settings).
+   Error can be safely shown to untrusted users. */
 int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
                                const struct mail_storage_service_input *input,
                                struct mail_storage_service_user **user_r,
                                const char **error_r);
-/* Returns 0 if ok, -1 if user had invalid settings. */
+/* Returns 0 if ok, -1 if fatal error, -2 if error is user-specific. */
 int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
                              struct mail_storage_service_user *user,
-                             struct mail_user **mail_user_r,
-                             const char **error_r);
+                             struct mail_user **mail_user_r);
 void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
                                          struct mail_storage_service_user *user);
-/* Combine lookup() and next() into one call. If either one fails with
-   "invalid settings", this function returns -2. */
+/* Combine lookup() and next() into one call. */
 int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
                                     const struct mail_storage_service_input *input,
                                     struct mail_storage_service_user **user_r,
index e775e2466dc63456196e08658f241c4c8bc91984..faf73076c87579d810fdbef2d183075edb8179dd 100644 (file)
@@ -28,7 +28,9 @@
 #include <stdlib.h>
 
 #define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
-#define ERRSTR_TEMP_USERDB_FAIL "451 4.3.0 <%s> Temporary user lookup failure"
+#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
+#define ERRSTR_TEMP_USERDB_FAIL \
+       ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
 
 #define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*30)
 
@@ -199,7 +201,7 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
        struct lmtp_proxy_settings set;
        struct auth_user_info info;
        struct mail_storage_service_input input;
-       const char *args, *const *fields, *orig_username = username;
+       const char *args, *const *fields, *errstr, *orig_username = username;
        pool_t pool;
        int ret;
 
@@ -219,10 +221,11 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
        ret = auth_master_pass_lookup(auth_conn, username, &info,
                                      pool, &fields);
        if (ret <= 0) {
+               errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
+                       t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
                pool_unref(&pool);
                if (ret < 0) {
-                       client_send_line(client, ERRSTR_TEMP_USERDB_FAIL,
-                                        address);
+                       client_send_line(client, "%s", errstr);
                        return TRUE;
                } else {
                        /* user not found from passdb. try userdb also. */
@@ -332,7 +335,7 @@ int cmd_rcpt(struct client *client, const char *args)
 {
        struct mail_recipient rcpt;
        struct mail_storage_service_input input;
-       const char *address, *username, *detail;
+       const char *address, *username, *detail, *prefix;
        const char *error = NULL, *arg, *const *argv;
        unsigned int len;
        int ret = 0;
@@ -384,8 +387,9 @@ int cmd_rcpt(struct client *client, const char *args)
                                          &rcpt.service_user, &error);
 
        if (ret < 0) {
-               i_error("User lookup failed: %s", error);
-               client_send_line(client, ERRSTR_TEMP_USERDB_FAIL, username);
+               prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX,
+                                        username);
+               client_send_line(client, "%s%s", prefix, error);
                return 0;
        }
        if (ret == 0) {
@@ -449,9 +453,7 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
        i_set_failure_prefix(t_strdup_printf("lmtp(%s, %s): ",
                                             my_pid, username));
        if (mail_storage_service_next(storage_service, rcpt->service_user,
-                                     &client->state.dest_user,
-                                     &error) < 0) {
-               i_error("%s", error);
+                                     &client->state.dest_user) < 0) {
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
                                 rcpt->address);
                return -1;
index d08dbebda6bfbbf1f337eec795fab062203466d9..61ce699e9fb3aba6be461d3a5fa66b327428727a 100644 (file)
@@ -378,6 +378,7 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
 
        i_assert(!client->destroyed ||
                 sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
+                sasl_reply == SASL_SERVER_REPLY_MASTER_ABORTED ||
                 sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
 
        switch (sasl_reply) {
@@ -428,6 +429,10 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
                        client_destroy_success(client, data);
                }
                break;
+       case SASL_SERVER_REPLY_MASTER_ABORTED:
+               /* mail process already sent the error message to client */
+               client_destroy_success(client, data);
+               break;
        case SASL_SERVER_REPLY_CONTINUE:
                client->v.auth_send_challenge(client, data);
 
index d5f632bb4924a5012fc6054f742fa5d02eb77d4b..7006fca446c6a8bf2c7c5917a944c962d3c7c0ba 100644 (file)
@@ -94,12 +94,15 @@ master_auth_callback(const struct master_auth_reply *reply, void *context)
 
        client->master_tag = 0;
        client->authenticating = FALSE;
-       switch (reply->status) {
-       case MASTER_AUTH_STATUS_OK:
-               sasl_reply = SASL_SERVER_REPLY_SUCCESS;
-               break;
-       case MASTER_AUTH_STATUS_INTERNAL_ERROR:
-               break;
+       if (reply != NULL) {
+               switch (reply->status) {
+               case MASTER_AUTH_STATUS_OK:
+                       sasl_reply = SASL_SERVER_REPLY_SUCCESS;
+                       break;
+               case MASTER_AUTH_STATUS_INTERNAL_ERROR:
+                       sasl_reply = SASL_SERVER_REPLY_MASTER_ABORTED;
+                       break;
+               }
        }
        client->mail_pid = reply->mail_pid;
        call_client_callback(client, sasl_reply, data, NULL);
index 7c8940ba18912f311c07460a39cc2873838251b3..b2fb2509be0c4bcbd29dc34585de9559b6335bc9 100644 (file)
@@ -8,6 +8,7 @@ enum sasl_server_reply {
        SASL_SERVER_REPLY_AUTH_FAILED,
        SASL_SERVER_REPLY_AUTH_ABORTED,
        SASL_SERVER_REPLY_MASTER_FAILED,
+       SASL_SERVER_REPLY_MASTER_ABORTED,
        SASL_SERVER_REPLY_CONTINUE
 };
 
index 9884bb80642b9d6115adbb0a54981db6e3759b07..7e0df5080058ef2884655ed84817d811a6ba6231 100644 (file)
@@ -161,6 +161,15 @@ login_client_connected(const struct master_login_client *client,
        }
 }
 
+static void login_client_failed(const struct master_login_client *client,
+                               const char *errormsg)
+{
+       const char *msg;
+
+       msg = t_strdup_printf("-ERR [IN-USE] %s\r\n", errormsg);
+       (void)write(client->fd, msg, strlen(msg));
+}
+
 static void client_connected(const struct master_service_connection *conn)
 {
        if (master_login == NULL) {
@@ -231,7 +240,8 @@ int main(int argc, char *argv[])
        } else {
                master_login = master_login_init(master_service, "auth-master",
                                                 postlogin_socket_path,
-                                                login_client_connected);
+                                                login_client_connected,
+                                                login_client_failed);
                io_loop_set_running(current_ioloop);
        }