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
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:
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 {
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);
&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
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);
#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"
}
}
+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);
}
}
+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) {
} 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);
}
struct auth_master_lookup_ctx {
struct auth_master_connection *conn;
+ const char *user;
const char *expected_reply;
int return_value;
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);
{
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;
}
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;
}
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;
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;
if (!is_valid_string(user) || !is_valid_string(info->service)) {
/* non-allowed characters, the user can't exist */
+ *fields_r = NULL;
return 0;
}
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;
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,
master_auth_connection_deinit(struct master_auth_connection **_conn)
{
struct master_auth_connection *conn = *_conn;
- struct master_auth_reply reply;
*_conn = NULL;
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);
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
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);
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);
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;
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;
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",
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);
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;
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);
}
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;
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;
}
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);
#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;
}
*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;
}
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,
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;
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];
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];
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);
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;
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)
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 ||
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;
}
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;
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,
#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)
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;
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. */
{
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;
&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) {
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;
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) {
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);
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);
SASL_SERVER_REPLY_AUTH_FAILED,
SASL_SERVER_REPLY_AUTH_ABORTED,
SASL_SERVER_REPLY_MASTER_FAILED,
+ SASL_SERVER_REPLY_MASTER_ABORTED,
SASL_SERVER_REPLY_CONTINUE
};
}
}
+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) {
} 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);
}