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