static void
auth_request_handler_reply_failure_finish(struct auth_request *request)
{
+ const char *code = NULL;
string_t *str = t_str_new(128);
auth_fields_remove(request->extra_fields, "nologin");
request->original_username);
}
- if (request->internal_failure)
- str_append(str, "\ttemp");
- else if (request->master_user != NULL) {
+ if (request->internal_failure) {
+ code = AUTH_CLIENT_FAIL_CODE_TEMPFAIL;
+ } else if (request->master_user != NULL) {
/* authentication succeeded, but we can't log in
as the wanted user */
- str_append(str, "\tauthz");
+ code = AUTH_CLIENT_FAIL_CODE_AUTHZFAILED;
+ } else {
+ switch (request->passdb_result) {
+ case PASSDB_RESULT_NEXT:
+ case PASSDB_RESULT_INTERNAL_FAILURE:
+ case PASSDB_RESULT_SCHEME_NOT_AVAILABLE:
+ case PASSDB_RESULT_USER_UNKNOWN:
+ case PASSDB_RESULT_PASSWORD_MISMATCH:
+ case PASSDB_RESULT_OK:
+ break;
+ case PASSDB_RESULT_USER_DISABLED:
+ code = AUTH_CLIENT_FAIL_CODE_USER_DISABLED;
+ break;
+ case PASSDB_RESULT_PASS_EXPIRED:
+ code = AUTH_CLIENT_FAIL_CODE_PASS_EXPIRED;
+ break;
+ }
}
+
if (auth_fields_exists(request->extra_fields, "nodelay")) {
/* this is normally a hidden field, need to add it explicitly */
str_append(str, "\tnodelay");
}
- auth_str_append_extra_fields(request, str);
- switch (request->passdb_result) {
- case PASSDB_RESULT_NEXT:
- case PASSDB_RESULT_INTERNAL_FAILURE:
- case PASSDB_RESULT_SCHEME_NOT_AVAILABLE:
- case PASSDB_RESULT_USER_UNKNOWN:
- case PASSDB_RESULT_PASSWORD_MISMATCH:
- case PASSDB_RESULT_OK:
- break;
- case PASSDB_RESULT_USER_DISABLED:
- str_append(str, "\tuser_disabled");
- break;
- case PASSDB_RESULT_PASS_EXPIRED:
- str_append(str, "\tpass_expired");
- break;
+ if (code != NULL) {
+ str_append(str, "\tcode=");
+ str_append(str, code);
}
+ auth_str_append_extra_fields(request, str);
auth_request_handle_failure(request, str_c(str));
}
reply, reply_size);
}
-static void auth_request_handler_auth_fail(struct auth_request_handler *handler,
+static void
+auth_request_handler_auth_fail_code(struct auth_request_handler *handler,
struct auth_request *request,
- const char *reason)
+ const char *fail_code, const char *reason)
{
string_t *str = t_str_new(128);
auth_request_log_info(request, AUTH_SUBSYS_MECH, "%s", reason);
- str_printfa(str, "FAIL\t%u\treason=", request->id);
+ str_printfa(str, "FAIL\t%u", request->id);
+ if (*fail_code != '\0') {
+ str_append(str, "\tcode=");
+ str_append(str, fail_code);
+ }
+ str_append(str, "\treason=");
str_append_tabescaped(str, reason);
handler->callback(str_c(str), handler->conn);
auth_request_handler_remove(handler, request);
}
+static void auth_request_handler_auth_fail
+(struct auth_request_handler *handler, struct auth_request *request,
+ const char *reason)
+{
+ auth_request_handler_auth_fail_code(handler, request, "", reason);
+}
+
static void auth_request_timeout(struct auth_request *request)
{
unsigned int secs = (unsigned int)(time(NULL) - request->last_access);
/* Major version changes are not backwards compatible,
minor version numbers can be ignored. */
#define AUTH_CLIENT_PROTOCOL_MAJOR_VERSION 1
-#define AUTH_CLIENT_PROTOCOL_MINOR_VERSION 1
+#define AUTH_CLIENT_PROTOCOL_MINOR_VERSION 2
/* GSSAPI can use quite large packets */
#define AUTH_CLIENT_MAX_LINE_LENGTH 16384
MECH_SEC_MUTUAL_AUTH = 0x0040
};
+/* auth failure codes */
+#define AUTH_CLIENT_FAIL_CODE_AUTHZFAILED "authz_fail"
+#define AUTH_CLIENT_FAIL_CODE_TEMPFAIL "temp_fail"
+#define AUTH_CLIENT_FAIL_CODE_USER_DISABLED "user_disabled"
+#define AUTH_CLIENT_FAIL_CODE_PASS_EXPIRED "pass_expired"
+
#endif
#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
#define AUTH_WAITING_WARNING_TIMEOUT_MSECS (10*1000)
+struct client_auth_fail_code_id {
+ const char *id;
+ enum client_auth_fail_code code;
+};
+
+static const struct client_auth_fail_code_id client_auth_fail_codes[] = {
+ { AUTH_CLIENT_FAIL_CODE_AUTHZFAILED,
+ CLIENT_AUTH_FAIL_CODE_AUTHZFAILED },
+ { AUTH_CLIENT_FAIL_CODE_TEMPFAIL,
+ CLIENT_AUTH_FAIL_CODE_TEMPFAIL },
+ { AUTH_CLIENT_FAIL_CODE_USER_DISABLED,
+ CLIENT_AUTH_FAIL_CODE_USER_DISABLED },
+ { AUTH_CLIENT_FAIL_CODE_PASS_EXPIRED,
+ CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED },
+ { NULL, CLIENT_AUTH_FAIL_CODE_NONE }
+};
+
+static enum client_auth_fail_code
+client_auth_fail_code_lookup(const char *fail_code)
+{
+ const struct client_auth_fail_code_id *fail = client_auth_fail_codes;
+
+ while (fail->id != NULL) {
+ if (strcmp(fail->id, fail_code) == 0)
+ return fail->code;
+ fail++;
+ }
+
+ return CLIENT_AUTH_FAIL_CODE_NONE;
+}
+
static void client_auth_failed(struct client *client)
{
i_free_and_null(client->master_data_prefix);
key = t_strdup_until(*args, p);
value = p + 1;
}
- if (strcmp(key, "nologin") == 0)
+ if (strcmp(key, "nologin") == 0) {
reply_r->nologin = TRUE;
- else if (strcmp(key, "proxy") == 0)
+ } else if (strcmp(key, "proxy") == 0)
reply_r->proxy = TRUE;
- else if (strcmp(key, "temp") == 0)
- reply_r->temp = TRUE;
- else if (strcmp(key, "authz") == 0)
- reply_r->authz_failure = TRUE;
- else if (strcmp(key, "user_disabled") == 0)
- client->auth_user_disabled = TRUE;
- else if (strcmp(key, "pass_expired") == 0)
- client->auth_pass_expired = TRUE;
else if (strcmp(key, "reason") == 0)
reply_r->reason = value;
else if (strcmp(key, "host") == 0)
PROXY_SSL_FLAG_STARTTLS;
if (strcmp(value, "any-cert") == 0)
reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
+ } else if (strcmp(key, "code") == 0) {
+ if (reply_r->fail_code != CLIENT_AUTH_FAIL_CODE_NONE) {
+ /* code already assigned */
+ } else {
+ reply_r->fail_code = client_auth_fail_code_lookup(value);
+ }
} else if (strcmp(key, "user") == 0 ||
strcmp(key, "postlogin_socket") == 0) {
/* already handled in sasl-server.c */
return TRUE;
}
} else if (reply->nologin) {
- /* Authentication went ok, but for some reason user isn't
- allowed to log in. Shouldn't probably happen. */
- if (reply->reason != NULL) {
- client_auth_result(client,
- CLIENT_AUTH_RESULT_AUTHFAILED_REASON,
- reply, reply->reason);
- } else if (reply->temp) {
- const char *timestamp, *msg;
-
+ enum client_auth_result result = CLIENT_AUTH_RESULT_AUTHFAILED;
+ const char *timestamp, *reason = reply->reason;
+
+ /* Either failed or user login is disabled */
+ switch (reply->fail_code) {
+ case CLIENT_AUTH_FAIL_CODE_AUTHZFAILED:
+ result = CLIENT_AUTH_RESULT_AUTHZFAILED;
+ if (reason == NULL)
+ reason = "Authorization failed";
+ break;
+ case CLIENT_AUTH_FAIL_CODE_TEMPFAIL:
+ result = CLIENT_AUTH_RESULT_TEMPFAIL;
timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time);
- msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]",
+ reason = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]",
my_hostname, timestamp);
- client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
- reply, msg);
- } else if (reply->authz_failure) {
- client_auth_result(client,
- CLIENT_AUTH_RESULT_AUTHZFAILED, reply,
- "Authorization failed");
- } else {
- client_auth_result(client,
- CLIENT_AUTH_RESULT_AUTHFAILED, reply,
- AUTH_FAILED_MSG);
+ break;
+ case CLIENT_AUTH_FAIL_CODE_USER_DISABLED:
+ case CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED:
+ default:
+ if (reason != NULL)
+ result = CLIENT_AUTH_RESULT_AUTHFAILED_REASON;
+ else
+ result = CLIENT_AUTH_RESULT_AUTHFAILED;
}
+
+ if (reason == NULL)
+ reason = AUTH_FAILED_MSG;
+ client_auth_result(client, result, reply, reason);
} else {
/* normal login/failure */
return FALSE;
sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
+ client->last_auth_fail = CLIENT_AUTH_FAIL_CODE_NONE;
memset(&reply, 0, sizeof(reply));
switch (sasl_reply) {
case SASL_SERVER_REPLY_SUCCESS:
if (args != NULL) {
client_auth_parse_args(client, TRUE, args, &reply);
reply.all_fields = args;
+ client->last_auth_fail = reply.fail_code;
if (client_auth_handle_reply(client, &reply, TRUE))
break;
}
timeout_remove(&client->to_auth_waiting);
if (args != NULL) {
client_auth_parse_args(client, FALSE, args, &reply);
+ client->last_auth_fail = reply.fail_code;
reply.nologin = TRUE;
reply.all_fields = args;
if (client_auth_handle_reply(client, &reply, FALSE))
return t_strdup_printf("(internal failure, %u successful auths)",
client->auth_successes);
}
- if (client->auth_user_disabled)
+
+ switch (client->last_auth_fail) {
+ case CLIENT_AUTH_FAIL_CODE_AUTHZFAILED:
+ return t_strdup_printf(
+ "(authorization failed, %u attempts in %u secs)",
+ client->auth_attempts, auth_secs);
+ case CLIENT_AUTH_FAIL_CODE_TEMPFAIL:
+ return "(auth service reported temporary failure)";
+ case CLIENT_AUTH_FAIL_CODE_USER_DISABLED:
return "(user disabled)";
- if (client->auth_pass_expired)
+ case CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED:
return "(password expired)";
+ default:
+ break;
+ }
+
return t_strdup_printf("(auth failed, %u attempts in %u secs)",
client->auth_attempts, auth_secs);
}
CLIENT_DISCONNECT_INTERNAL_ERROR
};
+enum client_auth_fail_code {
+ CLIENT_AUTH_FAIL_CODE_NONE = 0,
+ CLIENT_AUTH_FAIL_CODE_AUTHZFAILED,
+ CLIENT_AUTH_FAIL_CODE_TEMPFAIL,
+ CLIENT_AUTH_FAIL_CODE_USER_DISABLED,
+ CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED,
+};
+
enum client_auth_result {
CLIENT_AUTH_RESULT_SUCCESS,
CLIENT_AUTH_RESULT_REFERRAL_SUCCESS,
struct client_auth_reply {
const char *master_user, *reason;
+ enum client_auth_fail_code fail_code;
+
/* for proxying */
const char *host, *hostip, *source_ip;
const char *destuser, *password, *proxy_mech;
bool proxy:1;
bool proxy_nopipelining:1;
bool proxy_not_trusted:1;
- bool temp:1;
bool nologin:1;
- bool authz_failure:1;
};
struct client_vfuncs {
unsigned int bad_counter;
unsigned int auth_attempts, auth_successes;
+ enum client_auth_fail_code last_auth_fail;
pid_t mail_pid;
/* Module-specific contexts. */
bool proxy_nopipelining:1;
bool proxy_not_trusted:1;
bool auth_waiting:1;
- bool auth_user_disabled:1;
- bool auth_pass_expired:1;
bool notified_auth_ready:1;
bool notified_disconnect:1;
/* ... */