swrap: Wrap fopen to detect stale file descriptors.
[Samba.git] / source4 / lib / http / http_auth.c
blob2bf4392e8f2d2fc565b265c1cb2917750eac3f9f
1 /*
2 Unix SMB/CIFS implementation.
4 HTTP library
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/>.
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"
32 /**
33 * Copy the request headers from src to dst
35 static NTSTATUS http_copy_header(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;
50 return NT_STATUS_OK;
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,
59 DATA_BLOB *in)
61 struct http_header *h;
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;
71 break;
72 default:
73 break;
78 return NT_STATUS_NOT_SUPPORTED;
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.
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)
94 NTSTATUS status;
95 DATA_BLOB in, out;
97 if (auth_response) {
98 status = http_parse_auth_response(auth, auth_response, &in);
99 } else {
100 in = data_blob_null;
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);
112 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
113 NTSTATUS status2;
115 *auth_request = talloc_zero(mem_ctx, struct http_request);
116 if (*auth_request == NULL) {
117 return NT_STATUS_NO_MEMORY;
120 status2 = http_copy_header(original_request, *auth_request);
121 if (!NT_STATUS_IS_OK(status2)) {
122 talloc_free(*auth_request);
123 return status2;
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);
135 return status;
138 struct http_auth_state
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;
150 int sys_errno;
151 int nwritten;
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)
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;
170 struct http_request *request_to_send;
172 req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
173 if (req == NULL) {
174 return NULL;
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;
185 status = gensec_init();
186 if (!NT_STATUS_IS_OK(status)) {
187 goto post_status;
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;
194 status = gensec_set_credentials(state->gensec_ctx, credentials);
195 if (!NT_STATUS_IS_OK(status)) {
196 goto post_status;
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;
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;
213 break;
214 default:
215 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
216 return tevent_req_post(req, ev);
220 * Store the gensec status to read the server response on callback
221 * if more processing is required
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;
237 * If no more processing is necessary, the http_create_auth_request
238 * function will attach the authentication header to the original
239 * request
241 request_to_send = NT_STATUS_IS_OK(state->gensec_status) ?
242 state->original_request : auth_request;
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);
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);
256 static void http_send_auth_request_done2(struct tevent_req *subreq);
257 static void http_send_auth_request_done(struct tevent_req *subreq)
259 NTSTATUS status;
260 struct tevent_req *req;
261 struct http_auth_state *state;
263 req = tevent_req_callback_data(subreq, struct tevent_req);
264 state = tevent_req_data(req, struct http_auth_state);
266 status = http_send_request_recv(subreq);
267 TALLOC_FREE(subreq);
268 if (tevent_req_nterror(req, status)) {
269 return;
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;
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;
286 tevent_req_set_callback(subreq, http_send_auth_request_done2,
287 req);
288 return;
292 * If gensec status is not NT_STATUS_OK neither
293 * NT_STATUS_MORE_PROCESSING_REQUIRED , it is an error
295 tevent_req_nterror(req, state->gensec_status);
298 static void http_send_auth_request_done2(struct tevent_req *subreq)
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;
305 struct http_request *request_to_send;
307 req = tevent_req_callback_data(subreq, struct tevent_req);
308 state = tevent_req_data(req, struct http_auth_state);
310 status = http_read_response_recv(subreq, state, &auth_response);
311 TALLOC_FREE(subreq);
312 if (tevent_req_nterror(req, status)) {
313 return;
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;
331 * If no more processing is necessary, the http_create_auth_request
332 * function will attach the authentication header to the original
333 * request
335 request_to_send = NT_STATUS_IS_OK(state->gensec_status) ?
336 state->original_request : auth_request;
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;
346 tevent_req_set_callback(subreq, http_send_auth_request_done, req);
350 NTSTATUS http_send_auth_request_recv(struct tevent_req *req)
352 NTSTATUS status;
354 if (tevent_req_is_nterror(req, &status)) {
355 tevent_req_received(req);
356 return status;
358 tevent_req_received(req);
360 return NT_STATUS_OK;