Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/smb/smb2cli_session.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   smb2 lib
4
   Copyright (C) Volker Lendecke 2011
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 "includes.h"
21
#include "system/network.h"
22
#include "../lib/util/tevent_ntstatus.h"
23
#include "../libcli/smb/smb_common.h"
24
#include "../libcli/smb/smbXcli_base.h"
25
26
struct smb2cli_session_setup_state {
27
  struct smbXcli_session *session;
28
  uint8_t fixed[24];
29
  uint8_t dyn_pad[1];
30
  struct iovec *recv_iov;
31
  DATA_BLOB out_security_buffer;
32
  NTSTATUS status;
33
};
34
35
static void smb2cli_session_setup_done(struct tevent_req *subreq);
36
37
struct tevent_req *smb2cli_session_setup_send(TALLOC_CTX *mem_ctx,
38
        struct tevent_context *ev,
39
        struct smbXcli_conn *conn,
40
        uint32_t timeout_msec,
41
        struct smbXcli_session *session,
42
        uint8_t in_flags,
43
        uint32_t in_capabilities,
44
        uint32_t in_channel,
45
        uint64_t in_previous_session_id,
46
        const DATA_BLOB *in_security_buffer)
47
0
{
48
0
  struct tevent_req *req, *subreq;
49
0
  struct smb2cli_session_setup_state *state;
50
0
  uint8_t *buf;
51
0
  uint8_t *dyn;
52
0
  size_t dyn_len;
53
0
  uint8_t security_mode;
54
0
  uint16_t security_buffer_offset = 0;
55
0
  uint16_t security_buffer_length = 0;
56
57
0
  req = tevent_req_create(mem_ctx, &state,
58
0
        struct smb2cli_session_setup_state);
59
0
  if (req == NULL) {
60
0
    return NULL;
61
0
  }
62
63
0
  if (session == NULL) {
64
0
    tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
65
0
    return tevent_req_post(req, ev);
66
0
  }
67
0
  state->session = session;
68
0
  security_mode = smb2cli_session_security_mode(session);
69
70
0
  if (in_security_buffer) {
71
0
    if (in_security_buffer->length > UINT16_MAX) {
72
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
73
0
      return tevent_req_post(req, ev);
74
0
    }
75
0
    security_buffer_offset = SMB2_HDR_BODY + 24;
76
0
    security_buffer_length = in_security_buffer->length;
77
0
  }
78
79
0
  buf = state->fixed;
80
81
0
  SSVAL(buf,  0, 25);
82
0
  SCVAL(buf,  2, in_flags);
83
0
  SCVAL(buf,  3, security_mode);
84
0
  SIVAL(buf,  4, in_capabilities);
85
0
  SIVAL(buf,  8, in_channel);
86
0
  SSVAL(buf, 12, security_buffer_offset);
87
0
  SSVAL(buf, 14, security_buffer_length);
88
0
  SBVAL(buf, 16, in_previous_session_id);
89
90
0
  if (security_buffer_length > 0) {
91
0
    dyn = in_security_buffer->data;
92
0
    dyn_len = in_security_buffer->length;
93
0
  } else {
94
0
    dyn = state->dyn_pad;;
95
0
    dyn_len = sizeof(state->dyn_pad);
96
0
  }
97
98
0
  subreq = smb2cli_req_send(state, ev,
99
0
          conn, SMB2_OP_SESSSETUP,
100
0
          0, 0, /* flags */
101
0
          timeout_msec,
102
0
          NULL, /* tcon */
103
0
          session,
104
0
          state->fixed, sizeof(state->fixed),
105
0
          dyn, dyn_len,
106
0
          UINT16_MAX); /* max_dyn_len */
107
0
  if (tevent_req_nomem(subreq, req)) {
108
0
    return tevent_req_post(req, ev);
109
0
  }
110
0
  tevent_req_set_callback(subreq, smb2cli_session_setup_done, req);
111
0
  return req;
112
0
}
113
114
static void smb2cli_session_setup_done(struct tevent_req *subreq)
115
0
{
116
0
  struct tevent_req *req =
117
0
    tevent_req_callback_data(subreq,
118
0
    struct tevent_req);
119
0
  struct smb2cli_session_setup_state *state =
120
0
    tevent_req_data(req,
121
0
    struct smb2cli_session_setup_state);
122
0
  NTSTATUS status;
123
0
  NTSTATUS preauth_status;
124
0
  uint64_t current_session_id;
125
0
  uint64_t session_id;
126
0
  uint16_t session_flags;
127
0
  uint16_t expected_offset = 0;
128
0
  uint16_t security_buffer_offset;
129
0
  uint16_t security_buffer_length;
130
0
  uint8_t *security_buffer_data = NULL;
131
0
  struct iovec sent_iov[3];
132
0
  const uint8_t *hdr;
133
0
  const uint8_t *body;
134
0
  static const struct smb2cli_req_expected_response expected[] = {
135
0
  {
136
0
    .status = NT_STATUS_MORE_PROCESSING_REQUIRED,
137
0
    .body_size = 0x09
138
0
  },
139
0
  {
140
0
    .status = NT_STATUS_OK,
141
0
    .body_size = 0x09
142
0
  }
143
0
  };
144
145
0
  status = smb2cli_req_recv(subreq, state, &state->recv_iov,
146
0
          expected, ARRAY_SIZE(expected));
147
0
  if (!NT_STATUS_IS_OK(status) &&
148
0
      !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
149
0
    TALLOC_FREE(subreq);
150
0
    tevent_req_nterror(req, status);
151
0
    return;
152
0
  }
153
154
0
  smb2cli_req_get_sent_iov(subreq, sent_iov);
155
0
  preauth_status = smb2cli_session_update_preauth(state->session, sent_iov);
156
0
  TALLOC_FREE(subreq);
157
0
  if (tevent_req_nterror(req, preauth_status)) {
158
0
    return;
159
0
  }
160
161
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
162
0
    preauth_status = smb2cli_session_update_preauth(state->session,
163
0
                state->recv_iov);
164
0
    if (tevent_req_nterror(req, preauth_status)) {
165
0
      return;
166
0
    }
167
0
  }
168
169
0
  hdr = (const uint8_t *)state->recv_iov[0].iov_base;
170
0
  body = (const uint8_t *)state->recv_iov[1].iov_base;
171
172
0
  session_id = BVAL(hdr, SMB2_HDR_SESSION_ID);
173
0
  session_flags = SVAL(body, 2);
174
175
0
  security_buffer_offset = SVAL(body, 4);
176
0
  security_buffer_length = SVAL(body, 6);
177
178
0
  if (security_buffer_length > 0) {
179
0
    expected_offset = SMB2_HDR_BODY + 8;
180
0
  }
181
0
  if (security_buffer_offset != 0) {
182
0
    security_buffer_data = (uint8_t *)state->recv_iov[2].iov_base;
183
0
    expected_offset = SMB2_HDR_BODY + 8;
184
0
  }
185
186
0
  if (security_buffer_offset != expected_offset) {
187
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
188
0
    return;
189
0
  }
190
0
  if (security_buffer_length > state->recv_iov[2].iov_len) {
191
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
192
0
    return;
193
0
  }
194
195
0
  state->out_security_buffer.data = security_buffer_data;
196
0
  state->out_security_buffer.length = security_buffer_length;
197
198
0
  current_session_id = smb2cli_session_current_id(state->session);
199
0
  if (current_session_id == 0) {
200
    /* A new session was requested */
201
0
    current_session_id = session_id;
202
0
  }
203
204
0
  if (current_session_id != session_id) {
205
0
    tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
206
0
    return;
207
0
  }
208
209
0
  smb2cli_session_set_id_and_flags(state->session,
210
0
           session_id, session_flags);
211
212
0
  state->status = status;
213
0
  tevent_req_done(req);
214
0
}
215
216
NTSTATUS smb2cli_session_setup_recv(struct tevent_req *req,
217
            TALLOC_CTX *mem_ctx,
218
            struct iovec **recv_iov,
219
            DATA_BLOB *out_security_buffer)
220
0
{
221
0
  struct smb2cli_session_setup_state *state =
222
0
    tevent_req_data(req,
223
0
    struct smb2cli_session_setup_state);
224
0
  NTSTATUS status;
225
0
  struct iovec *_tmp;
226
227
0
  if (tevent_req_is_nterror(req, &status)) {
228
0
    tevent_req_received(req);
229
0
    return status;
230
0
  }
231
232
0
  if (recv_iov == NULL) {
233
0
    recv_iov = &_tmp;
234
0
  }
235
236
0
  *recv_iov = talloc_move(mem_ctx, &state->recv_iov);
237
238
0
  *out_security_buffer = state->out_security_buffer;
239
240
  /*
241
   * Return the status from the server:
242
   * NT_STATUS_MORE_PROCESSING_REQUIRED or
243
   * NT_STATUS_OK.
244
   */
245
0
  status = state->status;
246
0
  tevent_req_received(req);
247
0
  return status;
248
0
}
249
250
struct smb2cli_logoff_state {
251
  uint8_t fixed[4];
252
};
253
254
static void smb2cli_logoff_done(struct tevent_req *subreq);
255
256
struct tevent_req *smb2cli_logoff_send(TALLOC_CTX *mem_ctx,
257
               struct tevent_context *ev,
258
               struct smbXcli_conn *conn,
259
               uint32_t timeout_msec,
260
               struct smbXcli_session *session)
261
0
{
262
0
  struct tevent_req *req, *subreq;
263
0
  struct smb2cli_logoff_state *state;
264
265
0
  req = tevent_req_create(mem_ctx, &state,
266
0
        struct smb2cli_logoff_state);
267
0
  if (req == NULL) {
268
0
    return NULL;
269
0
  }
270
0
  SSVAL(state->fixed, 0, 4);
271
272
0
  subreq = smb2cli_req_send(state, ev,
273
0
          conn, SMB2_OP_LOGOFF,
274
0
          0, 0, /* flags */
275
0
          timeout_msec,
276
0
          NULL, /* tcon */
277
0
          session,
278
0
          state->fixed, sizeof(state->fixed),
279
0
          NULL, 0, /* dyn* */
280
0
          0); /* max_dyn_len */
281
0
  if (tevent_req_nomem(subreq, req)) {
282
0
    return tevent_req_post(req, ev);
283
0
  }
284
0
  tevent_req_set_callback(subreq, smb2cli_logoff_done, req);
285
0
  return req;
286
0
}
287
288
static void smb2cli_logoff_done(struct tevent_req *subreq)
289
0
{
290
0
  struct tevent_req *req =
291
0
    tevent_req_callback_data(subreq,
292
0
    struct tevent_req);
293
0
  struct smb2cli_logoff_state *state =
294
0
    tevent_req_data(req,
295
0
    struct smb2cli_logoff_state);
296
0
  NTSTATUS status;
297
0
  struct iovec *iov;
298
0
  static const struct smb2cli_req_expected_response expected[] = {
299
0
  {
300
0
    .status = NT_STATUS_OK,
301
0
    .body_size = 0x04
302
0
  }
303
0
  };
304
305
0
  status = smb2cli_req_recv(subreq, state, &iov,
306
0
          expected, ARRAY_SIZE(expected));
307
0
  TALLOC_FREE(subreq);
308
0
  if (tevent_req_nterror(req, status)) {
309
0
    return;
310
0
  }
311
0
  tevent_req_done(req);
312
0
}
313
314
NTSTATUS smb2cli_logoff_recv(struct tevent_req *req)
315
0
{
316
0
  return tevent_req_simple_recv_ntstatus(req);
317
0
}
318
319
NTSTATUS smb2cli_logoff(struct smbXcli_conn *conn,
320
      uint32_t timeout_msec,
321
      struct smbXcli_session *session)
322
0
{
323
0
  TALLOC_CTX *frame = talloc_stackframe();
324
0
  struct tevent_context *ev;
325
0
  struct tevent_req *req;
326
0
  NTSTATUS status = NT_STATUS_NO_MEMORY;
327
328
0
  if (smbXcli_conn_has_async_calls(conn)) {
329
    /*
330
     * Can't use sync call while an async call is in flight
331
     */
332
0
    status = NT_STATUS_INVALID_PARAMETER;
333
0
    goto fail;
334
0
  }
335
0
  ev = samba_tevent_context_init(frame);
336
0
  if (ev == NULL) {
337
0
    goto fail;
338
0
  }
339
0
  req = smb2cli_logoff_send(frame, ev, conn, timeout_msec, session);
340
0
  if (req == NULL) {
341
0
    goto fail;
342
0
  }
343
0
  if (!tevent_req_poll_ntstatus(req, ev, &status)) {
344
0
    goto fail;
345
0
  }
346
0
  status = smb2cli_logoff_recv(req);
347
0
 fail:
348
  TALLOC_FREE(frame);
349
0
  return status;
350
0
}