s3:tldap: let tldap_gensec_bind_send/recv use gensec_update_send/recv
[samba.git] / source3 / lib / tldap_gensec_bind.c
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 static 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 {
60         struct tevent_req *req = NULL;
61         struct tldap_gensec_bind_state *state = NULL;
62         NTSTATUS status;
63
64         req = tevent_req_create(mem_ctx, &state,
65                                 struct tldap_gensec_bind_state);
66         if (req == NULL) {
67                 return NULL;
68         }
69         state->ev = ev;
70         state->ctx = ctx;
71         state->creds = creds;
72         state->target_service = target_service;
73         state->target_hostname = target_hostname;
74         state->target_principal = target_principal;
75         state->lp_ctx = lp_ctx;
76         state->gensec_features = gensec_features;
77         state->first = true;
78
79         gensec_init();
80
81         status = gensec_client_start(
82                 state, &state->gensec,
83                 lpcfg_gensec_settings(state, state->lp_ctx));
84         if (!NT_STATUS_IS_OK(status)) {
85                 DBG_DEBUG("gensec_client_start failed: %s\n",
86                           nt_errstr(status));
87                 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
88                 return tevent_req_post(req, ev);
89         }
90
91         status = gensec_set_credentials(state->gensec, state->creds);
92         if (!NT_STATUS_IS_OK(status)) {
93                 DBG_DEBUG("gensec_set_credentials failed: %s\n",
94                           nt_errstr(status));
95                 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
96                 return tevent_req_post(req, ev);
97         }
98
99         status = gensec_set_target_service(state->gensec,
100                                            state->target_service);
101         if (!NT_STATUS_IS_OK(status)) {
102                 DBG_DEBUG("gensec_set_target_service failed: %s\n",
103                           nt_errstr(status));
104                 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
105                 return tevent_req_post(req, ev);
106         }
107
108         if (state->target_hostname != NULL) {
109                 status = gensec_set_target_hostname(state->gensec,
110                                                     state->target_hostname);
111                 if (!NT_STATUS_IS_OK(status)) {
112                         DBG_DEBUG("gensec_set_target_hostname failed: %s\n",
113                                   nt_errstr(status));
114                         tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
115                         return tevent_req_post(req, ev);
116                 }
117         }
118
119         if (state->target_principal != NULL) {
120                 status = gensec_set_target_principal(state->gensec,
121                                                      state->target_principal);
122                 if (!NT_STATUS_IS_OK(status)) {
123                         DBG_DEBUG("gensec_set_target_principal failed: %s\n",
124                                   nt_errstr(status));
125                         tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
126                         return tevent_req_post(req, ev);
127                 }
128         }
129
130         gensec_want_feature(state->gensec, state->gensec_features);
131
132         status = gensec_start_mech_by_sasl_name(state->gensec, "GSS-SPNEGO");
133         if (!NT_STATUS_IS_OK(status)) {
134                 DBG_ERR("gensec_start_mech_by_sasl_name(GSS-SPNEGO) failed: %s\n",
135                         nt_errstr(status));
136                 tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR);
137                 return tevent_req_post(req, ev);
138         }
139
140         tldap_gensec_update_next(req);
141         if (!tevent_req_is_in_progress(req)) {
142                 return tevent_req_post(req, ev);
143         }
144
145         return req;
146 }
147
148 static void tldap_gensec_update_next(struct tevent_req *req)
149 {
150         struct tldap_gensec_bind_state *state = tevent_req_data(
151                 req, struct tldap_gensec_bind_state);
152         struct tevent_req *subreq = NULL;
153
154         subreq = gensec_update_send(state,
155                                     state->ev,
156                                     state->gensec,
157                                     state->gensec_input);
158         if (tevent_req_nomem(subreq, req)) {
159                 return;
160         }
161         tevent_req_set_callback(subreq,
162                                 tldap_gensec_update_done,
163                                 req);
164 }
165
166 static void tldap_gensec_update_done(struct tevent_req *subreq)
167 {
168         struct tevent_req *req = tevent_req_callback_data(
169                 subreq, struct tevent_req);
170         struct tldap_gensec_bind_state *state = tevent_req_data(
171                 req, struct tldap_gensec_bind_state);
172
173         state->gensec_status = gensec_update_recv(subreq,
174                                                   state,
175                                                   &state->gensec_output);
176         TALLOC_FREE(subreq);
177         data_blob_free(&state->gensec_input);
178         if (!NT_STATUS_IS_OK(state->gensec_status) &&
179             !NT_STATUS_EQUAL(state->gensec_status,
180                              NT_STATUS_MORE_PROCESSING_REQUIRED)) {
181                 DBG_DEBUG("gensec_update failed: %s\n",
182                           nt_errstr(state->gensec_status));
183                 tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
184                 return;
185         }
186
187         if (NT_STATUS_IS_OK(state->gensec_status) &&
188             (state->gensec_output.length == 0)) {
189
190                 if (state->first) {
191                         tevent_req_ldap_error(req, TLDAP_INVALID_CREDENTIALS);
192                 } else {
193                         tevent_req_done(req);
194                 }
195                 return;
196         }
197
198         state->first = false;
199
200         subreq = tldap_sasl_bind_send(state,
201                                       state->ev,
202                                       state->ctx,
203                                       "",
204                                       "GSS-SPNEGO",
205                                       &state->gensec_output,
206                                       NULL,
207                                       0,
208                                       NULL,
209                                       0);
210         if (tevent_req_nomem(subreq, req)) {
211                 return;
212         }
213         tevent_req_set_callback(subreq, tldap_gensec_bind_done, req);
214 }
215
216 static void tldap_gensec_bind_done(struct tevent_req *subreq)
217 {
218         struct tevent_req *req = tevent_req_callback_data(
219                 subreq, struct tevent_req);
220         struct tldap_gensec_bind_state *state = tevent_req_data(
221                 req, struct tldap_gensec_bind_state);
222         TLDAPRC rc;
223
224         rc = tldap_sasl_bind_recv(subreq, state, &state->gensec_input);
225         TALLOC_FREE(subreq);
226         data_blob_free(&state->gensec_output);
227         if (!TLDAP_RC_IS_SUCCESS(rc) &&
228             !TLDAP_RC_EQUAL(rc, TLDAP_SASL_BIND_IN_PROGRESS)) {
229                 tevent_req_ldap_error(req, rc);
230                 return;
231         }
232
233         if (TLDAP_RC_IS_SUCCESS(rc) && NT_STATUS_IS_OK(state->gensec_status)) {
234                 tevent_req_done(req);
235                 return;
236         }
237
238         tldap_gensec_update_next(req);
239 }
240
241 static TLDAPRC tldap_gensec_bind_recv(struct tevent_req *req)
242 {
243         struct tldap_gensec_bind_state *state = tevent_req_data(
244                 req, struct tldap_gensec_bind_state);
245         struct tstream_context *plain, *sec;
246         NTSTATUS status;
247         TLDAPRC rc;
248
249         if (tevent_req_is_ldap_error(req, &rc)) {
250                 return rc;
251         }
252
253         if ((state->gensec_features & GENSEC_FEATURE_SIGN) &&
254             !gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN)) {
255                 return TLDAP_OPERATIONS_ERROR;
256         }
257         if ((state->gensec_features & GENSEC_FEATURE_SEAL) &&
258             !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) {
259                 return TLDAP_OPERATIONS_ERROR;
260         }
261
262         if (!gensec_have_feature(state->gensec, GENSEC_FEATURE_SIGN) &&
263             !gensec_have_feature(state->gensec, GENSEC_FEATURE_SEAL)) {
264                 return TLDAP_SUCCESS;
265         }
266
267         /*
268          * The gensec ctx needs to survive as long as the ldap context
269          * lives
270          */
271         talloc_steal(state->ctx, state->gensec);
272
273         plain = tldap_get_tstream(state->ctx);
274
275         status = gensec_create_tstream(state->ctx, state->gensec,
276                                        plain, &sec);
277         if (!NT_STATUS_IS_OK(status)) {
278                 DBG_DEBUG("gensec_create_tstream failed: %s\n",
279                           nt_errstr(status));
280                 return TLDAP_OPERATIONS_ERROR;
281         }
282
283         tldap_set_tstream(state->ctx, sec);
284
285         return TLDAP_SUCCESS;
286 }
287
288 TLDAPRC tldap_gensec_bind(
289         struct tldap_context *ctx, struct cli_credentials *creds,
290         const char *target_service, const char *target_hostname,
291         const char *target_principal, struct loadparm_context *lp_ctx,
292         uint32_t gensec_features)
293 {
294         TALLOC_CTX *frame = talloc_stackframe();
295         struct tevent_context *ev;
296         struct tevent_req *req;
297         TLDAPRC rc = TLDAP_NO_MEMORY;
298
299         ev = samba_tevent_context_init(frame);
300         if (ev == NULL) {
301                 goto fail;
302         }
303         req = tldap_gensec_bind_send(frame, ev, ctx, creds, target_service,
304                                      target_hostname, target_principal, lp_ctx,
305                                      gensec_features);
306         if (req == NULL) {
307                 goto fail;
308         }
309         if (!tevent_req_poll(req, ev)) {
310                 rc = TLDAP_OPERATIONS_ERROR;
311                 goto fail;
312         }
313         rc = tldap_gensec_bind_recv(req);
314  fail:
315         TALLOC_FREE(frame);
316         return rc;
317 }