Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/libcli/smb_composite/smb2.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Andrew Tridgell 2008
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
  a composite API for making SMB-like calls using SMB2. This is useful
21
  as SMB2 often requires more than one requests where a single SMB
22
  request would do. In converting code that uses SMB to use SMB2,
23
  these routines make life a lot easier
24
*/
25
26
27
#include "includes.h"
28
#include <tevent.h>
29
#include "lib/util/tevent_ntstatus.h"
30
#include "libcli/raw/libcliraw.h"
31
#include "libcli/raw/raw_proto.h"
32
#include "libcli/composite/composite.h"
33
#include "libcli/smb_composite/smb_composite.h"
34
#include "libcli/smb2/smb2_calls.h"
35
36
/*
37
  continue after a SMB2 close
38
 */
39
static void continue_close(struct smb2_request *req)
40
0
{
41
0
  struct composite_context *ctx = talloc_get_type(req->async.private_data, 
42
0
              struct composite_context);
43
0
  NTSTATUS status;
44
0
  struct smb2_close close_parm;
45
46
0
  status = smb2_close_recv(req, &close_parm);
47
0
  composite_error(ctx, status); 
48
0
}
49
50
struct smb2_composite_unlink_state {
51
  bool truncate_if_needed;
52
  struct smb2_handle handle;
53
};
54
55
/*
56
  continue after the truncate in a composite unlink
57
 */
58
static void continue_truncate(struct smb2_request *req)
59
0
{
60
0
  struct composite_context *ctx = talloc_get_type(req->async.private_data,
61
0
              struct composite_context);
62
0
  struct smb2_composite_unlink_state *state =
63
0
    talloc_get_type_abort(ctx->private_data,
64
0
    struct smb2_composite_unlink_state);
65
0
  struct smb2_tree *tree = req->tree;
66
0
  struct smb2_close close_parm;
67
0
  NTSTATUS status;
68
69
0
  status = smb2_setinfo_recv(req);
70
0
  if (!NT_STATUS_IS_OK(status)) {
71
    /* we ignore errors as we should not leak the handle */
72
0
  }
73
74
0
  ZERO_STRUCT(close_parm);
75
0
  close_parm.in.file.handle = state->handle;
76
0
  close_parm.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
77
78
0
  req = smb2_close_send(tree, &close_parm);
79
0
  composite_continue_smb2(ctx, req, continue_close, ctx);
80
0
}
81
82
/*
83
  continue after the create in a composite unlink
84
 */
85
static void continue_unlink(struct smb2_request *req)
86
0
{
87
0
  struct composite_context *ctx = talloc_get_type(req->async.private_data, 
88
0
              struct composite_context);
89
0
  struct smb2_composite_unlink_state *state =
90
0
    talloc_get_type_abort(ctx->private_data,
91
0
    struct smb2_composite_unlink_state);
92
0
  struct smb2_tree *tree = req->tree;
93
0
  struct smb2_create create_parm;
94
0
  struct smb2_close close_parm;
95
0
  NTSTATUS status;
96
97
0
  status = smb2_create_recv(req, ctx, &create_parm);
98
0
  if (!NT_STATUS_IS_OK(status)) {
99
0
    composite_error(ctx, status);
100
0
    return;
101
0
  }
102
103
0
  if (create_parm.out.size != 0 &&
104
0
      state->truncate_if_needed)
105
0
  {
106
0
    union smb_setfileinfo sinfo;
107
108
0
    state->handle = create_parm.out.file.handle;
109
110
0
    ZERO_STRUCT(sinfo);
111
0
    sinfo.end_of_file_info.level =
112
0
      RAW_SFILEINFO_END_OF_FILE_INFORMATION;
113
0
    sinfo.end_of_file_info.in.file.handle = state->handle;
114
0
    sinfo.end_of_file_info.in.size = 0;
115
0
    req = smb2_setinfo_file_send(tree, &sinfo);
116
0
    composite_continue_smb2(ctx, req, continue_truncate, ctx);
117
0
    return;
118
0
  }
119
120
0
  ZERO_STRUCT(close_parm);
121
0
  close_parm.in.file.handle = create_parm.out.file.handle;
122
  
123
0
  req = smb2_close_send(tree, &close_parm);
124
0
  composite_continue_smb2(ctx, req, continue_close, ctx);
125
0
}
126
127
/*
128
  composite SMB2 unlink call
129
*/
130
struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree, 
131
                 union smb_unlink *io)
132
0
{
133
0
  struct composite_context *ctx;
134
0
  struct smb2_composite_unlink_state *state = NULL;
135
0
  struct smb2_create create_parm;
136
0
  struct smb2_request *req;
137
138
0
  ctx = composite_create(tree, tree->session->transport->ev);
139
0
  if (ctx == NULL) return NULL;
140
141
0
  state = talloc_zero(ctx, struct smb2_composite_unlink_state);
142
0
  if (composite_nomem(state, ctx)) {
143
0
    return ctx;
144
0
  }
145
0
  ctx->private_data = state;
146
0
  state->truncate_if_needed = io->unlink.in.truncate_if_needed;
147
148
  /* check for wildcards - we could support these with a
149
     search, but for now they aren't necessary */
150
0
  if (strpbrk(io->unlink.in.pattern, "*?<>") != NULL) {
151
0
    composite_error(ctx, NT_STATUS_NOT_SUPPORTED);
152
0
    return ctx;
153
0
  }
154
155
0
  ZERO_STRUCT(create_parm);
156
0
  create_parm.in.desired_access     = SEC_STD_DELETE;
157
0
  if (state->truncate_if_needed) {
158
0
    create_parm.in.desired_access |= SEC_FILE_WRITE_DATA;
159
0
  }
160
0
  create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
161
0
  create_parm.in.share_access = 
162
0
    NTCREATEX_SHARE_ACCESS_DELETE|
163
0
    NTCREATEX_SHARE_ACCESS_READ|
164
0
    NTCREATEX_SHARE_ACCESS_WRITE;
165
0
  create_parm.in.create_options = 
166
0
    NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
167
0
    NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
168
0
  create_parm.in.fname = io->unlink.in.pattern;
169
0
  if (create_parm.in.fname[0] == '\\') {
170
0
    create_parm.in.fname++;
171
0
  }
172
173
0
  req = smb2_create_send(tree, &create_parm);
174
175
0
  composite_continue_smb2(ctx, req, continue_unlink, ctx);
176
0
  return ctx;
177
0
}
178
179
180
/*
181
  composite unlink call - sync interface
182
*/
183
NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io)
184
0
{
185
0
  struct composite_context *c = smb2_composite_unlink_send(tree, io);
186
0
  return composite_wait_free(c);
187
0
}
188
189
190
191
192
/*
193
  continue after the create in a composite mkdir
194
 */
195
static void continue_mkdir(struct smb2_request *req)
196
0
{
197
0
  struct composite_context *ctx = talloc_get_type(req->async.private_data, 
198
0
              struct composite_context);
199
0
  struct smb2_tree *tree = req->tree;
200
0
  struct smb2_create create_parm;
201
0
  struct smb2_close close_parm;
202
0
  NTSTATUS status;
203
204
0
  status = smb2_create_recv(req, ctx, &create_parm);
205
0
  if (!NT_STATUS_IS_OK(status)) {
206
0
    composite_error(ctx, status);
207
0
    return;
208
0
  }
209
210
0
  ZERO_STRUCT(close_parm);
211
0
  close_parm.in.file.handle = create_parm.out.file.handle;
212
  
213
0
  req = smb2_close_send(tree, &close_parm);
214
0
  composite_continue_smb2(ctx, req, continue_close, ctx);
215
0
}
216
217
/*
218
  composite SMB2 mkdir call
219
*/
220
struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree, 
221
                 union smb_mkdir *io)
222
0
{
223
0
  struct composite_context *ctx;
224
0
  struct smb2_create create_parm;
225
0
  struct smb2_request *req;
226
227
0
  ctx = composite_create(tree, tree->session->transport->ev);
228
0
  if (ctx == NULL) return NULL;
229
230
0
  ZERO_STRUCT(create_parm);
231
232
0
  create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
233
0
  create_parm.in.share_access = 
234
0
    NTCREATEX_SHARE_ACCESS_READ|
235
0
    NTCREATEX_SHARE_ACCESS_WRITE;
236
0
  create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
237
0
  create_parm.in.file_attributes   = FILE_ATTRIBUTE_DIRECTORY;
238
0
  create_parm.in.create_disposition = NTCREATEX_DISP_CREATE;
239
0
  create_parm.in.fname = io->mkdir.in.path;
240
0
  if (create_parm.in.fname[0] == '\\') {
241
0
    create_parm.in.fname++;
242
0
  }
243
244
0
  req = smb2_create_send(tree, &create_parm);
245
246
0
  composite_continue_smb2(ctx, req, continue_mkdir, ctx);
247
248
0
  return ctx;
249
0
}
250
251
252
/*
253
  composite mkdir call - sync interface
254
*/
255
NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io)
256
0
{
257
0
  struct composite_context *c = smb2_composite_mkdir_send(tree, io);
258
0
  return composite_wait_free(c);
259
0
}
260
261
262
263
/*
264
  continue after the create in a composite rmdir
265
 */
266
static void continue_rmdir(struct smb2_request *req)
267
0
{
268
0
  struct composite_context *ctx = talloc_get_type(req->async.private_data, 
269
0
              struct composite_context);
270
0
  struct smb2_tree *tree = req->tree;
271
0
  struct smb2_create create_parm;
272
0
  struct smb2_close close_parm;
273
0
  NTSTATUS status;
274
275
0
  status = smb2_create_recv(req, ctx, &create_parm);
276
0
  if (!NT_STATUS_IS_OK(status)) {
277
0
    composite_error(ctx, status);
278
0
    return;
279
0
  }
280
281
0
  ZERO_STRUCT(close_parm);
282
0
  close_parm.in.file.handle = create_parm.out.file.handle;
283
  
284
0
  req = smb2_close_send(tree, &close_parm);
285
0
  composite_continue_smb2(ctx, req, continue_close, ctx);
286
0
}
287
288
/*
289
  composite SMB2 rmdir call
290
*/
291
struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree, 
292
                struct smb_rmdir *io)
293
0
{
294
0
  struct composite_context *ctx;
295
0
  struct smb2_create create_parm;
296
0
  struct smb2_request *req;
297
298
0
  ctx = composite_create(tree, tree->session->transport->ev);
299
0
  if (ctx == NULL) return NULL;
300
301
0
  ZERO_STRUCT(create_parm);
302
0
  create_parm.in.desired_access     = SEC_STD_DELETE;
303
0
  create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
304
0
  create_parm.in.share_access = 
305
0
    NTCREATEX_SHARE_ACCESS_DELETE|
306
0
    NTCREATEX_SHARE_ACCESS_READ|
307
0
    NTCREATEX_SHARE_ACCESS_WRITE;
308
0
  create_parm.in.create_options = 
309
0
    NTCREATEX_OPTIONS_DIRECTORY |
310
0
    NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
311
0
  create_parm.in.fname = io->in.path;
312
0
  if (create_parm.in.fname[0] == '\\') {
313
0
    create_parm.in.fname++;
314
0
  }
315
316
0
  req = smb2_create_send(tree, &create_parm);
317
318
0
  composite_continue_smb2(ctx, req, continue_rmdir, ctx);
319
0
  return ctx;
320
0
}
321
322
323
/*
324
  composite rmdir call - sync interface
325
*/
326
NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io)
327
0
{
328
0
  struct composite_context *c = smb2_composite_rmdir_send(tree, io);
329
0
  return composite_wait_free(c);
330
0
}
331
332
struct smb2_composite_setpathinfo_state {
333
  struct smb2_tree *tree;
334
  union smb_setfileinfo io;
335
  NTSTATUS set_status;
336
  struct smb2_create cr;
337
  struct smb2_close cl;
338
};
339
340
static void smb2_composite_setpathinfo_create_done(struct smb2_request *smb2req);
341
342
/*
343
  composite SMB2 setpathinfo call
344
*/
345
struct tevent_req *smb2_composite_setpathinfo_send(TALLOC_CTX *mem_ctx,
346
               struct tevent_context *ev,
347
               struct smb2_tree *tree,
348
               const union smb_setfileinfo *io)
349
0
{
350
0
  struct tevent_req *req;
351
0
  struct smb2_composite_setpathinfo_state *state;
352
0
  struct smb2_request *smb2req;
353
354
0
  req = tevent_req_create(mem_ctx, &state,
355
0
        struct smb2_composite_setpathinfo_state);
356
0
  if (req == NULL) {
357
0
    return NULL;
358
0
  }
359
360
0
  state->tree = tree;
361
0
  state->io = *io;
362
363
0
  state->cr.in.desired_access     = SEC_FLAG_MAXIMUM_ALLOWED;
364
0
  state->cr.in.create_disposition = NTCREATEX_DISP_OPEN;
365
0
  state->cr.in.share_access =
366
0
    NTCREATEX_SHARE_ACCESS_DELETE|
367
0
    NTCREATEX_SHARE_ACCESS_READ|
368
0
    NTCREATEX_SHARE_ACCESS_WRITE;
369
0
  state->cr.in.create_options = 0;
370
0
  state->cr.in.fname = state->io.generic.in.file.path;
371
0
  if (state->cr.in.fname[0] == '\\') {
372
0
    state->cr.in.fname++;
373
0
  }
374
375
0
  smb2req = smb2_create_send(tree, &state->cr);
376
0
  if (tevent_req_nomem(smb2req, req)) {
377
0
    return tevent_req_post(req, ev);
378
0
  }
379
0
  smb2req->async.fn = smb2_composite_setpathinfo_create_done;
380
0
  smb2req->async.private_data = req;
381
382
0
  return req;
383
0
}
384
385
static void smb2_composite_setpathinfo_setinfo_done(struct smb2_request *smb2req);
386
387
static void smb2_composite_setpathinfo_create_done(struct smb2_request *smb2req)
388
0
{
389
0
  struct tevent_req *req =
390
0
    talloc_get_type_abort(smb2req->async.private_data,
391
0
    struct tevent_req);
392
0
  struct smb2_composite_setpathinfo_state *state =
393
0
    tevent_req_data(req,
394
0
    struct smb2_composite_setpathinfo_state);
395
0
  NTSTATUS status;
396
397
0
  status = smb2_create_recv(smb2req, state, &state->cr);
398
0
  if (tevent_req_nterror(req, status)) {
399
0
    return;
400
0
  }
401
402
0
  state->io.generic.in.file.handle = state->cr.out.file.handle;
403
404
0
  smb2req = smb2_setinfo_file_send(state->tree, &state->io);
405
0
  if (tevent_req_nomem(smb2req, req)) {
406
0
    return;
407
0
  }
408
0
  smb2req->async.fn = smb2_composite_setpathinfo_setinfo_done;
409
0
  smb2req->async.private_data = req;
410
0
}
411
412
static void smb2_composite_setpathinfo_close_done(struct smb2_request *smb2req);
413
414
static void smb2_composite_setpathinfo_setinfo_done(struct smb2_request *smb2req)
415
0
{
416
0
  struct tevent_req *req =
417
0
    talloc_get_type_abort(smb2req->async.private_data,
418
0
    struct tevent_req);
419
0
  struct smb2_composite_setpathinfo_state *state =
420
0
    tevent_req_data(req,
421
0
    struct smb2_composite_setpathinfo_state);
422
0
  NTSTATUS status;
423
424
0
  status = smb2_setinfo_recv(smb2req);
425
0
  state->set_status = status;
426
427
0
  state->cl.in.file.handle = state->io.generic.in.file.handle;
428
429
0
  smb2req = smb2_close_send(state->tree, &state->cl);
430
0
  if (tevent_req_nomem(smb2req, req)) {
431
0
    return;
432
0
  }
433
0
  smb2req->async.fn = smb2_composite_setpathinfo_close_done;
434
0
  smb2req->async.private_data = req;
435
0
}
436
437
static void smb2_composite_setpathinfo_close_done(struct smb2_request *smb2req)
438
0
{
439
0
  struct tevent_req *req =
440
0
    talloc_get_type_abort(smb2req->async.private_data,
441
0
    struct tevent_req);
442
0
  struct smb2_composite_setpathinfo_state *state =
443
0
    tevent_req_data(req,
444
0
    struct smb2_composite_setpathinfo_state);
445
0
  NTSTATUS status;
446
447
0
  status = smb2_close_recv(smb2req, &state->cl);
448
449
0
  if (tevent_req_nterror(req, state->set_status)) {
450
0
    return;
451
0
  }
452
453
0
  if (tevent_req_nterror(req, status)) {
454
0
    return;
455
0
  }
456
457
0
  tevent_req_done(req);
458
0
}
459
460
NTSTATUS smb2_composite_setpathinfo_recv(struct tevent_req *req)
461
0
{
462
0
  NTSTATUS status;
463
464
0
  if (tevent_req_is_nterror(req, &status)) {
465
0
    tevent_req_received(req);
466
0
    return status;
467
0
  }
468
469
0
  tevent_req_received(req);
470
0
  return NT_STATUS_OK;
471
0
}
472
473
/*
474
  composite setpathinfo call
475
 */
476
NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io)
477
0
{
478
0
  struct tevent_req *subreq;
479
0
  NTSTATUS status;
480
0
  bool ok;
481
0
  TALLOC_CTX *frame = talloc_stackframe();
482
0
  struct tevent_context *ev = tree->session->transport->ev;
483
484
0
  if (frame == NULL) {
485
0
    return NT_STATUS_NO_MEMORY;
486
0
  }
487
488
0
  subreq = smb2_composite_setpathinfo_send(frame, ev, tree, io);
489
0
  if (subreq == NULL) {
490
0
    TALLOC_FREE(frame);
491
0
    return NT_STATUS_NO_MEMORY;
492
0
  }
493
494
0
  ok = tevent_req_poll(subreq, ev);
495
0
  if (!ok) {
496
0
    status = map_nt_error_from_unix_common(errno);
497
0
    TALLOC_FREE(frame);
498
0
    return status;
499
0
  }
500
501
0
  status = smb2_composite_setpathinfo_recv(subreq);
502
0
  TALLOC_FREE(subreq);
503
0
  if (!NT_STATUS_IS_OK(status)) {
504
0
    TALLOC_FREE(frame);
505
0
    return status;
506
0
  }
507
508
0
  TALLOC_FREE(frame);
509
0
  return NT_STATUS_OK;
510
0
}