s3:tldap: simplify tldap_gensec_bind.h
[samba.git] / source3 / lib / tldap_gensec_bind.c
blob4472eb1c60541801b80ebd427fd9fbaecb62a14d
1 /*
2 * Unix SMB/CIFS implementation.
3 * Gensec based tldap auth
4 * Copyright (C) Volker Lendecke 2015
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.
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.
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/>.
20 #include "replace.h"
21 #include "tldap.h"
22 #include "tldap_util.h"
23 #include "tldap_gensec_bind.h"
24 #include "auth/credentials/credentials.h"
25 #include "lib/util/tevent_unix.h"
26 #include "lib/util/talloc_stack.h"
27 #include "lib/util/samba_util.h"
28 #include "lib/util/debug.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/gensec/gensec_internal.h" /* TODO: remove this */
31 #include "lib/param/param.h"
32 #include "source4/auth/gensec/gensec_tstream.h"
34 struct tldap_gensec_bind_state {
35 struct tevent_context *ev;
36 struct tldap_context *ctx;
37 struct cli_credentials *creds;
38 const char *target_service;
39 const char *target_hostname;
40 const char *target_principal;
41 struct loadparm_context *lp_ctx;
42 uint32_t gensec_features;
44 bool first;
45 struct gensec_security *gensec;
46 NTSTATUS gensec_status;
47 DATA_BLOB gensec_output;
50 static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq);
51 static void tldap_gensec_update_done(struct tldap_gensec_bind_state *state,
52 struct tevent_req *subreq);
53 static void tldap_gensec_bind_done(struct tevent_req *subreq);
55 static struct tevent_req *tldap_gensec_bind_send(
56 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
57 struct tldap_context *ctx, struct cli_credentials *creds,
58 const char *target_service, const char *target_hostname,
59 const char *target_principal, struct loadparm_context *lp_ctx,
60 uint32_t gensec_features)
62 struct tevent_req *req, *subreq;
63 struct tldap_gensec_bind_state *state;
65 const char *attrs[] = { "supportedSASLMechanisms" };
67 req = tevent_req_create(mem_ctx, &state,
68 struct tldap_gensec_bind_state);
69 if (req == NULL) {
70 return NULL;
72 state->ev = ev;
73 state->ctx = ctx;
74 state->creds = creds;
75 state->target_service = target_service;
76 state->target_hostname = target_hostname;
77 state->target_principal = target_principal;
78 state->lp_ctx = lp_ctx;
79 state->gensec_features = gensec_features;
80 state->first = true;
82 subreq = tldap_search_all_send(
83 state, state->ev, state->ctx, "", TLDAP_SCOPE_BASE,
84 "(objectclass=*)", attrs, ARRAY_SIZE(attrs),
85 false, NULL, 0, NULL, 0, 0, 1 /* sizelimit */, 0);
86 if (tevent_req_nomem(subreq, req)) {
87 return tevent_req_post(req, ev);
89 tevent_req_set_callback(subreq, tldap_gensec_bind_got_mechs, req);
90 return req;
93 static void tldap_gensec_bind_got_mechs(struct tevent_req *subreq)
95 struct tevent_req *req = tevent_req_callback_data(
96 subreq, struct tevent_req);
97 struct tldap_gensec_bind_state *state = tevent_req_data(
98 req, struct tldap_gensec_bind_state);
99 struct tldap_message **msgs, *msg, *result;
100 struct tldap_attribute *attribs, *attrib;
101 int num_attribs;
102 size_t num_msgs;
103 TLDAPRC rc;
104 int i;
105 bool ok;
106 const char **sasl_mechs;
107 NTSTATUS status;
109 rc = tldap_search_all_recv(subreq, state, &msgs, &result);
110 TALLOC_FREE(subreq);
111 if (tevent_req_ldap_error(req, rc)) {
112 return;
116 * TODO: Inspect "Result"
119 num_msgs = talloc_array_length(msgs);
120 if (num_msgs != 1) {
121 DBG_DEBUG("num_msgs = %zu\n", num_msgs);
122 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
123 return;
125 msg = msgs[0];
127 ok = tldap_entry_attributes(msg, &attribs, &num_attribs);
128 if (!ok) {
129 DBG_DEBUG("tldap_entry_attributes failed\n");
130 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
131 return;
134 if (num_attribs != 1) {
135 DBG_DEBUG("num_attribs = %d\n", num_attribs);
136 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
137 return;
139 attrib = &attribs[0];
141 sasl_mechs = talloc_array(state, const char *, attrib->num_values+1);
142 if (tevent_req_nomem(sasl_mechs, req)) {
143 return;
146 for (i=0; i<attrib->num_values; i++) {
147 DATA_BLOB *v = &attrib->values[i];
148 size_t len;
150 ok = convert_string_talloc(sasl_mechs, CH_UTF8, CH_UNIX,
151 v->data, v->length,
152 &sasl_mechs[i], &len);
153 if (!ok) {
154 DBG_DEBUG("convert_string_talloc failed\n");
155 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
156 return;
159 sasl_mechs[attrib->num_values] = NULL;
161 gensec_init();
163 status = gensec_client_start(
164 state, &state->gensec,
165 lpcfg_gensec_settings(state, state->lp_ctx));
166 if (!NT_STATUS_IS_OK(status)) {
167 DBG_DEBUG("gensec_client_start failed: %s\n",
168 nt_errstr(status));
169 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
170 return;
173 status = gensec_set_credentials(state->gensec, state->creds);
174 if (!NT_STATUS_IS_OK(status)) {
175 DBG_DEBUG("gensec_set_credentials failed: %s\n",
176 nt_errstr(status));
177 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
178 return;
181 status = gensec_set_target_service(state->gensec,
182 state->target_service);
183 if (!NT_STATUS_IS_OK(status)) {
184 DBG_DEBUG("gensec_set_target_service failed: %s\n",
185 nt_errstr(status));
186 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
187 return;
190 if (state->target_hostname != NULL) {
191 status = gensec_set_target_hostname(state->gensec,
192 state->target_hostname);
193 if (!NT_STATUS_IS_OK(status)) {
194 DBG_DEBUG("gensec_set_target_hostname failed: %s\n",
195 nt_errstr(status));
196 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
197 return;
201 if (state->target_principal != NULL) {
202 status = gensec_set_target_principal(state->gensec,
203 state->target_principal);
204 if (!NT_STATUS_IS_OK(status)) {
205 DBG_DEBUG("gensec_set_target_principal failed: %s\n",
206 nt_errstr(status));
207 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
208 return;
212 gensec_want_feature(state->gensec, state->gensec_features);
214 status = gensec_start_mech_by_sasl_list(state->gensec, sasl_mechs);
215 if (!NT_STATUS_IS_OK(status)) {
216 DBG_DEBUG("gensec_start_mech_by_sasl_list failed: %s\n",
217 nt_errstr(status));
218 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
219 return;
222 state->gensec_status = gensec_update(state->gensec, state,
223 data_blob_null,
224 &state->gensec_output);
225 tldap_gensec_update_done(state, req);
228 static void tldap_gensec_update_done(struct tldap_gensec_bind_state *state,
229 struct tevent_req *req)
231 struct tevent_req *subreq;
233 if (!NT_STATUS_IS_OK(state->gensec_status) &&
234 !NT_STATUS_EQUAL(state->gensec_status,
235 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
236 DBG_DEBUG("gensec_update failed: %s\n",
237 nt_errstr(state->gensec_status));
238 tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
239 return;
242 if (NT_STATUS_IS_OK(state->gensec_status) &&
243 (state->gensec_output.length == 0)) {
245 if (state->first) {
246 tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
247 } else {
248 tevent_req_done(req);
250 return;
253 state->first = false;
255 subreq = tldap_sasl_bind_send(
256 state, state->ev, state->ctx, "",
257 state->gensec->ops->sasl_name, &state->gensec_output,
258 NULL, 0, NULL, 0);
259 if (tevent_req_nomem(subreq, req)) {
260 return;
262 tevent_req_set_callback(subreq, tldap_gensec_bind_done, req);
265 static void tldap_gensec_bind_done(struct tevent_req *subreq)
267 struct tevent_req *req = tevent_req_callback_data(
268 subreq, struct tevent_req);
269 struct tldap_gensec_bind_state *state = tevent_req_data(
270 req, struct tldap_gensec_bind_state);
271 DATA_BLOB input;
272 TLDAPRC rc;
274 rc = tldap_sasl_bind_recv(subreq, state, &input);
275 TALLOC_FREE(subreq);
276 if (!TLDAP_RC_IS_SUCCESS(rc) &&
277 !TLDAP_RC_EQUAL(rc, TLDAP_SASL_BIND_IN_PROGRESS)) {
278 tevent_req_ldap_error(req, rc);
279 return;
282 if (TLDAP_RC_IS_SUCCESS(rc) && NT_STATUS_IS_OK(state->gensec_status)) {
283 tevent_req_done(req);
284 return;
287 state->gensec_status = gensec_update(state->gensec, state,
288 input,
289 &state->gensec_output);
290 tldap_gensec_update_done(state, req);
293 static TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req)
295 struct tldap_gensec_bind_state *state = tevent_req_data(
296 req, struct tldap_gensec_bind_state);
297 struct tstream_context *plain, *sec;
298 NTSTATUS status;
299 TLDAPRC rc;
301 if (tevent_req_is_ldap_error(req, &rc)) {
302 return rc;
305 if ((state->gensec_features & GENSEC_FEATURE_SIGN) &&
306 !gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN)) {
307 return TLDAP_OPERATIONS_ERROR;
309 if ((state->gensec_features & GENSEC_FEATURE_SEAL) &&
310 !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) {
311 return TLDAP_OPERATIONS_ERROR;
314 if (!gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN) &&
315 !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) {
316 return TLDAP_SUCCESS;
320 * The gensec ctx needs to survive as long as the ldap context
321 * lives
323 talloc_steal(state->ctx, state->gensec);
325 plain = tldap_get_tstream(state->ctx);
327 status = gensec_create_tstream(state->ctx, state->gensec,
328 plain, &sec);
329 if (!NT_STATUS_IS_OK(status)) {
330 DBG_DEBUG("gensec_create_tstream failed: %s\n",
331 nt_errstr(status));
332 return TLDAP_OPERATIONS_ERROR;
335 tldap_set_tstream(state->ctx, sec);
337 return TLDAP_SUCCESS;
340 TLDAPRC tldap_gensec_bind(
341 struct tldap_context *ctx, struct cli_credentials *creds,
342 const char *target_service, const char *target_hostname,
343 const char *target_principal, struct loadparm_context *lp_ctx,
344 uint32_t gensec_features)
346 TALLOC_CTX *frame = talloc_stackframe();
347 struct tevent_context *ev;
348 struct tevent_req *req;
349 TLDAPRC rc = TLDAP_NO_MEMORY;
351 ev = samba_tevent_context_init(frame);
352 if (ev == NULL) {
353 goto fail;
355 req = tldap_gensec_bind_send(frame, ev, ctx, creds, target_service,
356 target_hostname, target_principal, lp_ctx,
357 gensec_features);
358 if (req == NULL) {
359 goto fail;
361 if (!tevent_req_poll(req, ev)) {
362 rc = TLDAP_OPERATIONS_ERROR;
363 goto fail;
365 rc = tldap_gensec_bind_recv(req);
366 fail:
367 TALLOC_FREE(frame);
368 return rc;