From 8268374c8307170f03b149a8fa7ed049c21889bf Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 11 May 2017 15:34:08 +0200 Subject: s4:lib/http: rewrite http_send_auth_request_*() using gensec_update_send/recv The new logic makes it much clearer that we have a loop of gensec_update_send() gensec_update_recv() http_send_request_send() http_send_request_recv() http_read_response_send() http_read_response_recv() Until the local gensec and the server are ready. I've tested this against Windows 2008R2 like this: bin/smbtorture \ -W BLA --realm=BLA.BASE \ -s /dev/null -Uadministrator%A1b2C3d4 \ ncacn_http:w2k8r2-219[593,RpcProxy=w2k8r2-219.bla.base,HttpUseTls=false,HttpAuthOption=basic] \ rpc.epmapper.epmapper.Lookup_simple \ and: bin/smbtorture \ -W BLA --realm=BLA.BASE \ -s /dev/null -Uadministrator%A1b2C3d4 \ ncacn_http:w2k8r2-219[593,RpcProxy=w2k8r2-219.bla.base,HttpUseTls=false,HttpAuthOption=ntlm] \ rpc.epmapper.epmapper.Lookup_simple \ Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider --- source4/lib/http/http.h | 2 +- source4/lib/http/http_auth.c | 337 +++++++++++++++++++------------------------ 2 files changed, 153 insertions(+), 186 deletions(-) diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h index 75a04b1179b..a601a03728b 100644 --- a/source4/lib/http/http.h +++ b/source4/lib/http/http.h @@ -111,7 +111,7 @@ struct tevent_req *http_send_auth_request_send(TALLOC_CTX *, struct tevent_context *, struct tstream_context *, struct tevent_queue *, - struct http_request *, + const struct http_request *, struct cli_credentials *, struct loadparm_context *, enum http_auth_method); diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c index b6f102f9d8b..3d2148ecd0e 100644 --- a/source4/lib/http/http_auth.c +++ b/source4/lib/http/http_auth.c @@ -32,7 +32,7 @@ /** * Copy the request headers from src to dst */ -static NTSTATUS http_copy_header(struct http_request *src, +static NTSTATUS http_copy_header(const struct http_request *src, struct http_request *dst) { struct http_header *h; @@ -78,278 +78,245 @@ static NTSTATUS http_parse_auth_response(enum http_auth_method auth, return NT_STATUS_NOT_SUPPORTED; } -/* - * Create the next authentication request to send to server if authentication - * is not completed. If it is completed, attachs the 'Authorization' header - * to the original request. - */ -static NTSTATUS http_create_auth_request(TALLOC_CTX *mem_ctx, - struct gensec_security *gensec_ctx, - struct tevent_context *ev, - enum http_auth_method auth, - struct http_request *original_request, - struct http_request *auth_response, - struct http_request **auth_request) -{ - NTSTATUS status; - DATA_BLOB in, out; - - if (auth_response) { - status = http_parse_auth_response(auth, auth_response, &in); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } else { - in = data_blob_null; - } - - status = gensec_update_ev(gensec_ctx, mem_ctx, ev, in, &out); - if (NT_STATUS_IS_OK(status)) { - if (out.length) { - http_add_header(original_request, - &original_request->headers, - "Authorization", (char*)out.data); - } - } +struct http_auth_state { + struct tevent_context *ev; - if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - NTSTATUS status2; - - *auth_request = talloc_zero(mem_ctx, struct http_request); - if (*auth_request == NULL) { - return NT_STATUS_NO_MEMORY; - } + struct tstream_context *stream; + struct tevent_queue *send_queue; - status2 = http_copy_header(original_request, *auth_request); - if (!NT_STATUS_IS_OK(status2)) { - talloc_free(*auth_request); - return status2; - } - - http_replace_header(*auth_request, &((*auth_request)->headers), - "Content-Length", "0"); - if (out.length) { - http_add_header(*auth_request, - &((*auth_request)->headers), - "Authorization", (char*)out.data); - } - } + enum http_auth_method auth; - return status; -} + struct gensec_security *gensec_ctx; + NTSTATUS gensec_status; -struct http_auth_state -{ - struct loadparm_context *lp_ctx; - struct tevent_context *ev; - struct tstream_context *stream; - struct tevent_queue *send_queue; - struct cli_credentials *credentials; - struct http_request *original_request; - struct gensec_security *gensec_ctx; - NTSTATUS gensec_status; - enum http_auth_method auth; - - int sys_errno; - int nwritten; + const struct http_request *original_request; + struct http_request *next_request; + struct http_request *auth_response; }; -static void http_send_auth_request_done(struct tevent_req *); +static void http_send_auth_request_gensec_done(struct tevent_req *subreq); +static void http_send_auth_request_http_req_done(struct tevent_req *subreq); +static void http_send_auth_request_http_rep_done(struct tevent_req *subreq); + struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream, struct tevent_queue *send_queue, - struct http_request *original_request, + const struct http_request *original_request, struct cli_credentials *credentials, struct loadparm_context *lp_ctx, enum http_auth_method auth) { - struct tevent_req *req; - struct tevent_req *subreq; - struct http_auth_state *state; + struct tevent_req *req = NULL; + struct http_auth_state *state = NULL; + struct tevent_req *subreq = NULL; + DATA_BLOB gensec_in = data_blob_null; NTSTATUS status; - struct http_request *auth_request = NULL; - struct http_request *request_to_send; + const char *mech_name = NULL; req = tevent_req_create(mem_ctx, &state, struct http_auth_state); if (req == NULL) { return NULL; } - state->ev = ev; state->stream = stream; state->send_queue = send_queue; - state->original_request = original_request; - state->credentials = credentials; - state->lp_ctx = lp_ctx; state->auth = auth; + state->original_request = original_request; status = gensec_init(); - if (!NT_STATUS_IS_OK(status)) { - goto post_status; + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } + status = gensec_client_start(state, &state->gensec_ctx, lpcfg_gensec_settings(state, lp_ctx)); - if (!NT_STATUS_IS_OK(status)) { - goto post_status; + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } + status = gensec_set_credentials(state->gensec_ctx, credentials); - if (!NT_STATUS_IS_OK(status)) { - goto post_status; + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } switch (state->auth) { case HTTP_AUTH_BASIC: - status = gensec_start_mech_by_name(state->gensec_ctx, - "http_basic"); - if (!NT_STATUS_IS_OK(status)) { - goto post_status; - } + mech_name = "http_basic"; break; case HTTP_AUTH_NTLM: - status = gensec_start_mech_by_name(state->gensec_ctx, - "http_ntlm"); - if (!NT_STATUS_IS_OK(status)) { - goto post_status; - } + mech_name = "http_ntlm"; break; default: tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); return tevent_req_post(req, ev); } - /* - * Store the gensec status to read the server response on callback - * if more processing is required - */ - state->gensec_status = http_create_auth_request(state, - state->gensec_ctx, - state->ev, - state->auth, - state->original_request, - NULL, - &auth_request); - if (!NT_STATUS_IS_OK(state->gensec_status) && - !NT_STATUS_EQUAL(state->gensec_status, - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - goto post_status; + status = gensec_start_mech_by_name(state->gensec_ctx, mech_name); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } - /* - * If no more processing is necessary, the http_create_auth_request - * function will attach the authentication header to the original - * request - */ - request_to_send = NT_STATUS_IS_OK(state->gensec_status) ? - state->original_request : auth_request; - - subreq = http_send_request_send(state, ev, stream, send_queue, - request_to_send); + subreq = gensec_update_send(state, state->ev, + state->gensec_ctx, + gensec_in); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } - tevent_req_set_callback(subreq, http_send_auth_request_done, req); + tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req); + return req; -post_status: - tevent_req_nterror(req, status); - return tevent_req_post(req, ev); } -static void http_send_auth_request_done2(struct tevent_req *subreq); -static void http_send_auth_request_done(struct tevent_req *subreq) +static void http_send_auth_request_gensec_done(struct tevent_req *subreq) { - NTSTATUS status; - struct tevent_req *req; - struct http_auth_state *state; + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct http_auth_state *state = + tevent_req_data(req, + struct http_auth_state); + DATA_BLOB gensec_out = data_blob_null; + NTSTATUS status; + int ret; - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct http_auth_state); + TALLOC_FREE(state->auth_response); - status = http_send_request_recv(subreq); + status = gensec_update_recv(subreq, state, &gensec_out); TALLOC_FREE(subreq); + state->gensec_status = status; + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + status = NT_STATUS_OK; + } if (tevent_req_nterror(req, status)) { return; } - /* If no more processing required, it is done */ - if (NT_STATUS_IS_OK(state->gensec_status)) { - tevent_req_done(req); + state->next_request = talloc_zero(state, struct http_request); + if (tevent_req_nomem(state->next_request, req)) { return; } - /* If more processing required, read the response from server */ - if (NT_STATUS_EQUAL(state->gensec_status, - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - subreq = http_read_response_send(state, state->ev, - state->stream); - if (tevent_req_nomem(subreq, req)) { + status = http_copy_header(state->original_request, state->next_request); + if (tevent_req_nterror(req, status)) { + return; + } + + if (!NT_STATUS_IS_OK(state->gensec_status)) { + /* + * More preprocessing required before we + * can include the content. + */ + ret = http_replace_header(state->next_request, + &state->next_request->headers, + "Content-Length", "0"); + if (ret != 0) { + tevent_req_oom(req); + return; + } + } else { + state->next_request->body = state->original_request->body; + } + + if (gensec_out.length > 0) { + ret = http_add_header(state->next_request, + &state->next_request->headers, + "Authorization", + (char *)gensec_out.data); + if (ret != 0) { + tevent_req_oom(req); return; } - tevent_req_set_callback(subreq, http_send_auth_request_done2, - req); + data_blob_free(&gensec_out); + } + + subreq = http_send_request_send(state, state->ev, + state->stream, + state->send_queue, + state->next_request); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + http_send_auth_request_http_req_done, + req); +} + +static void http_send_auth_request_http_req_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct http_auth_state *state = + tevent_req_data(req, + struct http_auth_state); + NTSTATUS status; + + TALLOC_FREE(state->next_request); + + status = http_send_request_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { return; } /* - * If gensec status is not NT_STATUS_OK neither - * NT_STATUS_MORE_PROCESSING_REQUIRED , it is an error + * If no more processing required, it is done + * + * The caller will use http_read_response_send/recv + * in order to get the high level response. */ - tevent_req_nterror(req, state->gensec_status); + if (NT_STATUS_IS_OK(state->gensec_status)) { + tevent_req_done(req); + return; + } + + /* If more processing required, read the response from server */ + subreq = http_read_response_send(state, state->ev, + state->stream); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + http_send_auth_request_http_rep_done, + req); } -static void http_send_auth_request_done2(struct tevent_req *subreq) +static void http_send_auth_request_http_rep_done(struct tevent_req *subreq) { + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct http_auth_state *state = + tevent_req_data(req, + struct http_auth_state); + DATA_BLOB gensec_in = data_blob_null; NTSTATUS status; - struct tevent_req *req; - struct http_auth_state *state; - struct http_request *auth_response; - struct http_request *auth_request = NULL; - struct http_request *request_to_send; - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct http_auth_state); - - status = http_read_response_recv(subreq, state, &auth_response); + status = http_read_response_recv(subreq, state, + &state->auth_response); TALLOC_FREE(subreq); if (tevent_req_nterror(req, status)) { return; } - state->gensec_status = http_create_auth_request(state, - state->gensec_ctx, - state->ev, - state->auth, - state->original_request, - auth_response, - &auth_request); - if (!NT_STATUS_IS_OK(state->gensec_status) && - !NT_STATUS_EQUAL(state->gensec_status, - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - tevent_req_nterror(req, status); + status = http_parse_auth_response(state->auth, + state->auth_response, + &gensec_in); + if (tevent_req_nterror(req, status)) { return; } - /* - * If no more processing is necessary, the http_create_auth_request - * function will attach the authentication header to the original - * request - */ - request_to_send = NT_STATUS_IS_OK(state->gensec_status) ? - state->original_request : auth_request; - - subreq = http_send_request_send(state, - state->ev, - state->stream, - state->send_queue, - request_to_send); + subreq = gensec_update_send(state, state->ev, + state->gensec_ctx, + gensec_in); if (tevent_req_nomem(subreq, req)) { return; } - tevent_req_set_callback(subreq, http_send_auth_request_done, req); + tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req); } - NTSTATUS http_send_auth_request_recv(struct tevent_req *req) { NTSTATUS status; -- cgit v1.2.1