Coverage Report

Created: 2026-09-03 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_ioctl_smbtorture.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Core SMB2 server
4
5
   Copyright (C) Stefan Metzmacher 2009
6
   Copyright (C) Jeremy Allison 2021
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 "smbd/smbd.h"
24
#include "smbd/globals.h"
25
#include "../libcli/smb/smb_common.h"
26
#include "../lib/util/tevent_ntstatus.h"
27
#include "include/ntioctl.h"
28
#include "smb2_ioctl_private.h"
29
#include "librpc/gen_ndr/ioctl.h"
30
31
#undef DBGC_CLASS
32
0
#define DBGC_CLASS DBGC_SMB2
33
34
struct async_sleep_state {
35
  struct smbd_server_connection *sconn;
36
  files_struct *fsp;
37
};
38
39
static void smbd_fsctl_torture_async_sleep_done(struct tevent_req *subreq);
40
41
static struct tevent_req *smbd_fsctl_torture_async_sleep_send(
42
        TALLOC_CTX *mem_ctx,
43
        struct tevent_context *ev,
44
        files_struct *fsp,
45
        uint8_t msecs)
46
0
{
47
0
  struct async_sleep_state *state = NULL;
48
0
  struct tevent_req *subreq = NULL;
49
0
  bool ok;
50
51
0
  subreq = tevent_req_create(mem_ctx,
52
0
        &state,
53
0
        struct async_sleep_state);
54
0
  if (!subreq) {
55
0
    return NULL;
56
0
  }
57
58
  /*
59
   * Store the conn separately, as the test is to
60
   * see if fsp is still a valid pointer, so we can't
61
   * do anything other than test it for entry in the
62
   * open files on this server connection.
63
   */
64
0
  state->sconn = fsp->conn->sconn;
65
0
  state->fsp = fsp;
66
67
  /*
68
   * Just wait for the specified number of micro seconds,
69
   * to allow the client time to close fsp.
70
   */
71
0
  ok = tevent_req_set_endtime(subreq,
72
0
            ev,
73
0
            timeval_current_ofs(0, msecs));
74
0
  if (!ok) {
75
0
    tevent_req_nterror(subreq, NT_STATUS_NO_MEMORY);
76
0
    return tevent_req_post(subreq, ev);
77
0
  }
78
79
0
  return subreq;
80
0
}
81
82
static files_struct *find_my_fsp(struct files_struct *fsp,
83
         void *private_data)
84
0
{
85
0
  struct files_struct *myfsp = (struct files_struct *)private_data;
86
87
0
  if (fsp == myfsp) {
88
0
    return myfsp;
89
0
  }
90
0
  return NULL;
91
0
}
92
93
static bool smbd_fsctl_torture_async_sleep_recv(struct tevent_req *subreq)
94
0
{
95
0
  tevent_req_received(subreq);
96
0
  return true;
97
0
}
98
99
static void smbd_fsctl_torture_async_sleep_done(struct tevent_req *subreq)
100
0
{
101
0
  struct files_struct *found_fsp;
102
0
  struct tevent_req *req = tevent_req_callback_data(
103
0
          subreq,
104
0
          struct tevent_req);
105
0
  struct async_sleep_state *state = tevent_req_data(
106
0
          subreq,
107
0
          struct async_sleep_state);
108
109
  /* Does state->fsp still exist on state->sconn ? */
110
0
  found_fsp = files_forall(state->sconn,
111
0
         find_my_fsp,
112
0
         state->fsp);
113
114
0
  smbd_fsctl_torture_async_sleep_recv(subreq);
115
0
  TALLOC_FREE(subreq);
116
117
0
  if (found_fsp == NULL) {
118
    /*
119
     * We didn't find it - return an error to the
120
     * smb2 ioctl request. Use NT_STATUS_FILE_CLOSED so
121
     * the client can tell the difference between
122
     * a bad fsp handle and
123
     *
124
     * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14769
125
     *
126
     * This request should block file closure until it
127
     * has completed.
128
     */
129
0
    tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
130
0
    return;
131
0
  }
132
0
  tevent_req_done(req);
133
0
}
134
135
static NTSTATUS smbd_fsctl_torture_stop_ctdb_node(uint8_t pnn)
136
0
{
137
0
  const char *opt = NULL;
138
0
  char *cmd = NULL;
139
0
  int ret;
140
141
0
  DBG_WARNING("Received stop node %"PRIu8" request\n", pnn);
142
143
0
  opt = lp_parm_const_string (-1, "clusteredmember", "stop_node", NULL);
144
0
  if (opt == NULL) {
145
0
    return NT_STATUS_INTERNAL_ERROR;
146
0
  }
147
148
0
  cmd = talloc_asprintf(talloc_tos(), "%s %"PRIu8, opt, pnn);
149
0
  if (cmd == NULL) {
150
0
    return NT_STATUS_NO_MEMORY;
151
0
  }
152
153
0
  DBG_WARNING("cmd: '%s'\n", cmd);
154
155
0
  ret = smbrun(cmd, NULL, NULL);
156
0
  if (ret != 0) {
157
0
    DBG_WARNING("'%s' failed\n", cmd);
158
0
    TALLOC_FREE(cmd);
159
0
    return NT_STATUS_INTERNAL_ERROR;
160
0
  }
161
0
  TALLOC_FREE(cmd);
162
163
0
  DBG_WARNING("Finished stop node %"PRIu8" request\n", pnn);
164
0
  return NT_STATUS_OK;
165
0
}
166
167
static NTSTATUS smbd_fsctl_torture_start_ctdb_node(uint8_t pnn)
168
0
{
169
0
  const char *opt = NULL;
170
0
  char *cmd = NULL;
171
0
  int ret;
172
173
0
  DBG_WARNING("Received start node %"PRIu8" request\n", pnn);
174
175
0
  opt = lp_parm_const_string (-1, "clusteredmember", "start_node", NULL);
176
0
  if (opt == NULL) {
177
0
    return NT_STATUS_INTERNAL_ERROR;
178
0
  }
179
180
0
  cmd = talloc_asprintf(talloc_tos(), "%s %"PRIu8, opt, pnn);
181
0
  if (cmd == NULL) {
182
0
    return NT_STATUS_NO_MEMORY;
183
0
  }
184
185
0
  DBG_WARNING("cmd: '%s'\n", cmd);
186
187
0
  ret = smbrun(cmd, NULL, NULL);
188
0
  if (ret != 0) {
189
0
    DBG_WARNING("'%s' failed\n", cmd);
190
0
    TALLOC_FREE(cmd);
191
0
    return NT_STATUS_INTERNAL_ERROR;
192
0
  }
193
0
  TALLOC_FREE(cmd);
194
195
0
  DBG_WARNING("Finished start node %"PRIu8" request\n", pnn);
196
0
  return NT_STATUS_OK;
197
0
}
198
199
struct tevent_req *smb2_ioctl_smbtorture(uint32_t ctl_code,
200
           struct tevent_context *ev,
201
           struct tevent_req *req,
202
           struct smbd_smb2_ioctl_state *state)
203
0
{
204
0
  NTSTATUS status;
205
0
  bool ok;
206
207
0
  ok = lp_parm_bool(-1, "smbd", "FSCTL_SMBTORTURE", false);
208
0
  if (!ok) {
209
0
    goto not_supported;
210
0
  }
211
212
0
  switch (ctl_code) {
213
0
  case FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT:
214
0
    if (state->in_input.length != 0) {
215
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
216
0
      return tevent_req_post(req, ev);
217
0
    }
218
219
0
    state->smb2req->xconn->ack.force_unacked_timeout = true;
220
0
    tevent_req_done(req);
221
0
    return tevent_req_post(req, ev);
222
223
0
  case FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8:
224
0
    if (state->in_input.length != 0) {
225
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
226
0
      return tevent_req_post(req, ev);
227
0
    }
228
229
0
    if (state->in_max_output > 0) {
230
0
      uint32_t size = state->in_max_output;
231
232
0
      state->out_output = data_blob_talloc(state, NULL, size);
233
0
      if (tevent_req_nomem(state->out_output.data, req)) {
234
0
        return tevent_req_post(req, ev);
235
0
      }
236
0
      memset(state->out_output.data, 8, size);
237
0
    }
238
239
0
    state->body_padding = 8;
240
0
    tevent_req_done(req);
241
0
    return tevent_req_post(req, ev);
242
243
0
  case FSCTL_SMBTORTURE_GLOBAL_READ_RESPONSE_BODY_PADDING8:
244
0
    if (state->in_input.length != 0) {
245
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
246
0
      return tevent_req_post(req, ev);
247
0
    }
248
249
0
    state->smb2req->xconn->smb2.smbtorture.read_body_padding = 8;
250
0
    tevent_req_done(req);
251
0
    return tevent_req_post(req, ev);
252
253
0
  case FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP: {
254
0
    struct tevent_req *subreq = NULL;
255
256
    /* Data is 1 byte of CVAL stored seconds to delay for. */
257
0
    if (state->in_input.length != 1) {
258
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
259
0
      return tevent_req_post(req, ev);
260
0
    }
261
0
    if (state->fsp == NULL) {
262
0
      tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
263
0
      return tevent_req_post(req, ev);
264
0
    }
265
266
0
    subreq = smbd_fsctl_torture_async_sleep_send(
267
0
            req,
268
0
            ev,
269
0
            state->fsp,
270
0
            CVAL(state->in_input.data,0));
271
0
    if (subreq == NULL) {
272
0
      tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
273
0
      return tevent_req_post(req, ev);
274
0
    }
275
0
    tevent_req_set_callback(subreq,
276
0
          smbd_fsctl_torture_async_sleep_done,
277
0
          req);
278
0
    return req;
279
0
        }
280
281
0
  case FSCTL_SMBTORTURE_GET_CTDB_PNN: {
282
0
    uint32_t pnn = get_my_vnn();
283
284
0
    if (state->in_input.length != 0) {
285
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
286
0
      return tevent_req_post(req, ev);
287
0
    }
288
289
0
    state->out_output = data_blob_talloc(state, NULL, 1);
290
0
    if (state->out_output.data == NULL) {
291
0
      tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
292
0
      return tevent_req_post(req, ev);
293
0
    }
294
295
0
    SCVAL(state->out_output.data, 0x00, (uint8_t)pnn);
296
297
0
    tevent_req_done(req);
298
0
    return tevent_req_post(req, ev);
299
0
  }
300
0
  case FSCTL_SMBTORTURE_STOP_CTDB_NODE:
301
0
    if (state->in_input.length != 1) {
302
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
303
0
      return tevent_req_post(req, ev);
304
0
    }
305
306
0
    status = smbd_fsctl_torture_stop_ctdb_node(
307
0
      PULL_BE_U8(state->in_input.data, 0));
308
0
    if (tevent_req_nterror(req, status)) {
309
0
      return tevent_req_post(req, ev);
310
0
    }
311
0
    tevent_req_done(req);
312
0
    return tevent_req_post(req, ev);
313
314
0
  case FSCTL_SMBTORTURE_START_CTDB_NODE:
315
0
    if (state->in_input.length != 1) {
316
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
317
0
      return tevent_req_post(req, ev);
318
0
    }
319
320
0
    status = smbd_fsctl_torture_start_ctdb_node(
321
0
      PULL_BE_U8(state->in_input.data, 0));
322
0
    if (tevent_req_nterror(req, status)) {
323
0
      return tevent_req_post(req, ev);
324
0
    }
325
0
    tevent_req_done(req);
326
0
    return tevent_req_post(req, ev);
327
328
0
  default:
329
0
    goto not_supported;
330
0
  }
331
332
0
not_supported:
333
0
  if (IS_IPC(state->smbreq->conn)) {
334
0
    status = NT_STATUS_FS_DRIVER_REQUIRED;
335
0
  } else {
336
0
    status = NT_STATUS_INVALID_DEVICE_REQUEST;
337
0
  }
338
339
  tevent_req_nterror(req, status);
340
0
  return tevent_req_post(req, ev);
341
0
}