/src/samba/source3/lib/tldap_gensec_bind.c
Line | Count | Source |
1 | | /* |
2 | | * Unix SMB/CIFS implementation. |
3 | | * Gensec based tldap auth |
4 | | * Copyright (C) Volker Lendecke 2015 |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "replace.h" |
21 | | #include "tldap.h" |
22 | | #include "tldap_gensec_bind.h" |
23 | | #include "auth/credentials/credentials.h" |
24 | | #include "lib/util/tevent_unix.h" |
25 | | #include "lib/util/talloc_stack.h" |
26 | | #include "lib/util/samba_util.h" |
27 | | #include "lib/util/debug.h" |
28 | | #include "auth/gensec/gensec.h" |
29 | | #include "lib/param/param.h" |
30 | | #include "source4/auth/gensec/gensec_tstream.h" |
31 | | |
32 | | struct tldap_gensec_bind_state { |
33 | | struct tevent_context *ev; |
34 | | struct tldap_context *ctx; |
35 | | struct cli_credentials *creds; |
36 | | const char *target_service; |
37 | | const char *target_hostname; |
38 | | const char *target_principal; |
39 | | struct loadparm_context *lp_ctx; |
40 | | uint32_t gensec_features; |
41 | | |
42 | | bool first; |
43 | | struct gensec_security *gensec; |
44 | | NTSTATUS gensec_status; |
45 | | DATA_BLOB gensec_input; |
46 | | DATA_BLOB gensec_output; |
47 | | }; |
48 | | |
49 | | static void tldap_gensec_update_next(struct tevent_req *req); |
50 | | static void tldap_gensec_update_done(struct tevent_req *subreq); |
51 | | static void tldap_gensec_bind_done(struct tevent_req *subreq); |
52 | | |
53 | | struct tevent_req *tldap_gensec_bind_send( |
54 | | TALLOC_CTX *mem_ctx, struct tevent_context *ev, |
55 | | struct tldap_context *ctx, struct cli_credentials *creds, |
56 | | const char *target_service, const char *target_hostname, |
57 | | const char *target_principal, struct loadparm_context *lp_ctx, |
58 | | uint32_t gensec_features) |
59 | 0 | { |
60 | 0 | struct tevent_req *req = NULL; |
61 | 0 | struct tldap_gensec_bind_state *state = NULL; |
62 | 0 | const DATA_BLOB *tls_cb = NULL; |
63 | 0 | NTSTATUS status; |
64 | |
|
65 | 0 | req = tevent_req_create(mem_ctx, &state, |
66 | 0 | struct tldap_gensec_bind_state); |
67 | 0 | if (req == NULL) { |
68 | 0 | return NULL; |
69 | 0 | } |
70 | 0 | state->ev = ev; |
71 | 0 | state->ctx = ctx; |
72 | 0 | state->creds = creds; |
73 | 0 | state->target_service = target_service; |
74 | 0 | state->target_hostname = target_hostname; |
75 | 0 | state->target_principal = target_principal; |
76 | 0 | state->lp_ctx = lp_ctx; |
77 | 0 | state->gensec_features = gensec_features; |
78 | 0 | state->first = true; |
79 | |
|
80 | 0 | gensec_init(); |
81 | |
|
82 | 0 | status = gensec_client_start( |
83 | 0 | state, &state->gensec, |
84 | 0 | lpcfg_gensec_settings(state, state->lp_ctx)); |
85 | 0 | if (!NT_STATUS_IS_OK(status)) { |
86 | 0 | DBG_DEBUG("gensec_client_start failed: %s\n", |
87 | 0 | nt_errstr(status)); |
88 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
89 | 0 | return tevent_req_post(req, ev); |
90 | 0 | } |
91 | | |
92 | 0 | status = gensec_set_credentials(state->gensec, state->creds); |
93 | 0 | if (!NT_STATUS_IS_OK(status)) { |
94 | 0 | DBG_DEBUG("gensec_set_credentials failed: %s\n", |
95 | 0 | nt_errstr(status)); |
96 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
97 | 0 | return tevent_req_post(req, ev); |
98 | 0 | } |
99 | | |
100 | 0 | status = gensec_set_target_service(state->gensec, |
101 | 0 | state->target_service); |
102 | 0 | if (!NT_STATUS_IS_OK(status)) { |
103 | 0 | DBG_DEBUG("gensec_set_target_service failed: %s\n", |
104 | 0 | nt_errstr(status)); |
105 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
106 | 0 | return tevent_req_post(req, ev); |
107 | 0 | } |
108 | | |
109 | 0 | if (state->target_hostname != NULL) { |
110 | 0 | status = gensec_set_target_hostname(state->gensec, |
111 | 0 | state->target_hostname); |
112 | 0 | if (!NT_STATUS_IS_OK(status)) { |
113 | 0 | DBG_DEBUG("gensec_set_target_hostname failed: %s\n", |
114 | 0 | nt_errstr(status)); |
115 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
116 | 0 | return tevent_req_post(req, ev); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | 0 | if (state->target_principal != NULL) { |
121 | 0 | status = gensec_set_target_principal(state->gensec, |
122 | 0 | state->target_principal); |
123 | 0 | if (!NT_STATUS_IS_OK(status)) { |
124 | 0 | DBG_DEBUG("gensec_set_target_principal failed: %s\n", |
125 | 0 | nt_errstr(status)); |
126 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
127 | 0 | return tevent_req_post(req, ev); |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | 0 | if (tldap_has_tls_tstream(state->ctx)) { |
132 | 0 | if (gensec_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { |
133 | 0 | DBG_WARNING("sign or seal not allowed over tls\n"); |
134 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
135 | 0 | return tevent_req_post(req, ev); |
136 | 0 | } |
137 | | |
138 | 0 | tls_cb = tldap_tls_channel_bindings(state->ctx); |
139 | 0 | } |
140 | | |
141 | 0 | if (tls_cb != NULL) { |
142 | 0 | uint32_t initiator_addrtype = 0; |
143 | 0 | const DATA_BLOB *initiator_address = NULL; |
144 | 0 | uint32_t acceptor_addrtype = 0; |
145 | 0 | const DATA_BLOB *acceptor_address = NULL; |
146 | 0 | const DATA_BLOB *application_data = tls_cb; |
147 | |
|
148 | 0 | status = gensec_set_channel_bindings(state->gensec, |
149 | 0 | initiator_addrtype, |
150 | 0 | initiator_address, |
151 | 0 | acceptor_addrtype, |
152 | 0 | acceptor_address, |
153 | 0 | application_data); |
154 | 0 | if (!NT_STATUS_IS_OK(status)) { |
155 | 0 | DBG_DEBUG("gensec_set_channel_bindings: %s\n", |
156 | 0 | nt_errstr(status)); |
157 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
158 | 0 | return tevent_req_post(req, ev); |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | 0 | gensec_want_feature(state->gensec, state->gensec_features); |
163 | |
|
164 | 0 | status = gensec_start_mech_by_sasl_name(state->gensec, "GSS-SPNEGO"); |
165 | 0 | if (!NT_STATUS_IS_OK(status)) { |
166 | 0 | DBG_ERR("gensec_start_mech_by_sasl_name(GSS-SPNEGO) failed: %s\n", |
167 | 0 | nt_errstr(status)); |
168 | 0 | tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); |
169 | 0 | return tevent_req_post(req, ev); |
170 | 0 | } |
171 | | |
172 | 0 | tldap_gensec_update_next(req); |
173 | 0 | if (!tevent_req_is_in_progress(req)) { |
174 | 0 | return tevent_req_post(req, ev); |
175 | 0 | } |
176 | | |
177 | 0 | return req; |
178 | 0 | } |
179 | | |
180 | | static void tldap_gensec_update_next(struct tevent_req *req) |
181 | 0 | { |
182 | 0 | struct tldap_gensec_bind_state *state = tevent_req_data( |
183 | 0 | req, struct tldap_gensec_bind_state); |
184 | 0 | struct tevent_req *subreq = NULL; |
185 | |
|
186 | 0 | subreq = gensec_update_send(state, |
187 | 0 | state->ev, |
188 | 0 | state->gensec, |
189 | 0 | state->gensec_input); |
190 | 0 | if (tevent_req_nomem(subreq, req)) { |
191 | 0 | return; |
192 | 0 | } |
193 | 0 | tevent_req_set_callback(subreq, |
194 | 0 | tldap_gensec_update_done, |
195 | 0 | req); |
196 | 0 | } |
197 | | |
198 | | static void tldap_gensec_update_done(struct tevent_req *subreq) |
199 | 0 | { |
200 | 0 | struct tevent_req *req = tevent_req_callback_data( |
201 | 0 | subreq, struct tevent_req); |
202 | 0 | struct tldap_gensec_bind_state *state = tevent_req_data( |
203 | 0 | req, struct tldap_gensec_bind_state); |
204 | |
|
205 | 0 | state->gensec_status = gensec_update_recv(subreq, |
206 | 0 | state, |
207 | 0 | &state->gensec_output); |
208 | 0 | TALLOC_FREE(subreq); |
209 | 0 | data_blob_free(&state->gensec_input); |
210 | 0 | if (!NT_STATUS_IS_OK(state->gensec_status) && |
211 | 0 | !NT_STATUS_EQUAL(state->gensec_status, |
212 | 0 | NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
213 | 0 | DBG_DEBUG("gensec_update failed: %s\n", |
214 | 0 | nt_errstr(state->gensec_status)); |
215 | 0 | tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS); |
216 | 0 | return; |
217 | 0 | } |
218 | | |
219 | 0 | if (NT_STATUS_IS_OK(state->gensec_status) && |
220 | 0 | (state->gensec_output.length == 0)) { |
221 | |
|
222 | 0 | if (state->first) { |
223 | 0 | tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS); |
224 | 0 | } else { |
225 | 0 | tevent_req_done(req); |
226 | 0 | } |
227 | 0 | return; |
228 | 0 | } |
229 | | |
230 | 0 | state->first = false; |
231 | |
|
232 | 0 | subreq = tldap_sasl_bind_send(state, |
233 | 0 | state->ev, |
234 | 0 | state->ctx, |
235 | 0 | "", |
236 | 0 | "GSS-SPNEGO", |
237 | 0 | &state->gensec_output, |
238 | 0 | NULL, |
239 | 0 | 0, |
240 | 0 | NULL, |
241 | 0 | 0); |
242 | 0 | if (tevent_req_nomem(subreq, req)) { |
243 | 0 | return; |
244 | 0 | } |
245 | 0 | tevent_req_set_callback(subreq, tldap_gensec_bind_done, req); |
246 | 0 | } |
247 | | |
248 | | static void tldap_gensec_bind_done(struct tevent_req *subreq) |
249 | 0 | { |
250 | 0 | struct tevent_req *req = tevent_req_callback_data( |
251 | 0 | subreq, struct tevent_req); |
252 | 0 | struct tldap_gensec_bind_state *state = tevent_req_data( |
253 | 0 | req, struct tldap_gensec_bind_state); |
254 | 0 | TLDAPRC rc; |
255 | |
|
256 | 0 | rc = tldap_sasl_bind_recv(subreq, state, &state->gensec_input); |
257 | 0 | TALLOC_FREE(subreq); |
258 | 0 | data_blob_free(&state->gensec_output); |
259 | 0 | if (!TLDAP_RC_IS_SUCCESS(rc) && |
260 | 0 | !TLDAP_RC_EQUAL(rc, TLDAP_SASL_BIND_IN_PROGRESS)) { |
261 | 0 | tevent_req_ldap_error(req, rc); |
262 | 0 | return; |
263 | 0 | } |
264 | | |
265 | 0 | if (TLDAP_RC_IS_SUCCESS(rc) && NT_STATUS_IS_OK(state->gensec_status)) { |
266 | 0 | tevent_req_done(req); |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | 0 | tldap_gensec_update_next(req); |
271 | 0 | } |
272 | | |
273 | | TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req) |
274 | 0 | { |
275 | 0 | struct tldap_gensec_bind_state *state = tevent_req_data( |
276 | 0 | req, struct tldap_gensec_bind_state); |
277 | 0 | struct tstream_context *plain, *sec; |
278 | 0 | NTSTATUS status; |
279 | 0 | TLDAPRC rc; |
280 | |
|
281 | 0 | if (tevent_req_is_ldap_error(req, &rc)) { |
282 | 0 | return rc; |
283 | 0 | } |
284 | | |
285 | 0 | if ((state->gensec_features & GENSEC_FEATURE_SIGN) && |
286 | 0 | !gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN)) { |
287 | 0 | return TLDAP_OPERATIONS_ERROR; |
288 | 0 | } |
289 | 0 | if ((state->gensec_features & GENSEC_FEATURE_SEAL) && |
290 | 0 | !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) { |
291 | 0 | return TLDAP_OPERATIONS_ERROR; |
292 | 0 | } |
293 | | |
294 | 0 | if (!gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN) && |
295 | 0 | !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) { |
296 | 0 | return TLDAP_SUCCESS; |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * The gensec ctx needs to survive as long as the ldap context |
301 | | * lives |
302 | | */ |
303 | 0 | talloc_steal(state->ctx, state->gensec); |
304 | |
|
305 | 0 | plain = tldap_get_plain_tstream(state->ctx); |
306 | |
|
307 | 0 | status = gensec_create_tstream(state->ctx, state->gensec, |
308 | 0 | plain, &sec); |
309 | 0 | if (!NT_STATUS_IS_OK(status)) { |
310 | 0 | DBG_DEBUG("gensec_create_tstream failed: %s\n", |
311 | 0 | nt_errstr(status)); |
312 | 0 | return TLDAP_OPERATIONS_ERROR; |
313 | 0 | } |
314 | | |
315 | 0 | tldap_set_gensec_tstream(state->ctx, &sec); |
316 | |
|
317 | 0 | return TLDAP_SUCCESS; |
318 | 0 | } |
319 | | |
320 | | TLDAPRC tldap_gensec_bind( |
321 | | struct tldap_context *ctx, struct cli_credentials *creds, |
322 | | const char *target_service, const char *target_hostname, |
323 | | const char *target_principal, struct loadparm_context *lp_ctx, |
324 | | uint32_t gensec_features) |
325 | 0 | { |
326 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
327 | 0 | struct tevent_context *ev; |
328 | 0 | struct tevent_req *req; |
329 | 0 | TLDAPRC rc = TLDAP_NO_MEMORY; |
330 | |
|
331 | 0 | ev = samba_tevent_context_init(frame); |
332 | 0 | if (ev == NULL) { |
333 | 0 | goto fail; |
334 | 0 | } |
335 | 0 | req = tldap_gensec_bind_send(frame, ev, ctx, creds, target_service, |
336 | 0 | target_hostname, target_principal, lp_ctx, |
337 | 0 | gensec_features); |
338 | 0 | if (req == NULL) { |
339 | 0 | goto fail; |
340 | 0 | } |
341 | 0 | if (!tevent_req_poll(req, ev)) { |
342 | 0 | rc = TLDAP_OPERATIONS_ERROR; |
343 | 0 | goto fail; |
344 | 0 | } |
345 | 0 | rc = tldap_gensec_bind_recv(req); |
346 | 0 | fail: |
347 | | TALLOC_FREE(frame); |
348 | 0 | return rc; |
349 | 0 | } |