From: Timo Sirainen Date: Wed, 31 Mar 2010 16:21:09 +0000 (+0300) Subject: Added support for userdb lookup to fail with a reason (many API changes). X-Git-Tag: 2.0.beta5~256 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3b22894b8805b186c73d8b754001e8d7e944be85;p=thirdparty%2Fdovecot%2Fcore.git Added support for userdb lookup to fail with a reason (many API changes). --HG-- branch : HEAD --- diff --git a/doc/auth-protocol.txt b/doc/auth-protocol.txt index 77abb9202d..00201392fc 100644 --- a/doc/auth-protocol.txt +++ b/doc/auth-protocol.txt @@ -164,7 +164,7 @@ Master Requests M: "USER" TAB TAB TAB service= [TAB ] S: "NOTFOUND" TAB - S: "FAIL" TAB TAB + S: "FAIL" TAB [TAB ] S: "USER" TAB TAB [TAB ] 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: diff --git a/src/doveadm/doveadm-auth.c b/src/doveadm/doveadm-auth.c index 89e3072306..99aa68f1a6 100644 --- a/src/doveadm/doveadm-auth.c +++ b/src/doveadm/doveadm-auth.c @@ -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 { diff --git a/src/doveadm/doveadm-mail.c b/src/doveadm/doveadm-mail.c index 7c82a48a30..2198ea2d0b 100644 --- a/src/doveadm/doveadm-mail.c +++ b/src/doveadm/doveadm-mail.c @@ -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); diff --git a/src/dsync/dsync.c b/src/dsync/dsync.c index ecb2e76045..108b2e79ae 100644 --- a/src/dsync/dsync.c +++ b/src/dsync/dsync.c @@ -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); diff --git a/src/imap/main.c b/src/imap/main.c index 321399b7a8..ac47c70c94 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -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); } diff --git a/src/lib-auth/auth-master.c b/src/lib-auth/auth-master.c index f7e0746cce..045f3ece3e 100644 --- a/src/lib-auth/auth-master.c +++ b/src/lib-auth/auth-master.c @@ -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; diff --git a/src/lib-auth/auth-master.h b/src/lib-auth/auth-master.h index b6c8a18353..1af1525dff 100644 --- a/src/lib-auth/auth-master.h +++ b/src/lib-auth/auth-master.h @@ -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, diff --git a/src/lib-master/master-auth.c b/src/lib-master/master-auth.c index 699a26f5c5..97b23819f8 100644 --- a/src/lib-master/master-auth.c +++ b/src/lib-master/master-auth.c @@ -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); diff --git a/src/lib-master/master-auth.h b/src/lib-master/master-auth.h index 1f729f8d7d..9b6bc10318 100644 --- a/src/lib-master/master-auth.h +++ b/src/lib-master/master-auth.h @@ -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); diff --git a/src/lib-master/master-login-auth.c b/src/lib-master/master-login-auth.c index b22f7b6be1..5ea1511785 100644 --- a/src/lib-master/master-login-auth.c +++ b/src/lib-master/master-login-auth.c @@ -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", diff --git a/src/lib-master/master-login-auth.h b/src/lib-master/master-login-auth.h index 785e73f589..f541561bc7 100644 --- a/src/lib-master/master-login-auth.h +++ b/src/lib-master/master-login-auth.h @@ -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); diff --git a/src/lib-master/master-login.c b/src/lib-master/master-login.c index ed693aed5e..cc4392fd6e 100644 --- a/src/lib-master/master-login.c +++ b/src/lib-master/master-login.c @@ -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; } diff --git a/src/lib-master/master-login.h b/src/lib-master/master-login.h index 6603dfc046..9031b9f341 100644 --- a/src/lib-master/master-login.h +++ b/src/lib-master/master-login.h @@ -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); diff --git a/src/lib-storage/mail-storage-service.c b/src/lib-storage/mail-storage-service.c index c2a708cb6d..8fda54dabb 100644 --- a/src/lib-storage/mail-storage-service.c +++ b/src/lib-storage/mail-storage-service.c @@ -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; diff --git a/src/lib-storage/mail-storage-service.h b/src/lib-storage/mail-storage-service.h index 8b040506e1..632efd993e 100644 --- a/src/lib-storage/mail-storage-service.h +++ b/src/lib-storage/mail-storage-service.h @@ -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, diff --git a/src/lmtp/commands.c b/src/lmtp/commands.c index e775e2466d..faf73076c8 100644 --- a/src/lmtp/commands.c +++ b/src/lmtp/commands.c @@ -28,7 +28,9 @@ #include #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; diff --git a/src/login-common/client-common-auth.c b/src/login-common/client-common-auth.c index d08dbebda6..61ce699e9f 100644 --- a/src/login-common/client-common-auth.c +++ b/src/login-common/client-common-auth.c @@ -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); diff --git a/src/login-common/sasl-server.c b/src/login-common/sasl-server.c index d5f632bb49..7006fca446 100644 --- a/src/login-common/sasl-server.c +++ b/src/login-common/sasl-server.c @@ -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); diff --git a/src/login-common/sasl-server.h b/src/login-common/sasl-server.h index 7c8940ba18..b2fb2509be 100644 --- a/src/login-common/sasl-server.h +++ b/src/login-common/sasl-server.h @@ -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 }; diff --git a/src/pop3/main.c b/src/pop3/main.c index 9884bb8064..7e0df50800 100644 --- a/src/pop3/main.c +++ b/src/pop3/main.c @@ -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); }