mirror of
https://github.com/samba-team/samba.git
synced 2025-02-03 13:47:25 +03:00
s4:lib/http: let http_read_response_send/recv() also consume the body if it fits into a max value
We need to consume full HTTP responses from the socket during the authentication exchanges, otherwise our HTTP parser gets out of sync for the next requests. This will be important for gensec mechs which use an even number for authentication packets. I guess this should be done just based on the Content-Length value and not based on the response code. So far I saw bodies with 200 and 401 codes. Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Andreas Schneider <asn@samba.org>
This commit is contained in:
parent
7b86da08ea
commit
e42f12c6be
@ -30,19 +30,39 @@
|
||||
|
||||
/**
|
||||
* Determines if a response should have a body.
|
||||
* Follows the rules in RFC 2616 section 4.3.
|
||||
* @return 1 if the response MUST have a body; 0 if the response MUST NOT have
|
||||
* a body. Returns -1 on error.
|
||||
*/
|
||||
static int http_response_needs_body(struct http_request *req)
|
||||
{
|
||||
struct http_header *h = NULL;
|
||||
|
||||
if (!req) return -1;
|
||||
|
||||
/* If response code is 503, the body contains the error description
|
||||
* (2.1.2.1.3)
|
||||
*/
|
||||
if (req->response_code == 503)
|
||||
return 1;
|
||||
for (h = req->headers; h != NULL; h = h->next) {
|
||||
int cmp;
|
||||
int n;
|
||||
char c;
|
||||
unsigned long long v;
|
||||
|
||||
cmp = strcasecmp(h->key, "Content-Length");
|
||||
if (cmp != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n = sscanf(h->value, "%llu%c", &v, &c);
|
||||
if (n != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
req->remaining_content_length = v;
|
||||
|
||||
if (v != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -92,15 +112,18 @@ static enum http_read_status http_parse_headers(struct http_read_response_state
|
||||
|
||||
ret = http_response_needs_body(state->response);
|
||||
switch (ret) {
|
||||
case 1:
|
||||
if (state->response->remaining_content_length <= state->max_content_length) {
|
||||
DEBUG(11, ("%s: Start of read body\n", __func__));
|
||||
state->parser_state = HTTP_READING_BODY;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case 0:
|
||||
DEBUG(11, ("%s: Skipping body for code %d\n", __func__,
|
||||
state->response->response_code));
|
||||
state->parser_state = HTTP_READING_DONE;
|
||||
break;
|
||||
case 1:
|
||||
DEBUG(11, ("%s: Start of read body\n", __func__));
|
||||
state->parser_state = HTTP_READING_BODY;
|
||||
break;
|
||||
case -1:
|
||||
DEBUG(0, ("%s_: Error in http_response_needs_body\n", __func__));
|
||||
TALLOC_FREE(line);
|
||||
@ -256,9 +279,19 @@ static enum http_read_status http_parse_firstline(struct http_read_response_stat
|
||||
|
||||
static enum http_read_status http_read_body(struct http_read_response_state *state)
|
||||
{
|
||||
enum http_read_status status = HTTP_DATA_CORRUPTED;
|
||||
/* TODO */
|
||||
return status;
|
||||
struct http_request *resp = state->response;
|
||||
|
||||
if (state->buffer.length < resp->remaining_content_length) {
|
||||
return HTTP_MORE_DATA_EXPECTED;
|
||||
}
|
||||
|
||||
resp->body = state->buffer;
|
||||
state->buffer = data_blob_null;
|
||||
talloc_steal(resp, resp->body.data);
|
||||
resp->remaining_content_length = 0;
|
||||
|
||||
state->parser_state = HTTP_READING_DONE;
|
||||
return HTTP_ALL_DATA_READ;
|
||||
}
|
||||
|
||||
static enum http_read_status http_read_trailer(struct http_read_response_state *state)
|
||||
@ -519,7 +552,8 @@ static int http_read_response_next_vector(struct tstream_context *stream,
|
||||
static void http_read_response_done(struct tevent_req *);
|
||||
struct tevent_req *http_read_response_send(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
struct tstream_context *stream)
|
||||
struct tstream_context *stream,
|
||||
size_t max_content_length)
|
||||
{
|
||||
struct tevent_req *req;
|
||||
struct tevent_req *subreq;
|
||||
@ -539,6 +573,7 @@ struct tevent_req *http_read_response_send(TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
|
||||
state->max_headers_size = HTTP_MAX_HEADER_SIZE;
|
||||
state->max_content_length = (uint64_t)max_content_length;
|
||||
state->parser_state = HTTP_READING_FIRSTLINE;
|
||||
state->response = talloc_zero(state, struct http_request);
|
||||
if (tevent_req_nomem(state->response, req)) {
|
||||
|
@ -82,6 +82,7 @@ struct http_request {
|
||||
size_t headers_size;
|
||||
unsigned int response_code; /* HTTP response code */
|
||||
char *response_code_line; /* Readable response */
|
||||
uint64_t remaining_content_length; /* data not represent in body */
|
||||
DATA_BLOB body;
|
||||
};
|
||||
|
||||
@ -101,7 +102,8 @@ NTSTATUS http_send_request_recv(struct tevent_req *);
|
||||
/* HTTP response */
|
||||
struct tevent_req *http_read_response_send(TALLOC_CTX *,
|
||||
struct tevent_context *,
|
||||
struct tstream_context *);
|
||||
struct tstream_context *,
|
||||
size_t max_content_length);
|
||||
NTSTATUS http_read_response_recv(struct tevent_req *,
|
||||
TALLOC_CTX *,
|
||||
struct http_request **);
|
||||
|
@ -272,9 +272,16 @@ static void http_send_auth_request_http_req_done(struct tevent_req *subreq)
|
||||
return;
|
||||
}
|
||||
|
||||
/* If more processing required, read the response from server */
|
||||
/*
|
||||
* If more processing required, read the response from server
|
||||
*
|
||||
* We may get an empty RPCH Echo packet from the server
|
||||
* on the "RPC_OUT_DATA" path. We need to consume this
|
||||
* from the socket, but for now we just ignore the bytes.
|
||||
*/
|
||||
subreq = http_read_response_send(state, state->ev,
|
||||
state->stream);
|
||||
state->stream,
|
||||
UINT16_MAX);
|
||||
if (tevent_req_nomem(subreq, req)) {
|
||||
return;
|
||||
}
|
||||
@ -301,6 +308,20 @@ static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We we asked for up to UINT16_MAX bytes of
|
||||
* content, we don't expect
|
||||
* state->auth_response->remaining_content_length
|
||||
* to be set.
|
||||
*
|
||||
* For now we just ignore any bytes in
|
||||
* state->auth_response->body.
|
||||
*/
|
||||
if (state->auth_response->remaining_content_length != 0) {
|
||||
tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
status = http_parse_auth_response(state->auth,
|
||||
state->auth_response,
|
||||
&gensec_in);
|
||||
|
@ -54,6 +54,7 @@ struct http_send_request_state {
|
||||
struct http_read_response_state {
|
||||
enum http_parser_state parser_state;
|
||||
size_t max_headers_size;
|
||||
uint64_t max_content_length;
|
||||
DATA_BLOB buffer;
|
||||
struct http_request *response;
|
||||
};
|
||||
|
@ -482,7 +482,8 @@ struct tevent_req *roh_recv_out_channel_response_send(TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
|
||||
subreq = http_read_response_send(state, ev,
|
||||
roh->default_channel_out->streams.active);
|
||||
roh->default_channel_out->streams.active,
|
||||
0); /* we'll get the content later */
|
||||
if (tevent_req_nomem(subreq, req)) {
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user