Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}