2 Unix SMB/CIFS implementation.
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/>.
24 #include "http_internal.h"
25 #include "lib/util/tevent_ntstatus.h"
26 #include "lib/param/param.h"
28 #include "auth/gensec/gensec.h"
29 #include "auth/credentials/credentials.h"
30 #include "lib/util/data_blob.h"
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
;
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
,
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) {
67 if (strncasecmp(h
->value
, "NTLM ", 5) == 0) {
68 *in
= data_blob_string_const(h
->value
);
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
)
98 status
= http_parse_auth_response(auth
, auth_response
, &in
);
103 status
= gensec_update_ev(gensec_ctx
, mem_ctx
, ev
, in
, &out
);
104 if (NT_STATUS_IS_OK(status
)) {
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
)) {
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
);
126 http_replace_header(*auth_request
, &((*auth_request
)->headers
),
127 "Content-Length", "0");
129 http_add_header(*auth_request
,
130 &((*auth_request
)->headers
),
131 "Authorization", (char*)out
.data
);
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
;
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
;
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
);
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
;
185 status
= gensec_init();
186 if (!NT_STATUS_IS_OK(status
)) {
189 status
= gensec_client_start(state
, &state
->gensec_ctx
,
190 lpcfg_gensec_settings(state
, lp_ctx
));
191 if (!NT_STATUS_IS_OK(status
)) {
194 status
= gensec_set_credentials(state
->gensec_ctx
, credentials
);
195 if (!NT_STATUS_IS_OK(status
)) {
199 switch (state
->auth
) {
200 case HTTP_AUTH_BASIC
:
201 status
= gensec_start_mech_by_name(state
->gensec_ctx
,
203 if (!NT_STATUS_IS_OK(status
)) {
208 status
= gensec_start_mech_by_name(state
->gensec_ctx
,
210 if (!NT_STATUS_IS_OK(status
)) {
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
,
227 state
->original_request
,
230 if (!NT_STATUS_IS_OK(state
->gensec_status
) &&
231 !NT_STATUS_EQUAL(state
->gensec_status
,
232 NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
237 * If no more processing is necessary, the http_create_auth_request
238 * function will attach the authentication header to the original
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
,
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
);
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
)
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
);
268 if (tevent_req_nterror(req
, status
)) {
272 /* If no more processing required, it is done */
273 if (NT_STATUS_IS_OK(state
->gensec_status
)) {
274 tevent_req_done(req
);
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
,
283 if (tevent_req_nomem(subreq
, req
)) {
286 tevent_req_set_callback(subreq
, http_send_auth_request_done2
,
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
)
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
);
312 if (tevent_req_nterror(req
, status
)) {
316 state
->gensec_status
= http_create_auth_request(state
,
320 state
->original_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
);
331 * If no more processing is necessary, the http_create_auth_request
332 * function will attach the authentication header to the original
335 request_to_send
= NT_STATUS_IS_OK(state
->gensec_status
) ?
336 state
->original_request
: auth_request
;
338 subreq
= http_send_request_send(state
,
343 if (tevent_req_nomem(subreq
, req
)) {
346 tevent_req_set_callback(subreq
, http_send_auth_request_done
, req
);
350 NTSTATUS
http_send_auth_request_recv(struct tevent_req
*req
)
354 if (tevent_req_is_nterror(req
, &status
)) {
355 tevent_req_received(req
);
358 tevent_req_received(req
);