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