]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Added a code= field to the auth FAIL response that replaces the "authz", "temp...
authorStephan Bosch <stephan.bosch@dovecot.fi>
Tue, 1 Nov 2016 20:32:42 +0000 (21:32 +0100)
committerGitLab <gitlab@git.dovecot.net>
Wed, 2 Nov 2016 12:01:45 +0000 (14:01 +0200)
src/auth/auth-request-handler.c
src/lib-auth/auth-client-interface.h
src/login-common/client-common-auth.c
src/login-common/client-common.c
src/login-common/client-common.h

index b3493f4854a1c0425c90efaf403146d0352ff0d1..8706f6a4f466336a8e66c7d6cd013e9ce961d514 100644 (file)
@@ -280,6 +280,7 @@ auth_request_handler_reply_success_finish(struct auth_request *request)
 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");
@@ -292,34 +293,40 @@ auth_request_handler_reply_failure_finish(struct auth_request *request)
                                      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));
 }
@@ -396,21 +403,34 @@ void auth_request_handler_reply_continue(struct auth_request *request,
                                   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);
index ee24e286734572087d04f0ced0124a9dc82fe7b0..493f6ec120d65b69ddfb6debafc844dd2109536d 100644 (file)
@@ -4,7 +4,7 @@
 /* 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
@@ -26,4 +26,10 @@ enum mech_security_flags {
        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
index bd7631ba343c9a2c46e471b9e56ab52c467a8e50..0c3373f6aac2aaa83d1cd5d6a5910ef9702ee56a 100644 (file)
 #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);
@@ -110,18 +141,10 @@ static void client_auth_parse_args(struct client *client, bool success,
                        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)
@@ -169,6 +192,12 @@ static void client_auth_parse_args(struct client *client, bool success,
                                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 */
@@ -478,29 +507,34 @@ client_auth_handle_reply(struct client *client,
                        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;
@@ -609,6 +643,7 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
                 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:
@@ -617,6 +652,7 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
                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;
                }
@@ -630,6 +666,7 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
                        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))
index ae383b263e25a68486a2a490ae510bc5db240728..3f49356727a668e8d5c6514284f76c2e5a5564bf 100644 (file)
@@ -750,10 +750,22 @@ const char *client_get_extra_disconnect_reason(struct client *client)
                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);
 }
index 5cf00567ea55a1fd56a38ee3cefe6f9622efb16c..27a64fb6d323a305609c57705a702c8a21f74725 100644 (file)
@@ -43,6 +43,14 @@ enum client_disconnect_reason {
        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,
@@ -57,6 +65,8 @@ enum client_auth_result {
 
 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;
@@ -71,9 +81,7 @@ struct client_auth_reply {
        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 {
@@ -150,6 +158,7 @@ struct client {
 
        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. */
@@ -179,8 +188,6 @@ struct client {
        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;
        /* ... */