2 Unix SMB/CIFS implementation.
4 HTTP library - NTLM authentication mechanism gensec module
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 "lib/util/tevent_ntstatus.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "auth/gensec/gensec_internal.h"
28 #include "lib/util/base64.h"
32 _PUBLIC_ NTSTATUS
gensec_http_generic_init(TALLOC_CTX
*);
34 struct gensec_http_generic_state
{
35 struct gensec_security
*sub
;
39 static NTSTATUS
gensec_http_generic_client_start(struct gensec_security
*gensec
,
40 const char *prefix_str
,
44 struct gensec_http_generic_state
*state
;
46 state
= talloc_zero(gensec
, struct gensec_http_generic_state
);
48 return NT_STATUS_NO_MEMORY
;
50 gensec
->private_data
= state
;
52 state
->prefix
= data_blob_string_const(prefix_str
);
54 status
= gensec_subcontext_start(state
, gensec
, &state
->sub
);
55 if (!NT_STATUS_IS_OK(status
)) {
59 return gensec_start_mech_by_oid(state
->sub
, mech_oid
);
62 static NTSTATUS
gensec_http_ntlm_client_start(struct gensec_security
*gensec
)
64 return gensec_http_generic_client_start(gensec
, "NTLM",
68 static NTSTATUS
gensec_http_negotiate_client_start(struct gensec_security
*gensec
)
70 return gensec_http_generic_client_start(gensec
, "Negotiate",
74 struct gensec_http_generic_update_state
{
75 struct gensec_security
*gensec
;
81 static void gensec_http_generic_update_done(struct tevent_req
*subreq
);
83 static struct tevent_req
*gensec_http_generic_update_send(TALLOC_CTX
*mem_ctx
,
84 struct tevent_context
*ev
,
85 struct gensec_security
*gensec_ctx
,
88 struct gensec_http_generic_state
*http_generic
=
89 talloc_get_type_abort(gensec_ctx
->private_data
,
90 struct gensec_http_generic_state
);
91 struct tevent_req
*req
= NULL
;
92 struct gensec_http_generic_update_state
*state
= NULL
;
93 struct tevent_req
*subreq
= NULL
;
95 req
= tevent_req_create(mem_ctx
, &state
,
96 struct gensec_http_generic_update_state
);
100 state
->gensec
= gensec_ctx
;
107 if (in
.length
< http_generic
->prefix
.length
) {
108 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
109 return tevent_req_post(req
, ev
);
112 cmp
= strncasecmp((const char *)in
.data
,
113 (const char *)http_generic
->prefix
.data
,
114 http_generic
->prefix
.length
);
116 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
117 return tevent_req_post(req
, ev
);
120 if (in
.length
== http_generic
->prefix
.length
) {
122 * We expect more data, but the
123 * server just sent the prefix without
124 * a space prefixing base64 data.
126 * It means the server rejects
129 tevent_req_nterror(req
, NT_STATUS_LOGON_FAILURE
);
130 return tevent_req_post(req
, ev
);
133 if (in
.data
[http_generic
->prefix
.length
] != ' ') {
134 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
135 return tevent_req_post(req
, ev
);
137 skip
= http_generic
->prefix
.length
+ 1;
139 b64b
= data_blob_const(in
.data
+ skip
, in
.length
- skip
);
140 if (b64b
.length
!= 0) {
144 * ensure it's terminated with \0' before
145 * passing to base64_decode_data_blob_talloc().
147 b64
= talloc_strndup(state
, (const char *)b64b
.data
,
149 if (tevent_req_nomem(b64
, req
)) {
150 return tevent_req_post(req
, ev
);
153 state
->sub_in
= base64_decode_data_blob_talloc(state
,
156 if (tevent_req_nomem(state
->sub_in
.data
, req
)) {
157 return tevent_req_post(req
, ev
);
162 subreq
= gensec_update_send(state
, ev
,
165 if (tevent_req_nomem(subreq
, req
)) {
166 return tevent_req_post(req
, ev
);
168 tevent_req_set_callback(subreq
, gensec_http_generic_update_done
, req
);
173 static void gensec_http_generic_update_done(struct tevent_req
*subreq
)
175 struct tevent_req
*req
=
176 tevent_req_callback_data(subreq
,
178 struct gensec_http_generic_update_state
*state
=
180 struct gensec_http_generic_update_state
);
181 struct gensec_http_generic_state
*http_generic
=
182 talloc_get_type_abort(state
->gensec
->private_data
,
183 struct gensec_http_generic_state
);
185 DATA_BLOB sub_out
= data_blob_null
;
190 status
= gensec_update_recv(subreq
, state
, &sub_out
);
192 state
->status
= status
;
193 if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
194 status
= NT_STATUS_OK
;
196 if (tevent_req_nterror(req
, status
)) {
200 if (sub_out
.length
== 0) {
201 tevent_req_done(req
);
205 b64
= base64_encode_data_blob(state
, sub_out
);
206 data_blob_free(&sub_out
);
207 if (tevent_req_nomem(b64
, req
)) {
211 prefix_length
= http_generic
->prefix
.length
;
212 str
= talloc_asprintf(state
, "%*.*s %s", prefix_length
, prefix_length
,
213 (const char *)http_generic
->prefix
.data
, b64
);
215 if (tevent_req_nomem(str
, req
)) {
219 state
->out
= data_blob_string_const(str
);
220 tevent_req_done(req
);
223 static NTSTATUS
gensec_http_generic_update_recv(struct tevent_req
*req
,
224 TALLOC_CTX
*out_mem_ctx
,
227 struct gensec_http_generic_update_state
*state
=
229 struct gensec_http_generic_update_state
);
232 *out
= data_blob_null
;
234 if (tevent_req_is_nterror(req
, &status
)) {
235 tevent_req_received(req
);
240 talloc_steal(out_mem_ctx
, state
->out
.data
);
241 status
= state
->status
;
242 tevent_req_received(req
);
246 static const struct gensec_security_ops gensec_http_ntlm_security_ops
= {
249 .client_start
= gensec_http_ntlm_client_start
,
250 .update_send
= gensec_http_generic_update_send
,
251 .update_recv
= gensec_http_generic_update_recv
,
253 .priority
= GENSEC_EXTERNAL
,
256 static const struct gensec_security_ops gensec_http_negotiate_security_ops
= {
257 .name
= "http_negotiate",
259 .client_start
= gensec_http_negotiate_client_start
,
260 .update_send
= gensec_http_generic_update_send
,
261 .update_recv
= gensec_http_generic_update_recv
,
263 .priority
= GENSEC_EXTERNAL
,
267 _PUBLIC_ NTSTATUS
gensec_http_generic_init(TALLOC_CTX
*ctx
)
271 status
= gensec_register(ctx
, &gensec_http_ntlm_security_ops
);
272 if (!NT_STATUS_IS_OK(status
)) {
273 DEBUG(0, ("Failed to register '%s' gensec backend!\n",
274 gensec_http_ntlm_security_ops
.name
));
278 status
= gensec_register(ctx
, &gensec_http_negotiate_security_ops
);
279 if (!NT_STATUS_IS_OK(status
)) {
280 DEBUG(0, ("Failed to register '%s' gensec backend!\n",
281 gensec_http_negotiate_security_ops
.name
));