client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
break;
case CLIENT_AUTH_RESULT_AUTHFAILED_REASON:
+ case CLIENT_AUTH_RESULT_MECH_INVALID:
if (text[0] == '[')
client_send_reply(client, IMAP_CMD_REPLY_NO, text);
else {
IMAP_RESP_CODE_UNAVAILABLE, text);
break;
case CLIENT_AUTH_RESULT_SSL_REQUIRED:
+ case CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED:
client_send_reply_code(client, IMAP_CMD_REPLY_NO,
IMAP_RESP_CODE_PRIVACYREQUIRED, text);
break;
#define AUTH_CLIENT_FAIL_CODE_USER_DISABLED "user_disabled"
#define AUTH_CLIENT_FAIL_CODE_PASS_EXPIRED "pass_expired"
+/* not actually returned from auth service */
+#define AUTH_CLIENT_FAIL_CODE_MECH_INVALID "auth_mech_invalid"
+#define AUTH_CLIENT_FAIL_CODE_MECH_SSL_REQUIRED "auth_mech_ssl_required"
+
#endif
CLIENT_AUTH_FAIL_CODE_USER_DISABLED },
{ AUTH_CLIENT_FAIL_CODE_PASS_EXPIRED,
CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED },
+ { AUTH_CLIENT_FAIL_CODE_MECH_INVALID,
+ CLIENT_AUTH_FAIL_CODE_MECH_INVALID },
+ { AUTH_CLIENT_FAIL_CODE_MECH_SSL_REQUIRED,
+ CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED },
{ NULL, CLIENT_AUTH_FAIL_CODE_NONE }
};
case CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED:
result = CLIENT_AUTH_RESULT_PASS_EXPIRED;
break;
+ case CLIENT_AUTH_FAIL_CODE_MECH_INVALID:
+ result = CLIENT_AUTH_RESULT_MECH_INVALID;
+ break;
+ case CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED:
+ result = CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED;
+ break;
case CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED:
result = CLIENT_AUTH_RESULT_LOGIN_DISABLED;
if (reason == NULL)
void client_auth_fail(struct client *client, const char *text)
{
- sasl_server_auth_failed(client, text);
+ sasl_server_auth_failed(client, text, NULL);
}
int client_auth_read_line(struct client *client)
bool client_check_plaintext_auth(struct client *client, bool pass_sent)
{
+ bool ssl_required = (strcmp(client->ssl_set->ssl, "required") == 0);
+
if (client->secured || (!client->set->disable_plaintext_auth &&
- strcmp(client->ssl_set->ssl, "required") != 0))
+ !ssl_required))
return TRUE;
if (client->set->auth_verbose) {
"without SSL/TLS, but your client did it anyway. "
"If anyone was listening, the password was exposed.");
}
- client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL,
+
+ if (ssl_required) {
+ client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL,
+ AUTH_PLAINTEXT_DISABLED_MSG);
+ } else {
+ client_auth_result(client, CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED, NULL,
AUTH_PLAINTEXT_DISABLED_MSG);
- client->auth_tried_disabled_plaintext = TRUE;
+ }
client->auth_attempts++;
return FALSE;
}
}
/* some auth attempts without SSL/TLS */
- if (client->auth_tried_disabled_plaintext)
- return "(tried to use disallowed plaintext auth)";
if (client->set->auth_ssl_require_client_cert &&
client->ssl_proxy == NULL)
return "(cert required, client didn't start TLS)";
- if (client->auth_tried_unsupported_mech)
- return "(tried to use unsupported auth mechanism)";
+
if (client->auth_waiting && client->auth_attempts == 1) {
return t_strdup_printf("(client didn't finish SASL auth, "
"waited %u secs)", auth_secs);
return "(password expired)";
case CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED:
return "(login disabled)";
+ case CLIENT_AUTH_FAIL_CODE_MECH_INVALID:
+ return "(tried to use unsupported auth mechanism)";
+ case CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED:
+ return "(tried to use disallowed plaintext auth)";
default:
break;
}
CLIENT_AUTH_FAIL_CODE_USER_DISABLED,
CLIENT_AUTH_FAIL_CODE_PASS_EXPIRED,
CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED,
+ CLIENT_AUTH_FAIL_CODE_MECH_INVALID,
+ CLIENT_AUTH_FAIL_CODE_MECH_SSL_REQUIRED,
};
enum client_auth_result {
CLIENT_AUTH_RESULT_PASS_EXPIRED,
CLIENT_AUTH_RESULT_SSL_REQUIRED,
CLIENT_AUTH_RESULT_LOGIN_DISABLED,
+ CLIENT_AUTH_RESULT_MECH_INVALID,
+ CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED
};
struct client_auth_reply {
bool trusted:1;
bool ssl_servername_settings_read:1;
bool authenticating:1;
- bool auth_tried_disabled_plaintext:1;
- bool auth_tried_unsupported_mech:1;
bool auth_try_aborted:1;
bool auth_initializing:1;
bool auth_process_comm_fail:1;
mech = auth_client_find_mech(auth_client, mech_name);
if (mech == NULL) {
- client->auth_tried_unsupported_mech = TRUE;
sasl_server_auth_failed(client,
- "Unsupported authentication mechanism.");
+ "Unsupported authentication mechanism.",
+ AUTH_CLIENT_FAIL_CODE_MECH_INVALID);
return;
}
if (!client->secured && client->set->disable_plaintext_auth &&
(mech->flags & MECH_SEC_PLAINTEXT) != 0) {
- client->auth_tried_disabled_plaintext = TRUE;
sasl_server_auth_failed(client,
- "Plaintext authentication disabled.");
+ "Plaintext authentication disabled.",
+ AUTH_CLIENT_FAIL_CODE_MECH_SSL_REQUIRED);
return;
}
authenticate_callback, client);
}
-static void ATTR_NULL(2)
+static void ATTR_NULL(2, 3)
sasl_server_auth_cancel(struct client *client, const char *reason,
- enum sasl_server_reply reply)
+ const char *code, enum sasl_server_reply reply)
{
i_assert(client->authenticating);
if (client->auth_request != NULL)
auth_client_request_abort(&client->auth_request);
+ if (code != NULL) {
+ const char *args[2];
+
+ args[0] = t_strconcat("code=", code, NULL);
+ args[1] = NULL;
+ call_client_callback(client, reply, reason, args);
+ return;
+ }
+
call_client_callback(client, reply, reason, NULL);
}
-void sasl_server_auth_failed(struct client *client, const char *reason)
+void sasl_server_auth_failed(struct client *client, const char *reason,
+ const char *code)
{
- sasl_server_auth_cancel(client, reason, SASL_SERVER_REPLY_AUTH_FAILED);
+ sasl_server_auth_cancel(client, reason, code, SASL_SERVER_REPLY_AUTH_FAILED);
}
void sasl_server_auth_abort(struct client *client)
{
client->auth_try_aborted = TRUE;
- sasl_server_auth_cancel(client, NULL, SASL_SERVER_REPLY_AUTH_ABORTED);
+ sasl_server_auth_cancel(client, NULL, NULL, SASL_SERVER_REPLY_AUTH_ABORTED);
}
const char *service, const char *mech_name,
const char *initial_resp_base64,
sasl_server_callback_t *callback);
-void sasl_server_auth_failed(struct client *client, const char *reason);
+void sasl_server_auth_failed(struct client *client, const char *reason,
+ const char *code) ATTR_NULL(3);
void sasl_server_auth_abort(struct client *client);
#endif
case CLIENT_AUTH_RESULT_PASS_EXPIRED:
case CLIENT_AUTH_RESULT_SSL_REQUIRED:
case CLIENT_AUTH_RESULT_LOGIN_DISABLED:
+ case CLIENT_AUTH_RESULT_MECH_INVALID:
+ case CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED:
client_send_reply(client, POP3_CMD_REPLY_AUTH_ERROR, text);
break;
default: