Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : HTTP library
5 :
6 : Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "http.h"
24 : #include "http_internal.h"
25 : #include "lib/util/tevent_ntstatus.h"
26 : #include "lib/param/param.h"
27 : #include "tevent.h"
28 : #include "auth/gensec/gensec.h"
29 : #include "auth/credentials/credentials.h"
30 : #include "lib/util/data_blob.h"
31 :
32 : #undef strcasecmp
33 : #undef strncasecmp
34 :
35 : /**
36 : * Copy the request headers from src to dst
37 : */
38 14 : static NTSTATUS http_copy_header(const struct http_request *src,
39 : struct http_request *dst)
40 : {
41 0 : struct http_header *h;
42 :
43 14 : dst->type = src->type;
44 14 : dst->major = src->major;
45 14 : dst->minor = src->minor;
46 14 : dst->uri = talloc_strdup(dst, src->uri);
47 :
48 42 : for (h = src->headers; h != NULL; h = h->next) {
49 28 : http_add_header(dst, &dst->headers, h->key, h->value);
50 : }
51 14 : dst->headers_size = src->headers_size;
52 :
53 14 : return NT_STATUS_OK;
54 : }
55 :
56 : /*
57 : * Retrieve the WWW-Authenticate header from server response based on the
58 : * authentication scheme being used.
59 : */
60 0 : static NTSTATUS http_parse_auth_response(const DATA_BLOB prefix,
61 : struct http_request *auth_response,
62 : DATA_BLOB *in)
63 : {
64 0 : struct http_header *h;
65 :
66 0 : for (h = auth_response->headers; h != NULL; h = h->next) {
67 0 : int cmp;
68 :
69 0 : cmp = strcasecmp(h->key, "WWW-Authenticate");
70 0 : if (cmp != 0) {
71 0 : continue;
72 : }
73 :
74 0 : cmp = strncasecmp(h->value,
75 0 : (const char *)prefix.data,
76 0 : prefix.length);
77 0 : if (cmp != 0) {
78 0 : continue;
79 : }
80 :
81 0 : *in = data_blob_string_const(h->value);
82 0 : return NT_STATUS_OK;
83 : }
84 :
85 0 : return NT_STATUS_NOT_SUPPORTED;
86 : }
87 :
88 : struct http_auth_state {
89 : struct tevent_context *ev;
90 :
91 : struct http_conn *http_conn;
92 :
93 : enum http_auth_method auth;
94 : DATA_BLOB prefix;
95 :
96 : struct gensec_security *gensec_ctx;
97 : NTSTATUS gensec_status;
98 :
99 : const struct http_request *original_request;
100 : struct http_request *next_request;
101 : struct http_request *auth_response;
102 : };
103 :
104 :
105 : static void http_send_auth_request_gensec_done(struct tevent_req *subreq);
106 : static void http_send_auth_request_http_req_done(struct tevent_req *subreq);
107 : static void http_send_auth_request_http_rep_done(struct tevent_req *subreq);
108 :
109 14 : struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
110 : struct tevent_context *ev,
111 : struct http_conn *http_conn,
112 : const struct http_request *original_request,
113 : struct cli_credentials *credentials,
114 : struct loadparm_context *lp_ctx,
115 : enum http_auth_method auth)
116 : {
117 14 : struct tevent_req *req = NULL;
118 14 : struct http_auth_state *state = NULL;
119 14 : struct tevent_req *subreq = NULL;
120 14 : DATA_BLOB gensec_in = data_blob_null;
121 0 : NTSTATUS status;
122 14 : struct http_header *h = NULL;
123 14 : const char *mech_name = NULL;
124 :
125 14 : req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
126 14 : if (req == NULL) {
127 0 : return NULL;
128 : }
129 14 : state->ev = ev;
130 14 : state->http_conn = http_conn;
131 14 : state->auth = auth;
132 14 : state->original_request = original_request;
133 :
134 14 : status = gensec_init();
135 14 : if (tevent_req_nterror(req, status)) {
136 0 : return tevent_req_post(req, ev);
137 : }
138 :
139 14 : status = gensec_client_start(state, &state->gensec_ctx,
140 : lpcfg_gensec_settings(state, lp_ctx));
141 14 : if (tevent_req_nterror(req, status)) {
142 0 : return tevent_req_post(req, ev);
143 : }
144 :
145 14 : status = gensec_set_credentials(state->gensec_ctx, credentials);
146 14 : if (tevent_req_nterror(req, status)) {
147 0 : return tevent_req_post(req, ev);
148 : }
149 :
150 42 : for (h = original_request->headers; h != NULL; h = h->next) {
151 0 : int cmp;
152 :
153 28 : cmp = strcasecmp(h->key, "Host");
154 28 : if (cmp != 0) {
155 28 : continue;
156 : }
157 :
158 0 : status = gensec_set_target_service(state->gensec_ctx, "http");
159 0 : if (tevent_req_nterror(req, status)) {
160 0 : return tevent_req_post(req, ev);
161 : }
162 :
163 0 : status = gensec_set_target_hostname(state->gensec_ctx, h->value);
164 0 : if (tevent_req_nterror(req, status)) {
165 0 : return tevent_req_post(req, ev);
166 : }
167 0 : break;
168 : }
169 :
170 14 : switch (state->auth) {
171 14 : case HTTP_AUTH_BASIC:
172 14 : mech_name = "http_basic";
173 14 : state->prefix = data_blob_string_const("Basic");
174 14 : break;
175 0 : case HTTP_AUTH_NTLM:
176 0 : mech_name = "http_ntlm";
177 0 : state->prefix = data_blob_string_const("NTLM");
178 0 : break;
179 0 : case HTTP_AUTH_NEGOTIATE:
180 0 : mech_name = "http_negotiate";
181 0 : state->prefix = data_blob_string_const("Negotiate");
182 0 : break;
183 0 : default:
184 0 : tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
185 0 : return tevent_req_post(req, ev);
186 : }
187 :
188 14 : status = gensec_start_mech_by_name(state->gensec_ctx, mech_name);
189 14 : if (tevent_req_nterror(req, status)) {
190 0 : return tevent_req_post(req, ev);
191 : }
192 :
193 14 : subreq = gensec_update_send(state, state->ev,
194 14 : state->gensec_ctx,
195 : gensec_in);
196 14 : if (tevent_req_nomem(subreq, req)) {
197 0 : return tevent_req_post(req, ev);
198 : }
199 14 : tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
200 :
201 14 : return req;
202 : }
203 :
204 14 : static void http_send_auth_request_gensec_done(struct tevent_req *subreq)
205 : {
206 0 : struct tevent_req *req =
207 14 : tevent_req_callback_data(subreq,
208 : struct tevent_req);
209 0 : struct http_auth_state *state =
210 14 : tevent_req_data(req,
211 : struct http_auth_state);
212 14 : DATA_BLOB gensec_out = data_blob_null;
213 0 : NTSTATUS status;
214 0 : int ret;
215 :
216 14 : TALLOC_FREE(state->auth_response);
217 :
218 14 : status = gensec_update_recv(subreq, state, &gensec_out);
219 14 : TALLOC_FREE(subreq);
220 14 : state->gensec_status = status;
221 14 : if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
222 0 : status = NT_STATUS_OK;
223 : }
224 14 : if (tevent_req_nterror(req, status)) {
225 0 : return;
226 : }
227 :
228 14 : state->next_request = talloc_zero(state, struct http_request);
229 14 : if (tevent_req_nomem(state->next_request, req)) {
230 0 : return;
231 : }
232 :
233 14 : status = http_copy_header(state->original_request, state->next_request);
234 14 : if (tevent_req_nterror(req, status)) {
235 0 : return;
236 : }
237 :
238 14 : if (!NT_STATUS_IS_OK(state->gensec_status)) {
239 : /*
240 : * More preprocessing required before we
241 : * can include the content.
242 : */
243 0 : ret = http_replace_header(state->next_request,
244 0 : &state->next_request->headers,
245 : "Content-Length", "0");
246 0 : if (ret != 0) {
247 0 : tevent_req_oom(req);
248 0 : return;
249 : }
250 : } else {
251 14 : state->next_request->body = state->original_request->body;
252 : }
253 :
254 14 : if (gensec_out.length > 0) {
255 14 : ret = http_add_header(state->next_request,
256 14 : &state->next_request->headers,
257 : "Authorization",
258 14 : (char *)gensec_out.data);
259 14 : if (ret != 0) {
260 0 : tevent_req_oom(req);
261 0 : return;
262 : }
263 14 : data_blob_free(&gensec_out);
264 : }
265 :
266 14 : subreq = http_send_request_send(state, state->ev,
267 : state->http_conn,
268 : state->next_request);
269 14 : if (tevent_req_nomem(subreq, req)) {
270 0 : return;
271 : }
272 14 : tevent_req_set_callback(subreq,
273 : http_send_auth_request_http_req_done,
274 : req);
275 : }
276 :
277 14 : static void http_send_auth_request_http_req_done(struct tevent_req *subreq)
278 : {
279 0 : struct tevent_req *req =
280 14 : tevent_req_callback_data(subreq,
281 : struct tevent_req);
282 0 : struct http_auth_state *state =
283 14 : tevent_req_data(req,
284 : struct http_auth_state);
285 0 : NTSTATUS status;
286 :
287 14 : TALLOC_FREE(state->next_request);
288 :
289 14 : status = http_send_request_recv(subreq);
290 14 : TALLOC_FREE(subreq);
291 14 : if (tevent_req_nterror(req, status)) {
292 14 : return;
293 : }
294 :
295 : /*
296 : * If no more processing required, it is done
297 : *
298 : * The caller will use http_read_response_send/recv
299 : * in order to get the high level response.
300 : */
301 14 : if (NT_STATUS_IS_OK(state->gensec_status)) {
302 14 : tevent_req_done(req);
303 14 : return;
304 : }
305 :
306 : /*
307 : * If more processing required, read the response from server
308 : *
309 : * We may get an empty RPCH Echo packet from the server
310 : * on the "RPC_OUT_DATA" path. We need to consume this
311 : * from the socket, but for now we just ignore the bytes.
312 : */
313 0 : subreq = http_read_response_send(state, state->ev,
314 : state->http_conn,
315 : UINT16_MAX);
316 0 : if (tevent_req_nomem(subreq, req)) {
317 0 : return;
318 : }
319 0 : tevent_req_set_callback(subreq,
320 : http_send_auth_request_http_rep_done,
321 : req);
322 : }
323 :
324 0 : static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
325 : {
326 0 : struct tevent_req *req =
327 0 : tevent_req_callback_data(subreq,
328 : struct tevent_req);
329 0 : struct http_auth_state *state =
330 0 : tevent_req_data(req,
331 : struct http_auth_state);
332 0 : DATA_BLOB gensec_in = data_blob_null;
333 0 : NTSTATUS status;
334 :
335 0 : status = http_read_response_recv(subreq, state,
336 : &state->auth_response);
337 0 : TALLOC_FREE(subreq);
338 0 : if (tevent_req_nterror(req, status)) {
339 0 : return;
340 : }
341 :
342 : /*
343 : * We we asked for up to UINT16_MAX bytes of
344 : * content, we don't expect
345 : * state->auth_response->remaining_content_length
346 : * to be set.
347 : *
348 : * For now we just ignore any bytes in
349 : * state->auth_response->body.
350 : */
351 0 : if (state->auth_response->remaining_content_length != 0) {
352 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
353 0 : return;
354 : }
355 :
356 0 : status = http_parse_auth_response(state->prefix,
357 : state->auth_response,
358 : &gensec_in);
359 0 : if (tevent_req_nterror(req, status)) {
360 0 : return;
361 : }
362 :
363 0 : subreq = gensec_update_send(state, state->ev,
364 : state->gensec_ctx,
365 : gensec_in);
366 0 : if (tevent_req_nomem(subreq, req)) {
367 0 : return;
368 : }
369 0 : tevent_req_set_callback(subreq, http_send_auth_request_gensec_done, req);
370 : }
371 :
372 14 : NTSTATUS http_send_auth_request_recv(struct tevent_req *req)
373 : {
374 0 : NTSTATUS status;
375 :
376 14 : if (tevent_req_is_nterror(req, &status)) {
377 0 : tevent_req_received(req);
378 0 : return status;
379 : }
380 14 : tevent_req_received(req);
381 :
382 14 : return NT_STATUS_OK;
383 : }
|