d846ec2a0484e2509c5e9d715f57a3536cddcba2
[sfrench/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(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(enum http_auth_method auth,
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                 if (strncasecmp(h->key, "WWW-Authenticate", 16) == 0) {
65                         switch (auth) {
66                         case HTTP_AUTH_NTLM:
67                                 if (strncasecmp(h->value, "NTLM ", 5) == 0) {
68                                         *in = data_blob_string_const(h->value);
69                                         return NT_STATUS_OK;
70                                 }
71                                 break;
72                         default:
73                                 break;
74                         }
75                 }
76         }
77
78         return NT_STATUS_NOT_SUPPORTED;
79 }
80
81 /*
82  * Create the next authentication request to send to server if authentication
83  * is not completed. If it is completed, attachs the 'Authorization' header
84  * to the original request.
85  */
86 static NTSTATUS http_create_auth_request(TALLOC_CTX *mem_ctx,
87                                          struct gensec_security *gensec_ctx,
88                                          struct tevent_context *ev,
89                                          enum http_auth_method auth,
90                                          struct http_request *original_request,
91                                          struct http_request *auth_response,
92                                          struct http_request **auth_request)
93 {
94         NTSTATUS status;
95         DATA_BLOB in, out;
96
97         if (auth_response) {
98                 status = http_parse_auth_response(auth, auth_response, &in);
99         } else {
100                 in = data_blob_null;
101         }
102
103         status = gensec_update_ev(gensec_ctx, mem_ctx, ev, in, &out);
104         if (NT_STATUS_IS_OK(status)) {
105                 if (out.length) {
106                         http_add_header(original_request,
107                                         &original_request->headers,
108                                         "Authorization", (char*)out.data);
109                 }
110         }
111
112         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
113                 NTSTATUS status2;
114
115                 *auth_request = talloc_zero(mem_ctx, struct http_request);
116                 if (*auth_request == NULL) {
117                         return NT_STATUS_NO_MEMORY;
118                 }
119
120                 status2 = http_copy_header(original_request, *auth_request);
121                 if (!NT_STATUS_IS_OK(status2)) {
122                         talloc_free(*auth_request);
123                         return status2;
124                 }
125
126                 http_replace_header(*auth_request, &((*auth_request)->headers),
127                                     "Content-Length", "0");
128                 if (out.length) {
129                         http_add_header(*auth_request,
130                                         &((*auth_request)->headers),
131                                         "Authorization", (char*)out.data);
132                 }
133         }
134
135         return status;
136 }
137
138 struct http_auth_state
139 {
140         struct loadparm_context *lp_ctx;
141         struct tevent_context   *ev;
142         struct tstream_context  *stream;
143         struct tevent_queue     *send_queue;
144         struct cli_credentials  *credentials;
145         struct http_request     *original_request;
146         struct gensec_security  *gensec_ctx;
147         NTSTATUS                gensec_status;
148         enum http_auth_method   auth;
149
150         int                     sys_errno;
151         int                     nwritten;
152 };
153
154
155 static void http_send_auth_request_done(struct tevent_req *);
156 struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
157                                                struct tevent_context *ev,
158                                                struct tstream_context *stream,
159                                                struct tevent_queue *send_queue,
160                                                struct http_request *original_request,
161                                                struct cli_credentials *credentials,
162                                                struct loadparm_context *lp_ctx,
163                                                enum http_auth_method auth)
164 {
165         struct tevent_req *req;
166         struct tevent_req *subreq;
167         struct http_auth_state *state;
168         NTSTATUS status;
169         struct http_request *auth_request = NULL;
170         struct http_request *request_to_send;
171
172         req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
173         if (req == NULL) {
174                 return NULL;
175         }
176
177         state->ev = ev;
178         state->stream = stream;
179         state->send_queue = send_queue;
180         state->original_request = original_request;
181         state->credentials = credentials;
182         state->lp_ctx = lp_ctx;
183         state->auth = auth;
184
185         status = gensec_init();
186         if (!NT_STATUS_IS_OK(status)) {
187                 goto post_status;
188         }
189         status = gensec_client_start(state, &state->gensec_ctx,
190                                      lpcfg_gensec_settings(state, lp_ctx));
191         if (!NT_STATUS_IS_OK(status)) {
192                 goto post_status;
193         }
194         status = gensec_set_credentials(state->gensec_ctx, credentials);
195         if (!NT_STATUS_IS_OK(status)) {
196                 goto post_status;
197         }
198
199         switch (state->auth) {
200         case HTTP_AUTH_BASIC:
201                 status = gensec_start_mech_by_name(state->gensec_ctx,
202                                                    "http_basic");
203                 if (!NT_STATUS_IS_OK(status)) {
204                         goto post_status;
205                 }
206                 break;
207         case HTTP_AUTH_NTLM:
208                 status = gensec_start_mech_by_name(state->gensec_ctx,
209                                                    "http_ntlm");
210                 if (!NT_STATUS_IS_OK(status)) {
211                         goto post_status;
212                 }
213                 break;
214         default:
215                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
216                 return tevent_req_post(req, ev);
217         }
218
219         /*
220          * Store the gensec status to read the server response on callback
221          * if more processing is required
222         */
223         state->gensec_status = http_create_auth_request(state,
224                                                         state->gensec_ctx,
225                                                         state->ev,
226                                                         state->auth,
227                                                         state->original_request,
228                                                         NULL,
229                                                         &auth_request);
230         if (!NT_STATUS_IS_OK(state->gensec_status) &&
231             !NT_STATUS_EQUAL(state->gensec_status,
232                              NT_STATUS_MORE_PROCESSING_REQUIRED)) {
233                 goto post_status;
234         }
235
236         /*
237          * If no more processing is necessary, the http_create_auth_request
238          * function will attach the authentication header to the original
239          * request
240          */
241         request_to_send = NT_STATUS_IS_OK(state->gensec_status) ?
242                                 state->original_request : auth_request;
243
244         subreq = http_send_request_send(state, ev, stream, send_queue,
245                                         request_to_send);
246         if (tevent_req_nomem(subreq, req)) {
247                 return tevent_req_post(req, ev);
248         }
249         tevent_req_set_callback(subreq, http_send_auth_request_done, req);
250         return req;
251 post_status:
252         tevent_req_nterror(req, status);
253         return tevent_req_post(req, ev);
254 }
255
256 static void http_send_auth_request_done2(struct tevent_req *subreq);
257 static void http_send_auth_request_done(struct tevent_req *subreq)
258 {
259         NTSTATUS                status;
260         struct tevent_req       *req;
261         struct http_auth_state  *state;
262
263         req = tevent_req_callback_data(subreq, struct tevent_req);
264         state = tevent_req_data(req, struct http_auth_state);
265
266         status = http_send_request_recv(subreq);
267         TALLOC_FREE(subreq);
268         if (tevent_req_nterror(req, status)) {
269                 return;
270         }
271
272         /* If no more processing required, it is done */
273         if (NT_STATUS_IS_OK(state->gensec_status)) {
274                 tevent_req_done(req);
275                 return;
276         }
277
278         /* If more processing required, read the response from server */
279         if (NT_STATUS_EQUAL(state->gensec_status,
280                             NT_STATUS_MORE_PROCESSING_REQUIRED)) {
281                 subreq = http_read_response_send(state, state->ev,
282                                                  state->stream);
283                 if (tevent_req_nomem(subreq, req)) {
284                         return;
285                 }
286                 tevent_req_set_callback(subreq, http_send_auth_request_done2,
287                                         req);
288                 return;
289         }
290
291         /*
292          * If gensec status is not NT_STATUS_OK neither
293          * NT_STATUS_MORE_PROCESSING_REQUIRED , it is an error
294          */
295         tevent_req_nterror(req, state->gensec_status);
296 }
297
298 static void http_send_auth_request_done2(struct tevent_req *subreq)
299 {
300         NTSTATUS status;
301         struct tevent_req       *req;
302         struct http_auth_state  *state;
303         struct http_request *auth_response;
304         struct http_request *auth_request = NULL;
305         struct http_request *request_to_send;
306
307         req = tevent_req_callback_data(subreq, struct tevent_req);
308         state = tevent_req_data(req, struct http_auth_state);
309
310         status = http_read_response_recv(subreq, state, &auth_response);
311         TALLOC_FREE(subreq);
312         if (tevent_req_nterror(req, status)) {
313                 return;
314         }
315
316         state->gensec_status = http_create_auth_request(state,
317                                                         state->gensec_ctx,
318                                                         state->ev,
319                                                         state->auth,
320                                                         state->original_request,
321                                                         auth_response,
322                                                         &auth_request);
323         if (!NT_STATUS_IS_OK(state->gensec_status) &&
324             !NT_STATUS_EQUAL(state->gensec_status,
325                              NT_STATUS_MORE_PROCESSING_REQUIRED)) {
326                 tevent_req_nterror(req, status);
327                 return;
328         }
329
330         /*
331          * If no more processing is necessary, the http_create_auth_request
332          * function will attach the authentication header to the original
333          * request
334          */
335         request_to_send = NT_STATUS_IS_OK(state->gensec_status) ?
336                                 state->original_request : auth_request;
337
338         subreq = http_send_request_send(state,
339                                         state->ev,
340                                         state->stream,
341                                         state->send_queue,
342                                         request_to_send);
343         if (tevent_req_nomem(subreq, req)) {
344                 return;
345         }
346         tevent_req_set_callback(subreq, http_send_auth_request_done, req);
347 }
348
349
350 NTSTATUS http_send_auth_request_recv(struct tevent_req *req)
351 {
352         NTSTATUS status;
353
354         if (tevent_req_is_nterror(req, &status)) {
355                 tevent_req_received(req);
356                 return status;
357         }
358         tevent_req_received(req);
359
360         return NT_STATUS_OK;
361 }