s4:lib/http: let http_read_response_send/recv() also consume the body if it fits...
authorStefan Metzmacher <metze@samba.org>
Thu, 20 Jul 2017 16:12:27 +0000 (18:12 +0200)
committerAndreas Schneider <asn@cryptomilk.org>
Mon, 7 Aug 2017 13:20:01 +0000 (15:20 +0200)
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>
source4/lib/http/http.c
source4/lib/http/http.h
source4/lib/http/http_auth.c
source4/lib/http/http_internal.h
source4/librpc/rpc/dcerpc_roh_channel_out.c

index c6976ee243d67ed6ce7b735ed8c5f7d6ede242f2..8fac33f2c1c768c92954d26309e5ffcd7ef5343c 100644 (file)
 
 /**
  * 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)) {
index 35fe8a7655be601c8acd7ae3299cf8c3267891c5..a871462b6a1084a8289bce228fde29d6fb74c113 100644 (file)
@@ -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 **);
index 3d2148ecd0eaf1e1de5f081b170383f72e8e0a96..d134bd6521999f4cd2e20c254cdc7995fc0f9a29 100644 (file)
@@ -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);
index 72ea9dfaa8d8d7defabb1b3b5026727c33cc0b6b..040466dcb293885eb8912ad8757a02015bcb9999 100644 (file)
@@ -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;
 };
index b370e564ee1547a9b3f2a23889a99fab53dc9666..8a337ad167fdd1f479da4a9613d5b5ebcbc7b7a2 100644 (file)
@@ -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);
        }