Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/libcli/smb2/util.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   SMB2 client utility functions
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 "libcli/raw/libcliraw.h"
24
#include "libcli/raw/raw_proto.h"
25
#include "libcli/smb2/smb2.h"
26
#include "libcli/smb2/smb2_calls.h"
27
#include "libcli/smb_composite/smb_composite.h"
28
#include "librpc/gen_ndr/ndr_security.h"
29
30
/*
31
  simple close wrapper with SMB2
32
*/
33
NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h)
34
0
{
35
0
  struct smb2_close c;
36
37
0
  ZERO_STRUCT(c);
38
0
  c.in.file.handle = h;
39
40
0
  return smb2_close(tree, &c);
41
0
}
42
43
/*
44
  unlink a file with SMB2
45
*/
46
NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname)
47
0
{
48
0
  union smb_unlink io;
49
  
50
0
  ZERO_STRUCT(io);
51
0
  io.unlink.in.pattern = fname;
52
53
0
  return smb2_composite_unlink(tree, &io);
54
0
}
55
56
57
/*
58
  rmdir with SMB2
59
*/
60
NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname)
61
0
{
62
0
  struct smb_rmdir io;
63
  
64
0
  ZERO_STRUCT(io);
65
0
  io.in.path = dname;
66
67
0
  return smb2_composite_rmdir(tree, &io);
68
0
}
69
70
71
/*
72
  mkdir with SMB2
73
*/
74
NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname)
75
0
{
76
0
  union smb_mkdir io;
77
  
78
0
  ZERO_STRUCT(io);
79
0
  io.mkdir.level = RAW_MKDIR_MKDIR;
80
0
  io.mkdir.in.path = dname;
81
82
0
  return smb2_composite_mkdir(tree, &io);
83
0
}
84
85
86
/*
87
  set file attribute with SMB2
88
*/
89
NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib)
90
0
{
91
0
  struct smb2_create cr = {0};
92
0
  struct smb2_handle h1 = {{0}};
93
0
  union smb_setfileinfo setinfo;
94
0
  NTSTATUS status;
95
96
0
  cr = (struct smb2_create) {
97
0
    .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE,
98
0
    .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
99
0
    .in.create_disposition = FILE_OPEN,
100
0
    .in.fname = name,
101
0
  };
102
0
  status = smb2_create(tree, tree, &cr);
103
0
  if (!NT_STATUS_IS_OK(status)) {
104
0
    return status;
105
0
  }
106
0
  h1 = cr.out.file.handle;
107
108
0
  setinfo = (union smb_setfileinfo) {
109
0
    .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
110
0
    .basic_info.in.file.handle = h1,
111
0
    .basic_info.in.attrib = attrib,
112
0
  };
113
114
0
  status = smb2_setinfo_file(tree, &setinfo);
115
0
  if (!NT_STATUS_IS_OK(status)) {
116
0
    smb2_util_close(tree, h1);
117
0
    return status;
118
0
  }
119
120
0
  smb2_util_close(tree, h1);
121
0
  return NT_STATUS_OK;
122
0
}
123
124
125
/*
126
  get file attribute with SMB2
127
*/
128
NTSTATUS smb2_util_getatr(struct smb2_tree *tree, const char *fname,
129
        uint16_t *attr, size_t *size, time_t *t)
130
0
{
131
0
  union smb_fileinfo parms;
132
0
  NTSTATUS status;
133
0
  struct smb2_create create_io = {0};
134
135
0
  create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
136
0
  create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
137
0
  create_io.in.create_disposition = FILE_OPEN;
138
0
  create_io.in.fname = fname;
139
0
  status = smb2_create(tree, tree, &create_io);
140
0
  if (!NT_STATUS_IS_OK(status)) {
141
0
    return status;
142
0
  }
143
144
0
  ZERO_STRUCT(parms);
145
0
  parms.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
146
0
  parms.all_info2.in.file.handle = create_io.out.file.handle;
147
0
  status = smb2_getinfo_file(tree, tree, &parms);
148
0
  if (!NT_STATUS_IS_OK(status)) {
149
0
    return status;
150
0
  }
151
152
0
  status = smb2_util_close(tree, create_io.out.file.handle);
153
0
  if (!NT_STATUS_IS_OK(status)) {
154
0
    return status;
155
0
  }
156
157
0
  if (size) {
158
0
    *size = parms.all_info2.out.size;
159
0
  }
160
161
0
  if (t) {
162
0
    *t = parms.all_info2.out.write_time;
163
0
  }
164
165
0
  if (attr) {
166
0
    *attr = parms.all_info2.out.attrib;
167
0
  }
168
169
0
  return status;
170
0
}
171
172
173
/* 
174
   recursively descend a tree deleting all files
175
   returns the number of files deleted, or -1 on error
176
*/
177
int smb2_deltree(struct smb2_tree *tree, const char *dname)
178
0
{
179
0
  NTSTATUS status;
180
0
  uint32_t total_deleted = 0;
181
0
  unsigned int count, i;
182
0
  union smb_search_data *list;
183
0
  TALLOC_CTX *tmp_ctx = talloc_new(tree);
184
0
  struct smb2_find f;
185
0
  struct smb2_create create_parm;
186
0
  union smb_fileinfo finfo;
187
0
  bool did_delete;
188
189
  /* it might be a file */
190
0
  status = smb2_util_unlink(tree, dname);
191
0
  if (NT_STATUS_IS_OK(status)) {
192
0
    talloc_free(tmp_ctx);
193
0
    return 1;
194
0
  }
195
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
196
0
      NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
197
0
      NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
198
0
    talloc_free(tmp_ctx);
199
0
    return 0;
200
0
  }
201
202
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
203
    /* it could be read-only */
204
0
    smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
205
0
    status = smb2_util_unlink(tree, dname);
206
0
  }
207
0
  if (NT_STATUS_IS_OK(status)) {
208
0
    talloc_free(tmp_ctx);
209
0
    return 1;
210
0
  }
211
212
0
  ZERO_STRUCT(create_parm);
213
0
  create_parm.in.desired_access = SEC_FILE_READ_DATA;
214
0
  create_parm.in.share_access = 
215
0
    NTCREATEX_SHARE_ACCESS_READ|
216
0
    NTCREATEX_SHARE_ACCESS_WRITE;
217
0
  create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
218
0
  create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
219
0
  create_parm.in.fname = dname;
220
221
0
  status = smb2_create(tree, tmp_ctx, &create_parm);
222
0
  if (NT_STATUS_IS_ERR(status)) {
223
0
    DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status)));
224
0
    talloc_free(tmp_ctx);
225
0
    return -1;
226
0
  }
227
228
0
  ZERO_STRUCT(finfo);
229
0
  finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
230
0
  finfo.generic.in.file.handle = create_parm.out.file.handle;
231
232
0
  status = smb2_getinfo_file(tree, tmp_ctx, &finfo);
233
0
  if (NT_STATUS_IS_OK(status)) {
234
    /*
235
     * For directories we need to cleanup
236
     * streams manually
237
     */
238
0
    for (i = 0; i < finfo.stream_info.out.num_streams; i++) {
239
0
      const struct stream_struct *s =
240
0
        &finfo.stream_info.out.streams[i];
241
0
      union smb_unlink io;
242
0
      char *spath = NULL;
243
244
0
      if (strequal(s->stream_name.s, "::$DATA")) {
245
        /* should not happen for directories */
246
0
        continue;
247
0
      }
248
249
0
      spath = talloc_asprintf(tmp_ctx,
250
0
            "%s%s",
251
0
            dname,
252
0
            s->stream_name.s);
253
0
      if (spath == NULL) {
254
0
        talloc_free(tmp_ctx);
255
0
        return -1;
256
0
      }
257
258
0
      ZERO_STRUCT(io);
259
0
      io.unlink.in.pattern = spath;
260
0
      if (s->alloc_size != 0) {
261
0
        io.unlink.in.truncate_if_needed = true;
262
0
      }
263
264
0
      status = smb2_composite_unlink(tree, &io);
265
0
      TALLOC_FREE(spath);
266
0
      if (NT_STATUS_IS_OK(status)) {
267
0
        total_deleted++;
268
0
      }
269
0
    }
270
0
  }
271
272
0
  do {
273
0
    did_delete = false;
274
275
0
    ZERO_STRUCT(f);
276
0
    f.in.file.handle       = create_parm.out.file.handle;
277
0
    f.in.max_response_size = 0x10000;
278
0
    f.in.level             = SMB2_FIND_NAME_INFO;
279
0
    f.in.pattern           = "*";
280
    
281
0
    status = smb2_find_level(tree, tmp_ctx, &f, &count, &list);
282
0
    if (NT_STATUS_IS_ERR(status)) {
283
0
      DEBUG(2,("Failed to list %s - %s\n", 
284
0
         dname, nt_errstr(status)));
285
0
      smb2_util_close(tree, create_parm.out.file.handle);
286
0
      talloc_free(tmp_ctx);
287
0
      return -1;
288
0
    }
289
    
290
0
    for (i=0;i<count;i++) {
291
0
      char *name;
292
0
      if (strcmp(".", list[i].name_info.name.s) == 0 ||
293
0
          strcmp("..", list[i].name_info.name.s) == 0) {
294
0
        continue;
295
0
      }
296
0
      name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s);
297
0
      status = smb2_util_unlink(tree, name);
298
0
      if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
299
        /* it could be read-only */
300
0
        smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL);
301
0
        status = smb2_util_unlink(tree, name);
302
0
      }
303
      
304
0
      if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
305
0
        int ret;
306
0
        ret = smb2_deltree(tree, name);
307
0
        if (ret > 0) total_deleted += ret;
308
0
      }
309
0
      talloc_free(name);
310
0
      if (NT_STATUS_IS_OK(status)) {
311
0
        total_deleted++;
312
0
        did_delete = true;
313
0
      }
314
0
    }
315
0
  } while (did_delete);
316
317
0
  smb2_util_close(tree, create_parm.out.file.handle);
318
319
0
  status = smb2_util_rmdir(tree, dname);
320
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
321
    /* it could be read-only */
322
0
    smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
323
0
    status = smb2_util_rmdir(tree, dname);
324
0
  }
325
326
0
  if (NT_STATUS_IS_ERR(status)) {
327
0
    DEBUG(2,("Failed to delete %s - %s\n", 
328
0
       dname, nt_errstr(status)));
329
0
    talloc_free(tmp_ctx);
330
0
    return -1;
331
0
  }
332
333
0
  talloc_free(tmp_ctx);
334
335
0
  return total_deleted;
336
0
}
337
338
/*
339
  check if two SMB2 file handles are the same
340
*/
341
bool smb2_util_handle_equal(const struct smb2_handle h1,
342
          const struct smb2_handle h2)
343
0
{
344
0
  return (h1.data[0] == h2.data[0]) && (h1.data[1] == h2.data[1]);
345
0
}
346
347
bool smb2_util_handle_empty(const struct smb2_handle h)
348
0
{
349
0
  struct smb2_handle empty;
350
351
0
  ZERO_STRUCT(empty);
352
353
0
  return smb2_util_handle_equal(h, empty);
354
0
}
355
356
/****************************************************************************
357
send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
358
****************************************************************************/
359
NTSTATUS smb2_qpathinfo_alt_name(TALLOC_CTX *ctx, struct smb2_tree *tree,
360
         const char *fname, const char **alt_name)
361
0
{
362
0
  union smb_fileinfo parms;
363
0
  TALLOC_CTX *mem_ctx;
364
0
  NTSTATUS status;
365
0
  struct smb2_create create_io = {0};
366
367
0
  mem_ctx = talloc_new(ctx);
368
0
  if (!mem_ctx) {
369
0
    return NT_STATUS_NO_MEMORY;
370
0
  }
371
372
0
  create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
373
0
  create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
374
0
  create_io.in.create_disposition = FILE_OPEN;
375
0
  create_io.in.fname = fname;
376
0
  status = smb2_create(tree, mem_ctx, &create_io);
377
0
  if (!NT_STATUS_IS_OK(status)) {
378
0
    talloc_free(mem_ctx);
379
0
    return status;
380
0
  }
381
382
0
  parms.alt_name_info.level = RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION;
383
0
  parms.alt_name_info.in.file.handle = create_io.out.file.handle;
384
385
0
  status = smb2_getinfo_file(tree, mem_ctx, &parms);
386
0
  if (!NT_STATUS_IS_OK(status)) {
387
0
    talloc_free(mem_ctx);
388
0
    return status;
389
0
  }
390
391
0
  status = smb2_util_close(tree, create_io.out.file.handle);
392
0
  if (!NT_STATUS_IS_OK(status)) {
393
0
    talloc_free(mem_ctx);
394
0
    return status;
395
0
  }
396
397
0
  if (!parms.alt_name_info.out.fname.s) {
398
0
    *alt_name = talloc_strdup(ctx, "");
399
0
  } else {
400
0
    *alt_name = talloc_strdup(ctx,
401
0
            parms.alt_name_info.out.fname.s);
402
0
  }
403
404
0
  talloc_free(mem_ctx);
405
406
0
  return NT_STATUS_OK;
407
0
}