/src/samba/libcli/http/gensec/generic.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | HTTP library - NTLM authentication mechanism gensec module |
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 <tevent.h> |
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" |
29 | | |
30 | | #undef strncasecmp |
31 | | |
32 | | _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *); |
33 | | |
34 | | struct gensec_http_generic_state { |
35 | | struct gensec_security *sub; |
36 | | DATA_BLOB prefix; |
37 | | }; |
38 | | |
39 | | static NTSTATUS gensec_http_generic_client_start(struct gensec_security *gensec, |
40 | | const char *prefix_str, |
41 | | const char *mech_oid) |
42 | 0 | { |
43 | 0 | NTSTATUS status; |
44 | 0 | struct gensec_http_generic_state *state; |
45 | |
|
46 | 0 | state = talloc_zero(gensec, struct gensec_http_generic_state); |
47 | 0 | if (state == NULL) { |
48 | 0 | return NT_STATUS_NO_MEMORY; |
49 | 0 | } |
50 | 0 | gensec->private_data = state; |
51 | |
|
52 | 0 | state->prefix = data_blob_string_const(prefix_str); |
53 | |
|
54 | 0 | status = gensec_subcontext_start(state, gensec, &state->sub); |
55 | 0 | if (!NT_STATUS_IS_OK(status)) { |
56 | 0 | return status; |
57 | 0 | } |
58 | | |
59 | 0 | return gensec_start_mech_by_oid(state->sub, mech_oid); |
60 | 0 | } |
61 | | |
62 | | static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec) |
63 | 0 | { |
64 | 0 | return gensec_http_generic_client_start(gensec, "NTLM", |
65 | 0 | GENSEC_OID_NTLMSSP); |
66 | 0 | } |
67 | | |
68 | | static NTSTATUS gensec_http_negotiate_client_start(struct gensec_security *gensec) |
69 | 0 | { |
70 | 0 | return gensec_http_generic_client_start(gensec, "Negotiate", |
71 | 0 | GENSEC_OID_SPNEGO); |
72 | 0 | } |
73 | | |
74 | | struct gensec_http_generic_update_state { |
75 | | struct gensec_security *gensec; |
76 | | DATA_BLOB sub_in; |
77 | | NTSTATUS status; |
78 | | DATA_BLOB out; |
79 | | }; |
80 | | |
81 | | static void gensec_http_generic_update_done(struct tevent_req *subreq); |
82 | | |
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, |
86 | | const DATA_BLOB in) |
87 | 0 | { |
88 | 0 | struct gensec_http_generic_state *http_generic = |
89 | 0 | talloc_get_type_abort(gensec_ctx->private_data, |
90 | 0 | struct gensec_http_generic_state); |
91 | 0 | struct tevent_req *req = NULL; |
92 | 0 | struct gensec_http_generic_update_state *state = NULL; |
93 | 0 | struct tevent_req *subreq = NULL; |
94 | |
|
95 | 0 | req = tevent_req_create(mem_ctx, &state, |
96 | 0 | struct gensec_http_generic_update_state); |
97 | 0 | if (req == NULL) { |
98 | 0 | return NULL; |
99 | 0 | } |
100 | 0 | state->gensec = gensec_ctx; |
101 | |
|
102 | 0 | if (in.length) { |
103 | 0 | int cmp; |
104 | 0 | DATA_BLOB b64b; |
105 | 0 | size_t skip = 0; |
106 | |
|
107 | 0 | if (in.length < http_generic->prefix.length) { |
108 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
109 | 0 | return tevent_req_post(req, ev); |
110 | 0 | } |
111 | | |
112 | 0 | cmp = strncasecmp((const char *)in.data, |
113 | 0 | (const char *)http_generic->prefix.data, |
114 | 0 | http_generic->prefix.length); |
115 | 0 | if (cmp != 0) { |
116 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
117 | 0 | return tevent_req_post(req, ev); |
118 | 0 | } |
119 | | |
120 | 0 | if (in.length == http_generic->prefix.length) { |
121 | | /* |
122 | | * We expect more data, but the |
123 | | * server just sent the prefix without |
124 | | * a space prefixing base64 data. |
125 | | * |
126 | | * It means the server rejects |
127 | | * the request with. |
128 | | */ |
129 | 0 | tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE); |
130 | 0 | return tevent_req_post(req, ev); |
131 | 0 | } |
132 | | |
133 | 0 | if (in.data[http_generic->prefix.length] != ' ') { |
134 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
135 | 0 | return tevent_req_post(req, ev); |
136 | 0 | } |
137 | 0 | skip = http_generic->prefix.length + 1; |
138 | |
|
139 | 0 | b64b = data_blob_const(in.data + skip, in.length - skip); |
140 | 0 | if (b64b.length != 0) { |
141 | 0 | char *b64 = NULL; |
142 | | |
143 | | /* |
144 | | * ensure it's terminated with \0' before |
145 | | * passing to base64_decode_data_blob_talloc(). |
146 | | */ |
147 | 0 | b64 = talloc_strndup(state, (const char *)b64b.data, |
148 | 0 | b64b.length); |
149 | 0 | if (tevent_req_nomem(b64, req)) { |
150 | 0 | return tevent_req_post(req, ev); |
151 | 0 | } |
152 | | |
153 | 0 | state->sub_in = base64_decode_data_blob_talloc(state, |
154 | 0 | b64); |
155 | 0 | TALLOC_FREE(b64); |
156 | 0 | if (tevent_req_nomem(state->sub_in.data, req)) { |
157 | 0 | return tevent_req_post(req, ev); |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | 0 | subreq = gensec_update_send(state, ev, |
163 | 0 | http_generic->sub, |
164 | 0 | state->sub_in); |
165 | 0 | if (tevent_req_nomem(subreq, req)) { |
166 | 0 | return tevent_req_post(req, ev); |
167 | 0 | } |
168 | 0 | tevent_req_set_callback(subreq, gensec_http_generic_update_done, req); |
169 | |
|
170 | 0 | return req; |
171 | 0 | } |
172 | | |
173 | | static void gensec_http_generic_update_done(struct tevent_req *subreq) |
174 | 0 | { |
175 | 0 | struct tevent_req *req = |
176 | 0 | tevent_req_callback_data(subreq, |
177 | 0 | struct tevent_req); |
178 | 0 | struct gensec_http_generic_update_state *state = |
179 | 0 | tevent_req_data(req, |
180 | 0 | struct gensec_http_generic_update_state); |
181 | 0 | struct gensec_http_generic_state *http_generic = |
182 | 0 | talloc_get_type_abort(state->gensec->private_data, |
183 | 0 | struct gensec_http_generic_state); |
184 | 0 | NTSTATUS status; |
185 | 0 | DATA_BLOB sub_out = data_blob_null; |
186 | 0 | char *b64 = NULL; |
187 | 0 | char *str = NULL; |
188 | 0 | int prefix_length; |
189 | |
|
190 | 0 | status = gensec_update_recv(subreq, state, &sub_out); |
191 | 0 | TALLOC_FREE(subreq); |
192 | 0 | state->status = status; |
193 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
194 | 0 | status = NT_STATUS_OK; |
195 | 0 | } |
196 | 0 | if (tevent_req_nterror(req, status)) { |
197 | 0 | return; |
198 | 0 | } |
199 | | |
200 | 0 | if (sub_out.length == 0) { |
201 | 0 | tevent_req_done(req); |
202 | 0 | return; |
203 | 0 | } |
204 | | |
205 | 0 | b64 = base64_encode_data_blob(state, sub_out); |
206 | 0 | data_blob_free(&sub_out); |
207 | 0 | if (tevent_req_nomem(b64, req)) { |
208 | 0 | return; |
209 | 0 | } |
210 | | |
211 | 0 | prefix_length = http_generic->prefix.length; |
212 | 0 | str = talloc_asprintf(state, "%*.*s %s", prefix_length, prefix_length, |
213 | 0 | (const char *)http_generic->prefix.data, b64); |
214 | 0 | TALLOC_FREE(b64); |
215 | 0 | if (tevent_req_nomem(str, req)) { |
216 | 0 | return; |
217 | 0 | } |
218 | | |
219 | 0 | state->out = data_blob_string_const(str); |
220 | 0 | tevent_req_done(req); |
221 | 0 | } |
222 | | |
223 | | static NTSTATUS gensec_http_generic_update_recv(struct tevent_req *req, |
224 | | TALLOC_CTX *out_mem_ctx, |
225 | | DATA_BLOB *out) |
226 | 0 | { |
227 | 0 | struct gensec_http_generic_update_state *state = |
228 | 0 | tevent_req_data(req, |
229 | 0 | struct gensec_http_generic_update_state); |
230 | 0 | NTSTATUS status; |
231 | |
|
232 | 0 | *out = data_blob_null; |
233 | |
|
234 | 0 | if (tevent_req_is_nterror(req, &status)) { |
235 | 0 | tevent_req_received(req); |
236 | 0 | return status; |
237 | 0 | } |
238 | | |
239 | 0 | *out = state->out; |
240 | 0 | talloc_steal(out_mem_ctx, state->out.data); |
241 | 0 | status = state->status; |
242 | 0 | tevent_req_received(req); |
243 | 0 | return status; |
244 | 0 | } |
245 | | |
246 | | static const struct gensec_security_ops gensec_http_ntlm_security_ops = { |
247 | | .name = "http_ntlm", |
248 | | .auth_type = 0, |
249 | | .client_start = gensec_http_ntlm_client_start, |
250 | | .update_send = gensec_http_generic_update_send, |
251 | | .update_recv = gensec_http_generic_update_recv, |
252 | | .enabled = true, |
253 | | .priority = GENSEC_EXTERNAL, |
254 | | }; |
255 | | |
256 | | static const struct gensec_security_ops gensec_http_negotiate_security_ops = { |
257 | | .name = "http_negotiate", |
258 | | .auth_type = 0, |
259 | | .client_start = gensec_http_negotiate_client_start, |
260 | | .update_send = gensec_http_generic_update_send, |
261 | | .update_recv = gensec_http_generic_update_recv, |
262 | | .enabled = true, |
263 | | .priority = GENSEC_EXTERNAL, |
264 | | .glue = true, |
265 | | }; |
266 | | |
267 | | _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx) |
268 | 0 | { |
269 | 0 | NTSTATUS status; |
270 | |
|
271 | 0 | status = gensec_register(ctx, &gensec_http_ntlm_security_ops); |
272 | 0 | if (!NT_STATUS_IS_OK(status)) { |
273 | 0 | DEBUG(0, ("Failed to register '%s' gensec backend!\n", |
274 | 0 | gensec_http_ntlm_security_ops.name)); |
275 | 0 | return status; |
276 | 0 | } |
277 | | |
278 | 0 | status = gensec_register(ctx, &gensec_http_negotiate_security_ops); |
279 | 0 | if (!NT_STATUS_IS_OK(status)) { |
280 | 0 | DEBUG(0, ("Failed to register '%s' gensec backend!\n", |
281 | 0 | gensec_http_negotiate_security_ops.name)); |
282 | 0 | return status; |
283 | 0 | } |
284 | | |
285 | 0 | return status; |
286 | 0 | } |