Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/libcli/smb2/connect.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   SMB2 composite connection setup
5
6
   Copyright (C) Andrew Tridgell 2005
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 "libcli/raw/libcliraw.h"
26
#include "libcli/raw/raw_proto.h"
27
#include "libcli/smb2/smb2.h"
28
#include "libcli/smb2/smb2_calls.h"
29
#include "libcli/composite/composite.h"
30
#include "libcli/resolve/resolve.h"
31
#include "param/param.h"
32
#include "auth/credentials/credentials.h"
33
#include "../libcli/smb/smbXcli_base.h"
34
#include "smb2_constants.h"
35
36
struct smb2_connect_state {
37
  struct tevent_context *ev;
38
  struct cli_credentials *credentials;
39
  struct loadparm_context *lp_ctx;
40
  bool fallback_to_anonymous;
41
  uint64_t previous_session_id;
42
  struct resolve_context *resolve_ctx;
43
  const char *host;
44
  const char *share;
45
  const char *unc;
46
  const char *socket_options;
47
  struct nbt_name calling, called;
48
  struct gensec_settings *gensec_settings;
49
  struct smbcli_options options;
50
  struct smb2_transport *transport;
51
  struct smb2_session *session;
52
  struct smb2_tree *tree;
53
};
54
55
static void smb2_connect_session_start(struct tevent_req *req);
56
static void smb2_connect_socket_done(struct composite_context *creq);
57
58
/*
59
  a composite function that does a full negprot/sesssetup/tcon, returning
60
  a connected smb2_tree
61
 */
62
struct tevent_req *smb2_connect_send(TALLOC_CTX *mem_ctx,
63
             struct tevent_context *ev,
64
             const char *host,
65
             const char *share,
66
             struct loadparm_context *lp_ctx,
67
             struct resolve_context *resolve_ctx,
68
             struct cli_credentials *credentials,
69
             bool fallback_to_anonymous,
70
             struct smbXcli_conn **existing_conn,
71
             uint64_t previous_session_id,
72
             const struct smbcli_options *options,
73
             const char *socket_options,
74
             struct gensec_settings *gensec_settings)
75
0
{
76
0
  struct tevent_req *req;
77
0
  struct smb2_connect_state *state;
78
0
  struct composite_context *creq;
79
0
  enum smb_encryption_setting encryption_state =
80
0
    cli_credentials_get_smb_encryption(credentials);
81
82
0
  req = tevent_req_create(mem_ctx, &state,
83
0
        struct smb2_connect_state);
84
0
  if (req == NULL) {
85
0
    return NULL;
86
0
  }
87
88
0
  state->ev = ev;
89
0
  state->credentials = credentials;
90
0
  state->lp_ctx = lp_ctx;
91
0
  state->fallback_to_anonymous = fallback_to_anonymous;
92
0
  state->previous_session_id = previous_session_id;
93
0
  state->options = *options;
94
0
  state->host = host;
95
0
  state->share = share;
96
0
  state->resolve_ctx = resolve_ctx;
97
0
  state->socket_options = socket_options;
98
0
  state->gensec_settings = gensec_settings;
99
100
0
  if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
101
0
    state->options.signing = SMB_SIGNING_REQUIRED;
102
0
  }
103
104
0
  make_nbt_name_client(&state->calling,
105
0
           cli_credentials_get_workstation(credentials));
106
107
0
  nbt_choose_called_name(state, &state->called,
108
0
             host, NBT_NAME_SERVER);
109
110
0
  state->unc = talloc_asprintf(state, "\\\\%s\\%s",
111
0
            state->host, state->share);
112
0
  if (tevent_req_nomem(state->unc, req)) {
113
0
    return tevent_req_post(req, ev);
114
0
  }
115
116
0
  if (existing_conn != NULL) {
117
0
    NTSTATUS status;
118
119
0
    status = smb2_transport_raw_init(state, ev,
120
0
             existing_conn,
121
0
             &state->options,
122
0
             &state->transport);
123
0
    if (tevent_req_nterror(req, status)) {
124
0
      return tevent_req_post(req, ev);
125
0
    }
126
127
0
    smb2_connect_session_start(req);
128
0
    if (!tevent_req_is_in_progress(req)) {
129
0
      return tevent_req_post(req, ev);
130
0
    }
131
132
0
    return req;
133
0
  }
134
135
0
  creq = smbcli_sock_connect_send(state, NULL, &state->options,
136
0
          state->host, lp_ctx, state->resolve_ctx,
137
0
          state->ev, state->socket_options,
138
0
          &state->calling,
139
0
          &state->called);
140
0
  if (tevent_req_nomem(creq, req)) {
141
0
    return tevent_req_post(req, ev);
142
0
  }
143
0
  creq->async.fn = smb2_connect_socket_done;
144
0
  creq->async.private_data = req;
145
146
0
  return req;
147
0
}
148
149
static void smb2_connect_negprot_done(struct tevent_req *subreq);
150
151
static void smb2_connect_socket_done(struct composite_context *creq)
152
0
{
153
0
  struct tevent_req *req =
154
0
    talloc_get_type_abort(creq->async.private_data,
155
0
    struct tevent_req);
156
0
  struct smb2_connect_state *state =
157
0
    tevent_req_data(req,
158
0
    struct smb2_connect_state);
159
0
  struct smbcli_socket *sock;
160
0
  struct tevent_req *subreq;
161
0
  NTSTATUS status;
162
0
  uint32_t timeout_msec;
163
0
  enum protocol_types min_protocol;
164
165
0
  status = smbcli_sock_connect_recv(creq, state, &sock);
166
0
  if (tevent_req_nterror(req, status)) {
167
0
    return;
168
0
  }
169
170
0
  state->transport = smb2_transport_init(sock, state, &state->options);
171
0
  if (tevent_req_nomem(state->transport, req)) {
172
0
    return;
173
0
  }
174
175
0
  timeout_msec = state->transport->options.request_timeout * 1000;
176
0
  min_protocol = state->transport->options.min_protocol;
177
0
  if (min_protocol < PROTOCOL_SMB2_02) {
178
0
    min_protocol = PROTOCOL_SMB2_02;
179
0
  }
180
181
0
  subreq = smbXcli_negprot_send(state, state->ev,
182
0
              state->transport->conn, timeout_msec,
183
0
              min_protocol,
184
0
              state->transport->options.max_protocol,
185
0
              state->transport->options.max_credits,
186
0
              NULL);
187
0
  if (tevent_req_nomem(subreq, req)) {
188
0
    return;
189
0
  }
190
0
  tevent_req_set_callback(subreq, smb2_connect_negprot_done, req);
191
0
}
192
193
static void smb2_connect_session_done(struct tevent_req *subreq);
194
195
static void smb2_connect_negprot_done(struct tevent_req *subreq)
196
0
{
197
0
  struct tevent_req *req =
198
0
    tevent_req_callback_data(subreq,
199
0
    struct tevent_req);
200
0
  NTSTATUS status;
201
202
0
  status = smbXcli_negprot_recv(subreq, NULL, NULL);
203
0
  TALLOC_FREE(subreq);
204
0
  if (tevent_req_nterror(req, status)) {
205
0
    return;
206
0
  }
207
208
0
  smb2_connect_session_start(req);
209
0
}
210
211
static void smb2_connect_session_start(struct tevent_req *req)
212
0
{
213
0
  struct smb2_connect_state *state =
214
0
    tevent_req_data(req,
215
0
    struct smb2_connect_state);
216
0
  struct smb2_transport *transport = state->transport;
217
0
  struct tevent_req *subreq = NULL;
218
219
0
  state->session = smb2_session_init(transport,
220
0
             state->lp_ctx,
221
0
             state->gensec_settings,
222
0
             state);
223
0
  if (tevent_req_nomem(state->session, req)) {
224
0
    return;
225
0
  }
226
227
0
  if (state->options.only_negprot) {
228
0
    state->tree = smb2_tree_init(state->session, state, true);
229
0
    if (tevent_req_nomem(state->tree, req)) {
230
0
      return;
231
0
    }
232
0
    tevent_req_done(req);
233
0
    return;
234
0
  }
235
236
0
  subreq = smb2_session_setup_spnego_send(state, state->ev,
237
0
            state->session,
238
0
            state->credentials,
239
0
            state->previous_session_id);
240
0
  if (tevent_req_nomem(subreq, req)) {
241
0
    return;
242
0
  }
243
0
  tevent_req_set_callback(subreq, smb2_connect_session_done, req);
244
0
}
245
246
static void smb2_connect_enc_start(struct tevent_req *req);
247
static void smb2_connect_tcon_start(struct tevent_req *req);
248
static void smb2_connect_tcon_done(struct tevent_req *subreq);
249
250
static void smb2_connect_session_done(struct tevent_req *subreq)
251
0
{
252
0
  struct tevent_req *req =
253
0
    tevent_req_callback_data(subreq,
254
0
    struct tevent_req);
255
0
  struct smb2_connect_state *state =
256
0
    tevent_req_data(req,
257
0
    struct smb2_connect_state);
258
0
  NTSTATUS status;
259
260
0
  status = smb2_session_setup_spnego_recv(subreq);
261
0
  TALLOC_FREE(subreq);
262
0
  if (!NT_STATUS_IS_OK(status) &&
263
0
      !cli_credentials_is_anonymous(state->credentials) &&
264
0
      state->fallback_to_anonymous) {
265
0
    struct cli_credentials *anon_creds = NULL;
266
267
    /*
268
     * The transport was moved to session,
269
     * we need to revert that before removing
270
     * the old broken session.
271
     */
272
0
    state->transport = talloc_move(state, &state->session->transport);
273
0
    TALLOC_FREE(state->session);
274
275
0
    anon_creds = cli_credentials_init_anon(state);
276
0
    if (tevent_req_nomem(anon_creds, req)) {
277
0
      return;
278
0
    }
279
0
    cli_credentials_set_workstation(anon_creds,
280
0
       cli_credentials_get_workstation(state->credentials),
281
0
       CRED_SPECIFIED);
282
283
    /*
284
     * retry with anonymous credentials
285
     */
286
0
    state->credentials = anon_creds;
287
0
    smb2_connect_session_start(req);
288
0
    return;
289
0
  }
290
0
  if (tevent_req_nterror(req, status)) {
291
0
    return;
292
0
  }
293
294
0
  state->tree = smb2_tree_init(state->session, state, true);
295
0
  if (tevent_req_nomem(state->tree, req)) {
296
0
    return;
297
0
  }
298
299
0
  smb2_connect_enc_start(req);
300
0
}
301
302
static void smb2_connect_enc_start(struct tevent_req *req)
303
0
{
304
0
  struct smb2_connect_state *state =
305
0
    tevent_req_data(req,
306
0
        struct smb2_connect_state);
307
0
  enum smb_encryption_setting encryption_state =
308
0
    cli_credentials_get_smb_encryption(state->credentials);
309
0
  NTSTATUS status;
310
311
0
  if (encryption_state < SMB_ENCRYPTION_DESIRED) {
312
0
    smb2_connect_tcon_start(req);
313
0
    return;
314
0
  }
315
316
0
  status = smb2cli_session_encryption_on(state->session->smbXcli);
317
0
  if (!NT_STATUS_IS_OK(status)) {
318
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
319
0
      if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
320
0
        smb2_connect_tcon_start(req);
321
0
        return;
322
0
      }
323
324
0
      DBG_ERR("Encryption required and server doesn't support "
325
0
        "SMB3 encryption - failing connect\n");
326
0
      tevent_req_nterror(req, status);
327
0
      return;
328
0
    }
329
330
0
    DBG_ERR("Encryption required and setup failed with error %s.\n",
331
0
      nt_errstr(status));
332
0
    tevent_req_nterror(req, NT_STATUS_PROTOCOL_NOT_SUPPORTED);
333
0
    return;
334
0
  }
335
336
0
  smb2_connect_tcon_start(req);
337
0
}
338
339
static void smb2_connect_tcon_start(struct tevent_req *req)
340
0
{
341
0
  struct smb2_connect_state *state =
342
0
    tevent_req_data(req,
343
0
        struct smb2_connect_state);
344
0
  struct tevent_req *subreq = NULL;
345
0
  uint32_t timeout_msec;
346
347
0
  timeout_msec = state->transport->options.request_timeout * 1000;
348
349
0
  subreq = smb2cli_tcon_send(state, state->ev,
350
0
           state->transport->conn,
351
0
           timeout_msec,
352
0
           state->session->smbXcli,
353
0
           state->tree->smbXcli,
354
0
           0, /* flags */
355
0
           state->unc);
356
0
  if (tevent_req_nomem(subreq, req)) {
357
0
    return;
358
0
  }
359
0
  tevent_req_set_callback(subreq, smb2_connect_tcon_done, req);
360
0
}
361
362
static void smb2_connect_tcon_done(struct tevent_req *subreq)
363
0
{
364
0
  struct tevent_req *req =
365
0
    tevent_req_callback_data(subreq,
366
0
    struct tevent_req);
367
0
  NTSTATUS status;
368
369
0
  status = smb2cli_tcon_recv(subreq);
370
0
  if (tevent_req_nterror(req, status)) {
371
0
    return;
372
0
  }
373
374
0
  tevent_req_done(req);
375
0
}
376
377
NTSTATUS smb2_connect_recv(struct tevent_req *req,
378
         TALLOC_CTX *mem_ctx,
379
         struct smb2_tree **tree)
380
0
{
381
0
  struct smb2_connect_state *state =
382
0
    tevent_req_data(req,
383
0
    struct smb2_connect_state);
384
0
  NTSTATUS status;
385
386
0
  if (tevent_req_is_nterror(req, &status)) {
387
0
    tevent_req_received(req);
388
0
    return status;
389
0
  }
390
391
0
  *tree = talloc_move(mem_ctx, &state->tree);
392
393
0
  tevent_req_received(req);
394
0
  return NT_STATUS_OK;
395
0
}
396
397
/*
398
  sync version of smb2_connect
399
*/
400
NTSTATUS smb2_connect_ext(TALLOC_CTX *mem_ctx,
401
        const char *host,
402
        const char *share,
403
        struct loadparm_context *lp_ctx,
404
        struct resolve_context *resolve_ctx,
405
        struct cli_credentials *credentials,
406
        struct smbXcli_conn **existing_conn,
407
        uint64_t previous_session_id,
408
        struct smb2_tree **tree,
409
        struct tevent_context *ev,
410
        const struct smbcli_options *options,
411
        const char *socket_options,
412
        struct gensec_settings *gensec_settings)
413
0
{
414
0
  struct tevent_req *subreq;
415
0
  NTSTATUS status;
416
0
  bool ok;
417
0
  TALLOC_CTX *frame = talloc_stackframe();
418
419
0
  if (frame == NULL) {
420
0
    return NT_STATUS_NO_MEMORY;
421
0
  }
422
423
0
  subreq = smb2_connect_send(frame,
424
0
           ev,
425
0
           host,
426
0
           share,
427
0
           lp_ctx,
428
0
           resolve_ctx,
429
0
           credentials,
430
0
           false, /* fallback_to_anonymous */
431
0
           existing_conn,
432
0
           previous_session_id,
433
0
           options,
434
0
           socket_options,
435
0
           gensec_settings);
436
0
  if (subreq == NULL) {
437
0
    TALLOC_FREE(frame);
438
0
    return NT_STATUS_NO_MEMORY;
439
0
  }
440
441
0
  ok = tevent_req_poll(subreq, ev);
442
0
  if (!ok) {
443
0
    status = map_nt_error_from_unix_common(errno);
444
0
    TALLOC_FREE(frame);
445
0
    return status;
446
0
  }
447
448
0
  status = smb2_connect_recv(subreq, mem_ctx, tree);
449
0
  TALLOC_FREE(subreq);
450
0
  if (!NT_STATUS_IS_OK(status)) {
451
0
    TALLOC_FREE(frame);
452
0
    return status;
453
0
  }
454
455
0
  TALLOC_FREE(frame);
456
0
  return NT_STATUS_OK;
457
0
}
458
459
NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
460
          const char *host,
461
          const char *share,
462
          struct loadparm_context *lp_ctx,
463
          struct resolve_context *resolve_ctx,
464
          struct cli_credentials *credentials,
465
          struct smb2_tree **tree,
466
          struct tevent_context *ev,
467
          const struct smbcli_options *options,
468
          const char *socket_options,
469
          struct gensec_settings *gensec_settings)
470
0
{
471
0
  NTSTATUS status;
472
473
0
  status = smb2_connect_ext(mem_ctx, host, share, lp_ctx, resolve_ctx,
474
0
          credentials,
475
0
          NULL, /* existing_conn */
476
0
          0, /* previous_session_id */
477
0
          tree, ev, options, socket_options,
478
0
          gensec_settings);
479
480
0
  return status;
481
0
}