2 Unix SMB/CIFS implementation.
6 Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "http_internal.h"
25 #include "lib/util/tevent_ntstatus.h"
26 #include "lib/param/param.h"
28 #include "auth/gensec/gensec.h"
29 #include "auth/credentials/credentials.h"
30 #include "lib/util/data_blob.h"
33 * Copy the request headers from src to dst
35 static NTSTATUS http_copy_header(const struct http_request *src,
36 struct http_request *dst)
38 struct http_header *h;
40 dst->type = src->type;
41 dst->major = src->major;
42 dst->minor = src->minor;
43 dst->uri = talloc_strdup(dst, src->uri);
45 for (h = src->headers; h != NULL; h = h->next) {
46 http_add_header(dst, &dst->headers, h->key, h->value);
48 dst->headers_size = src->headers_size;
54 * Retrieve the WWW-Authenticate header from server response based on the
55 * authentication scheme being used.
57 static NTSTATUS http_parse_auth_response(enum http_auth_method auth,
58 struct http_request *auth_response,
61 struct http_header *h;
63 for (h = auth_response->headers; h != NULL; h = h->next) {
66 cmp = strcasecmp(h->key, "WWW-Authenticate");
73 if (strncasecmp(h->value, "NTLM ", 5) == 0) {
74 *in = data_blob_string_const(h->value);
83 return NT_STATUS_NOT_SUPPORTED;
86 struct http_auth_state {
87 struct tevent_context *ev;
89 struct tstream_context *stream;
90 struct tevent_queue *send_queue;
92 enum http_auth_method auth;
94 struct gensec_security *gensec_ctx;
95 NTSTATUS gensec_status;
97 const struct http_request *original_request;
98 struct http_request *next_request;
99 struct http_request *auth_response;
103 static void http_send_auth_request_gensec_done(struct tevent_req *subreq);
104 static void http_send_auth_request_http_req_done(struct tevent_req *subreq);
105 static void http_send_auth_request_http_rep_done(struct tevent_req *subreq);
107 struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
108 struct tevent_context *ev,
109 struct tstream_context *stream,
110 struct tevent_queue *send_queue,
111 const struct http_request *original_request,
112 struct cli_credentials *credentials,
113 struct loadparm_context *lp_ctx,
114 enum http_auth_method auth)
116 struct tevent_req *req = NULL;
117 struct http_auth_state *state = NULL;
118 struct tevent_req *subreq = NULL;
119 DATA_BLOB gensec_in = data_blob_null;
121 const char *mech_name = NULL;
123 req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
128 state->stream = stream;
129 state->send_queue = send_queue;
131 state->original_request = original_request;
133 status = gensec_init();
134 if (tevent_req_nterror(req, status)) {
135 return tevent_req_post(req, ev);
138 status = gensec_client_start(state, &state->gensec_ctx,
139 lpcfg_gensec_settings(state, lp_ctx));
140 if (tevent_req_nterror(req, status)) {
141 return tevent_req_post(req, ev);
144 status = gensec_set_credentials(state->gensec_ctx, credentials);
145 if (tevent_req_nterror(req, status)) {
146 return tevent_req_post(req, ev);
149 switch (state->auth) {
150 case HTTP_AUTH_BASIC:
151 mech_name = "http_basic";
154 mech_name = "http_ntlm";
157 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
158 return tevent_req_post(req, ev);
161 status = gensec_start_mech_by_name(state->gensec_ctx, mech_name);
162 if (tevent_req_nterror(req, status)) {
163 return tevent_req_post(req, ev);
166 subreq = gensec_update_send(state, state->ev,
169 if (tevent_req_nomem(subreq, req)) {
170 return tevent_req_post(req, ev);
172 tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
177 static void http_send_auth_request_gensec_done(struct tevent_req *subreq)
179 struct tevent_req *req =
180 tevent_req_callback_data(subreq,
182 struct http_auth_state *state =
184 struct http_auth_state);
185 DATA_BLOB gensec_out = data_blob_null;
189 TALLOC_FREE(state->auth_response);
191 status = gensec_update_recv(subreq, state, &gensec_out);
193 state->gensec_status = status;
194 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
195 status = NT_STATUS_OK;
197 if (tevent_req_nterror(req, status)) {
201 state->next_request = talloc_zero(state, struct http_request);
202 if (tevent_req_nomem(state->next_request, req)) {
206 status = http_copy_header(state->original_request, state->next_request);
207 if (tevent_req_nterror(req, status)) {
211 if (!NT_STATUS_IS_OK(state->gensec_status)) {
213 * More preprocessing required before we
214 * can include the content.
216 ret = http_replace_header(state->next_request,
217 &state->next_request->headers,
218 "Content-Length", "0");
224 state->next_request->body = state->original_request->body;
227 if (gensec_out.length > 0) {
228 ret = http_add_header(state->next_request,
229 &state->next_request->headers,
231 (char *)gensec_out.data);
236 data_blob_free(&gensec_out);
239 subreq = http_send_request_send(state, state->ev,
242 state->next_request);
243 if (tevent_req_nomem(subreq, req)) {
246 tevent_req_set_callback(subreq,
247 http_send_auth_request_http_req_done,
251 static void http_send_auth_request_http_req_done(struct tevent_req *subreq)
253 struct tevent_req *req =
254 tevent_req_callback_data(subreq,
256 struct http_auth_state *state =
258 struct http_auth_state);
261 TALLOC_FREE(state->next_request);
263 status = http_send_request_recv(subreq);
265 if (tevent_req_nterror(req, status)) {
270 * If no more processing required, it is done
272 * The caller will use http_read_response_send/recv
273 * in order to get the high level response.
275 if (NT_STATUS_IS_OK(state->gensec_status)) {
276 tevent_req_done(req);
281 * If more processing required, read the response from server
283 * We may get an empty RPCH Echo packet from the server
284 * on the "RPC_OUT_DATA" path. We need to consume this
285 * from the socket, but for now we just ignore the bytes.
287 subreq = http_read_response_send(state, state->ev,
290 if (tevent_req_nomem(subreq, req)) {
293 tevent_req_set_callback(subreq,
294 http_send_auth_request_http_rep_done,
298 static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
300 struct tevent_req *req =
301 tevent_req_callback_data(subreq,
303 struct http_auth_state *state =
305 struct http_auth_state);
306 DATA_BLOB gensec_in = data_blob_null;
309 status = http_read_response_recv(subreq, state,
310 &state->auth_response);
312 if (tevent_req_nterror(req, status)) {
317 * We we asked for up to UINT16_MAX bytes of
318 * content, we don't expect
319 * state->auth_response->remaining_content_length
322 * For now we just ignore any bytes in
323 * state->auth_response->body.
325 if (state->auth_response->remaining_content_length != 0) {
326 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
330 status = http_parse_auth_response(state->auth,
331 state->auth_response,
333 if (tevent_req_nterror(req, status)) {
337 subreq = gensec_update_send(state, state->ev,
340 if (tevent_req_nomem(subreq, req)) {
343 tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
346 NTSTATUS http_send_auth_request_recv(struct tevent_req *req)
350 if (tevent_req_is_nterror(req, &status)) {
351 tevent_req_received(req);
354 tevent_req_received(req);