From: Stephan Bosch Date: Mon, 23 Oct 2023 19:04:15 +0000 (+0200) Subject: auth: sasl-server-request - Track and enforce valid SASL interaction state X-Git-Tag: 2.4.2~193 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2609ab981d697a640d91bb18aee4da37259529c2;p=thirdparty%2Fdovecot%2Fcore.git auth: sasl-server-request - Track and enforce valid SASL interaction state --- diff --git a/src/auth/auth-request.c b/src/auth/auth-request.c index 037a98dd30..7af85e935c 100644 --- a/src/auth/auth-request.c +++ b/src/auth/auth-request.c @@ -298,8 +298,6 @@ static void auth_request_success_continue(struct auth_policy_check_ctx *ctx) if (ctx->success_data->used > 0 && !request->fields.final_resp_ok) { /* we'll need one more SASL round, since client doesn't support the final SASL response */ - i_assert(!request->final_resp_sent); - request->final_resp_sent = TRUE; auth_request_handler_reply_continue(request, ctx->success_data->data, ctx->success_data->used); return; @@ -324,8 +322,6 @@ void auth_request_fail_with_reply(struct auth_request *request, if (final_data_size > 0 && !request->fields.final_resp_ok) { /* Otherwise, we need to send the data as part of a normal challenge and wait for a dummy client response. */ - i_assert(!request->final_resp_sent); - request->final_resp_sent = TRUE; auth_request_handler_reply_continue(request, final_data, final_data_size); return; @@ -418,15 +414,6 @@ void auth_request_continue(struct auth_request *request, { i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); - if (request->final_resp_sent) { - if (!request->fields.successful) { - auth_request_fail(request); - return; - } - auth_request_success(request, "", 0); - return; - } - auth_request_refresh_last_access(request); auth_sasl_request_continue(request, data, data_size); } diff --git a/src/auth/auth-request.h b/src/auth/auth-request.h index c30a6e36cd..308d4d934c 100644 --- a/src/auth/auth-request.h +++ b/src/auth/auth-request.h @@ -241,9 +241,6 @@ struct auth_request { needs to be tracked outside regular extra fields, because they get rolled back on passdb failure. */ bool failure_nodelay:1; - /* Sent final response (challenge). Waiting for dummy client response. - */ - bool final_resp_sent:1; bool event_finished_sent:1; diff --git a/src/auth/auth-sasl.c b/src/auth/auth-sasl.c index e6fa3534ed..c985041e3e 100644 --- a/src/auth/auth-sasl.c +++ b/src/auth/auth-sasl.c @@ -145,7 +145,8 @@ auth_sasl_request_output(struct sasl_server_req_ctx *rctx, auth_request_internal_failure(request); break; case SASL_SERVER_OUTPUT_FAILURE: - auth_request_fail(request); + auth_request_fail_with_reply( + request, output->data, output->data_size); break; case SASL_SERVER_OUTPUT_CONTINUE: auth_request_handler_reply_continue(request, output->data, diff --git a/src/auth/sasl-server-private.h b/src/auth/sasl-server-private.h index d48b8294db..7b908dd481 100644 --- a/src/auth/sasl-server-private.h +++ b/src/auth/sasl-server-private.h @@ -19,8 +19,14 @@ struct sasl_server_request { struct sasl_server_mech_request *mech; struct event *event; + enum sasl_server_request_state state; + unsigned int sequence; + enum sasl_server_passdb_type passdb_type; sasl_server_mech_passdb_callback_t *passdb_callback; + + bool failed:1; + bool finished_with_data:1; }; struct sasl_server_mech_reg { diff --git a/src/auth/sasl-server-request.c b/src/auth/sasl-server-request.c index 5a5deefb6b..f8d0b48d81 100644 --- a/src/auth/sasl-server-request.c +++ b/src/auth/sasl-server-request.c @@ -158,6 +158,9 @@ void sasl_server_request_initial(struct sasl_server_req_ctx *rctx, struct sasl_server_mech_request *mreq = req->mech; const struct sasl_server_mech *mech = mreq->mech; + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW); + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (sasl_server_request_fail_on_nuls(req, data, data_size)) return; @@ -174,6 +177,19 @@ void sasl_server_request_input(struct sasl_server_req_ctx *rctx, struct sasl_server_mech_request *mreq = req->mech; const struct sasl_server_mech *mech = mreq->mech; + if (req->state == SASL_SERVER_REQUEST_STATE_FINISHED && + req->finished_with_data) { + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (!req->failed) + sasl_server_request_success(mreq, "", 0); + else + sasl_server_request_failure(mreq); + return; + } + i_assert(req->state == SASL_SERVER_REQUEST_STATE_CLIENT); + i_assert(!req->finished_with_data); + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (sasl_server_request_fail_on_nuls(req, data, data_size)) return; @@ -207,7 +223,11 @@ bool sasl_server_request_set_authid(struct sasl_server_mech_request *mreq, i_assert(req->rctx != NULL); i_assert(funcs->request_set_authid != NULL); - return funcs->request_set_authid(req->rctx, authid_type, authid); + if (!funcs->request_set_authid(req->rctx, authid_type, authid)) { + req->failed = TRUE; + return FALSE; + } + return TRUE; } bool sasl_server_request_set_authzid(struct sasl_server_mech_request *mreq, @@ -219,7 +239,11 @@ bool sasl_server_request_set_authzid(struct sasl_server_mech_request *mreq, i_assert(req->rctx != NULL); i_assert(funcs->request_set_authzid != NULL); - return funcs->request_set_authzid(req->rctx, authzid); + if (!funcs->request_set_authzid(req->rctx, authzid)) { + req->failed = TRUE; + return FALSE; + } + return TRUE; } void sasl_server_request_set_realm(struct sasl_server_mech_request *mreq, @@ -286,6 +310,13 @@ void sasl_server_request_output(struct sasl_server_mech_request *mreq, i_assert(req->rctx != NULL); + i_assert(!req->failed); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER || + req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_CLIENT; + req->sequence++; + const struct sasl_server_output output = { .status = SASL_SERVER_OUTPUT_CONTINUE, .data = data, @@ -304,6 +335,17 @@ void sasl_server_request_success(struct sasl_server_mech_request *mreq, i_assert(req->rctx != NULL); + i_assert(!req->failed); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER || + req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_FINISHED; + req->sequence++; + if (data_size > 0) { + i_assert(!req->finished_with_data); + req->finished_with_data = TRUE; + } + const struct sasl_server_output output = { .status = SASL_SERVER_OUTPUT_SUCCESS, .data = data, @@ -324,6 +366,18 @@ sasl_server_request_failure_common(struct sasl_server_mech_request *mreq, i_assert(req->rctx != NULL); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER || + req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_FINISHED; + req->sequence++; + req->failed = TRUE; + if (data_size > 0) { + i_assert(status != SASL_SERVER_OUTPUT_INTERNAL_FAILURE); + i_assert(!req->finished_with_data); + req->finished_with_data = TRUE; + } + const struct sasl_server_output output = { .status = status, .data = data, @@ -360,6 +414,11 @@ verify_plain_callback(struct sasl_server_req_ctx *rctx, { struct sasl_server_request *req = rctx->request; + i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) + req->failed = TRUE; + i_assert(req->passdb_type == SASL_SERVER_PASSDB_TYPE_VERIFY_PLAIN); req->passdb_callback(req->mech, result); } @@ -374,6 +433,11 @@ void sasl_server_request_verify_plain( i_assert(req->rctx != NULL); + i_assert(!req->failed); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER); + req->state = SASL_SERVER_REQUEST_STATE_PASSDB; + req->passdb_type = SASL_SERVER_PASSDB_TYPE_VERIFY_PLAIN; req->passdb_callback = callback; @@ -388,6 +452,11 @@ lookup_credentials_callback(struct sasl_server_req_ctx *rctx, { struct sasl_server_request *req = rctx->request; + i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) + req->failed = TRUE; + i_assert(req->passdb_type == SASL_SERVER_PASSDB_TYPE_LOOKUP_CREDENTIALS); req->passdb_callback(req->mech, result); @@ -403,6 +472,11 @@ void sasl_server_request_lookup_credentials( i_assert(req->rctx != NULL); + i_assert(!req->failed); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER); + req->state = SASL_SERVER_REQUEST_STATE_PASSDB; + req->passdb_type = SASL_SERVER_PASSDB_TYPE_LOOKUP_CREDENTIALS; req->passdb_callback = callback; @@ -417,6 +491,11 @@ set_credentials_callback(struct sasl_server_req_ctx *rctx, { struct sasl_server_request *req = rctx->request; + i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); + req->state = SASL_SERVER_REQUEST_STATE_SERVER; + if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) + req->failed = TRUE; + i_assert(req->passdb_type == SASL_SERVER_PASSDB_TYPE_SET_CREDENTIALS); req->passdb_callback(req->mech, result); } @@ -432,6 +511,11 @@ void sasl_server_request_set_credentials( i_assert(req->rctx != NULL); + i_assert(!req->failed); + i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || + req->state == SASL_SERVER_REQUEST_STATE_SERVER); + req->state = SASL_SERVER_REQUEST_STATE_PASSDB; + req->passdb_type = SASL_SERVER_PASSDB_TYPE_SET_CREDENTIALS; req->passdb_callback = callback; diff --git a/src/auth/sasl-server.h b/src/auth/sasl-server.h index 97ddf25fae..2431e3d3a6 100644 --- a/src/auth/sasl-server.h +++ b/src/auth/sasl-server.h @@ -91,6 +91,21 @@ enum sasl_server_authid_type { SASL_SERVER_AUTHID_TYPE_EXTERNAL, }; +enum sasl_server_request_state { + /* Request is newly created */ + SASL_SERVER_REQUEST_STATE_NEW = 0, + /* Server needs to act next on this request */ + SASL_SERVER_REQUEST_STATE_SERVER, + /* Client needs to act next on this request */ + SASL_SERVER_REQUEST_STATE_CLIENT, + /* Server is waiting for passdb lookup */ + SASL_SERVER_REQUEST_STATE_PASSDB, + /* Request is finished */ + SASL_SERVER_REQUEST_STATE_FINISHED, + /* Request is aborted */ + SASL_SERVER_REQUEST_STATE_ABORTED, +}; + struct sasl_server_req_ctx { const struct sasl_server_mech *mech; const char *mech_name;