Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/libcli/smb/smb2cli_create.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 "smb_common.h"
24
#include "smbXcli_base.h"
25
#include "smb2_create_blob.h"
26
#include "reparse.h"
27
28
struct smb2cli_create_state {
29
  enum protocol_types protocol; /* for symlink error response parser */
30
  uint8_t *name_utf16;
31
  size_t name_utf16_len;
32
  uint8_t fixed[56];
33
34
  uint64_t fid_persistent;
35
  uint64_t fid_volatile;
36
  struct smb_create_returns cr;
37
  struct smb2_create_blobs blobs;
38
  struct symlink_reparse_struct *symlink;
39
  struct tevent_req *subreq;
40
};
41
42
static void smb2cli_create_done(struct tevent_req *subreq);
43
static bool smb2cli_create_cancel(struct tevent_req *req);
44
45
struct tevent_req *smb2cli_create_send(
46
  TALLOC_CTX *mem_ctx,
47
  struct tevent_context *ev,
48
  struct smbXcli_conn *conn,
49
  uint32_t timeout_msec,
50
  struct smbXcli_session *session,
51
  struct smbXcli_tcon *tcon,
52
  const char *filename,
53
  uint8_t  oplock_level,    /* SMB2_OPLOCK_LEVEL_* */
54
  uint32_t impersonation_level, /* SMB2_IMPERSONATION_* */
55
  uint32_t desired_access,
56
  uint32_t file_attributes,
57
  uint32_t share_access,
58
  uint32_t create_disposition,
59
  uint32_t create_options,
60
  struct smb2_create_blobs *blobs)
61
0
{
62
0
  struct tevent_req *req, *subreq;
63
0
  struct smb2cli_create_state *state;
64
0
  uint8_t *fixed;
65
0
  DATA_BLOB blob;
66
0
  NTSTATUS status;
67
0
  size_t blobs_offset;
68
0
  uint8_t *dyn;
69
0
  size_t dyn_len;
70
0
  size_t max_dyn_len;
71
0
  uint32_t additional_flags = 0;
72
0
  uint32_t clear_flags = 0;
73
0
  bool ok;
74
75
0
  req = tevent_req_create(mem_ctx, &state,
76
0
        struct smb2cli_create_state);
77
0
  if (req == NULL) {
78
0
    return NULL;
79
0
  }
80
0
  state->protocol = smbXcli_conn_protocol(conn);
81
82
0
  ok = convert_string_talloc(
83
0
    state,
84
0
    CH_UNIX,
85
0
    CH_UTF16,
86
0
    filename,
87
0
    strlen(filename),
88
0
    &state->name_utf16,
89
0
    &state->name_utf16_len);
90
0
  if (!ok) {
91
0
    tevent_req_oom(req);
92
0
    return tevent_req_post(req, ev);
93
0
  }
94
95
0
  if (strlen(filename) == 0) {
96
0
    TALLOC_FREE(state->name_utf16);
97
0
    state->name_utf16_len = 0;
98
0
  }
99
100
0
  fixed = state->fixed;
101
102
0
  SSVAL(fixed, 0, 57);
103
0
  SCVAL(fixed, 3, oplock_level);
104
0
  SIVAL(fixed, 4, impersonation_level);
105
0
  SIVAL(fixed, 24, desired_access);
106
0
  SIVAL(fixed, 28, file_attributes);
107
0
  SIVAL(fixed, 32, share_access);
108
0
  SIVAL(fixed, 36, create_disposition);
109
0
  SIVAL(fixed, 40, create_options);
110
111
0
  SSVAL(fixed, 44, SMB2_HDR_BODY + 56);
112
0
  SSVAL(fixed, 46, state->name_utf16_len);
113
114
0
  blob = data_blob_null;
115
116
0
  if (blobs != NULL) {
117
0
    status = smb2_create_blob_push(state, &blob, *blobs);
118
0
    if (tevent_req_nterror(req, status)) {
119
0
      return tevent_req_post(req, ev);
120
0
    }
121
0
  }
122
123
0
  blobs_offset = state->name_utf16_len;
124
0
  blobs_offset = ((blobs_offset + 3) & ~3);
125
126
0
  if (blob.length > 0) {
127
0
    blobs_offset = ((blobs_offset + 7) & ~7);
128
0
    SIVAL(fixed, 48, blobs_offset + SMB2_HDR_BODY + 56);
129
0
    SIVAL(fixed, 52, blob.length);
130
0
  }
131
132
0
  dyn_len = MAX(1, blobs_offset + blob.length);
133
0
  dyn = talloc_zero_array(state, uint8_t, dyn_len);
134
0
  if (tevent_req_nomem(dyn, req)) {
135
0
    return tevent_req_post(req, ev);
136
0
  }
137
138
0
  if (state->name_utf16 != NULL) {
139
0
    memcpy(dyn, state->name_utf16, state->name_utf16_len);
140
0
  }
141
142
0
  if (blob.data != NULL) {
143
0
    memcpy(dyn + blobs_offset,
144
0
           blob.data, blob.length);
145
0
    data_blob_free(&blob);
146
0
  }
147
148
0
  if (smbXcli_conn_dfs_supported(conn) &&
149
0
      smbXcli_tcon_is_dfs_share(tcon))
150
0
  {
151
0
    additional_flags |= SMB2_HDR_FLAG_DFS;
152
0
  }
153
154
  /*
155
   * We use max_dyn_len = 0
156
   * as we don't explicitly ask for any output length.
157
   *
158
   * But it's still possible for the server to return
159
   * large create blobs.
160
   */
161
0
  max_dyn_len = 0;
162
163
0
  subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_CREATE,
164
0
          additional_flags, clear_flags,
165
0
          timeout_msec,
166
0
          tcon,
167
0
          session,
168
0
          state->fixed, sizeof(state->fixed),
169
0
          dyn, dyn_len,
170
0
          max_dyn_len);
171
0
  if (tevent_req_nomem(subreq, req)) {
172
0
    return tevent_req_post(req, ev);
173
0
  }
174
0
  tevent_req_set_callback(subreq, smb2cli_create_done, req);
175
176
0
  state->subreq = subreq;
177
0
  tevent_req_set_cancel_fn(req, smb2cli_create_cancel);
178
179
0
  return req;
180
0
}
181
182
static bool smb2cli_create_cancel(struct tevent_req *req)
183
0
{
184
0
  struct smb2cli_create_state *state = tevent_req_data(req,
185
0
    struct smb2cli_create_state);
186
0
  return tevent_req_cancel(state->subreq);
187
0
}
188
189
/*
190
 * [MS-SMB2] 2.2.2.2.1 Symbolic Link Error Response
191
 */
192
193
static NTSTATUS smb2cli_parse_symlink_error_response(
194
  TALLOC_CTX *mem_ctx,
195
  const uint8_t *buf,
196
  size_t buflen,
197
  struct symlink_reparse_struct **psymlink)
198
0
{
199
0
  struct symlink_reparse_struct *symlink = NULL;
200
0
  struct reparse_data_buffer reparse_buf = {
201
0
    .tag = 0,
202
0
  };
203
0
  uint32_t symlink_length, error_tag;
204
0
  NTSTATUS status;
205
206
0
  if (buflen < 8) {
207
0
    DBG_DEBUG("buffer too short: %zu bytes\n", buflen);
208
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
209
0
  }
210
211
0
  symlink_length = IVAL(buf, 0);
212
0
  if (symlink_length != (buflen-4)) {
213
0
    DBG_DEBUG("symlink_length=%"PRIu32", (buflen-4)=%zu\n",
214
0
        symlink_length, buflen-4);
215
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
216
0
  }
217
218
0
  error_tag = IVAL(buf, 4);
219
0
  if (error_tag != SYMLINK_ERROR_TAG) {
220
0
    DBG_DEBUG("error_tag=%"PRIu32", expected 0x%x\n",
221
0
        error_tag,
222
0
        SYMLINK_ERROR_TAG);
223
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
224
0
  }
225
226
0
  symlink = talloc(mem_ctx, struct symlink_reparse_struct);
227
0
  if (symlink == NULL) {
228
0
    return NT_STATUS_NO_MEMORY;
229
0
  }
230
231
0
  status = reparse_data_buffer_parse(symlink,
232
0
             &reparse_buf,
233
0
             buf + 8,
234
0
             buflen - 8);
235
0
  if (!NT_STATUS_IS_OK(status)) {
236
0
    DBG_DEBUG("reparse_data_buffer_parse() failed: %s\n",
237
0
        nt_errstr(status));
238
0
    TALLOC_FREE(symlink);
239
0
    return status;
240
0
  }
241
242
0
  if (reparse_buf.tag != IO_REPARSE_TAG_SYMLINK) {
243
0
    DBG_DEBUG("Got tag 0x%" PRIx32 ", "
244
0
        "expected IO_REPARSE_TAG_SYMLINK\n",
245
0
        reparse_buf.tag);
246
0
    TALLOC_FREE(symlink);
247
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
248
0
  }
249
250
0
  *symlink = reparse_buf.parsed.lnk;
251
0
  *psymlink = symlink;
252
0
  return NT_STATUS_OK;
253
0
}
254
255
/*
256
 * [MS-SMB2] 2.2.2 ErrorData
257
 *
258
 * This is in theory a broad API, but as right now we only have a
259
 * single [MS-SMB2] 2.2.2.2.1 symlink error response we can return
260
 * just this.
261
 */
262
static NTSTATUS smb2cli_create_error_data_parse(
263
  enum protocol_types protocol,
264
  uint8_t error_context_count,
265
  uint32_t byte_count,
266
  const uint8_t *buf,
267
  size_t buflen,
268
  TALLOC_CTX *mem_ctx,
269
  struct symlink_reparse_struct **_symlink)
270
0
{
271
0
  struct symlink_reparse_struct *symlink = NULL;
272
0
  uint32_t error_data_length, error_id;
273
0
  NTSTATUS status;
274
275
0
  if (protocol != PROTOCOL_SMB3_11) {
276
0
    if (error_context_count != 0) {
277
0
      DBG_DEBUG("Got error_context_count=%"PRIu8"\n",
278
0
          error_context_count);
279
0
      return NT_STATUS_INVALID_NETWORK_RESPONSE;
280
0
    }
281
282
0
    status = smb2cli_parse_symlink_error_response(
283
0
      mem_ctx, buf, buflen, &symlink);
284
0
    if (!NT_STATUS_IS_OK(status)) {
285
0
      return status;
286
0
    }
287
0
    *_symlink = symlink;
288
0
    return NT_STATUS_OK;
289
0
  }
290
291
  /*
292
   * The STOPPED_ON_SYMLINK that I've seen coming from W2k16 has
293
   * just a single array element in the [MS-SMB2] 2.2.2
294
   * ErrorData array. We'll need to adapt this if there actually
295
   * comes an array of multiple ErrorData elements.
296
   */
297
298
0
  if (error_context_count != 1) {
299
0
    DBG_DEBUG("Got error_context_count=%"PRIu8"\n",
300
0
        error_context_count);
301
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
302
0
  }
303
304
0
  if (byte_count != buflen) {
305
0
    DBG_DEBUG("bytecount=%"PRIu32", "
306
0
        "buflen=%zu\n",
307
0
        byte_count,
308
0
        buflen);
309
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
310
0
  }
311
312
0
  if (buflen < 8) {
313
0
    DBG_DEBUG("buflen=%zu\n", buflen);
314
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
315
0
  }
316
317
0
  error_data_length = IVAL(buf, 0);
318
0
  if (error_data_length != (buflen - 8)) {
319
0
    DBG_DEBUG("error_data_length=%"PRIu32", expected %zu\n",
320
0
        error_data_length,
321
0
        buflen - 8);
322
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
323
0
  }
324
325
0
  error_id = IVAL(buf, 4);
326
0
  if (error_id != 0) {
327
0
    DBG_DEBUG("error_id=%"PRIu32", expected 0\n", error_id);
328
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
329
0
  }
330
331
0
  status = smb2cli_parse_symlink_error_response(
332
0
    mem_ctx, buf + 8, buflen - 8, &symlink);
333
0
  if (!NT_STATUS_IS_OK(status)) {
334
0
    DBG_DEBUG("smb2cli_parse_symlink_error_response failed: %s\n",
335
0
        nt_errstr(status));
336
0
    return status;
337
0
  }
338
339
0
  *_symlink = symlink;
340
0
  return NT_STATUS_OK;
341
0
}
342
343
static NTSTATUS smb2cli_create_unparsed_unix_len(
344
  size_t unparsed_utf16_len,
345
  uint8_t *name_utf16,
346
  size_t name_utf16_len,
347
  size_t *_unparsed_unix_len)
348
0
{
349
0
  uint8_t *unparsed_utf16 = NULL;
350
0
  uint8_t *unparsed_unix = NULL;
351
0
  size_t unparsed_unix_len = 0;
352
0
  bool ok;
353
354
0
  if (unparsed_utf16_len > name_utf16_len) {
355
0
    DBG_DEBUG("unparsed_utf16_len=%zu, name_utf16_len=%zu\n",
356
0
        unparsed_utf16_len,
357
0
        name_utf16_len);
358
0
    return NT_STATUS_INVALID_NETWORK_RESPONSE;
359
0
  }
360
361
0
  if (unparsed_utf16_len == 0) {
362
0
    *_unparsed_unix_len = 0;
363
0
    return NT_STATUS_OK;
364
0
  }
365
366
0
  unparsed_utf16 = name_utf16 + name_utf16_len - unparsed_utf16_len;
367
368
0
  ok = convert_string_talloc(
369
0
    talloc_tos(),
370
0
    CH_UTF16,
371
0
    CH_UNIX,
372
0
    unparsed_utf16,
373
0
    unparsed_utf16_len,
374
0
    &unparsed_unix,
375
0
    &unparsed_unix_len);
376
0
  if (!ok) {
377
0
    NTSTATUS status = map_nt_error_from_unix_common(errno);
378
0
    DBG_DEBUG("convert_string_talloc failed: %s\n",
379
0
        strerror(errno));
380
0
    return status;
381
0
  }
382
0
  *_unparsed_unix_len = unparsed_unix_len;
383
0
  return NT_STATUS_OK;
384
0
}
385
386
static void smb2cli_create_done(struct tevent_req *subreq)
387
0
{
388
0
  struct tevent_req *req =
389
0
    tevent_req_callback_data(subreq,
390
0
    struct tevent_req);
391
0
  struct smb2cli_create_state *state =
392
0
    tevent_req_data(req,
393
0
    struct smb2cli_create_state);
394
0
  NTSTATUS status;
395
0
  struct iovec *iov;
396
0
  uint8_t *body;
397
0
  uint32_t offset, length;
398
0
  static const struct smb2cli_req_expected_response expected[] = {
399
0
  {
400
0
    .status = NT_STATUS_OK,
401
0
    .body_size = 0x59
402
0
  },
403
0
  {
404
0
    .status = NT_STATUS_STOPPED_ON_SYMLINK,
405
0
    .body_size = 0x9,
406
0
  }
407
0
  };
408
409
0
  status = smb2cli_req_recv(subreq, state, &iov,
410
0
          expected, ARRAY_SIZE(expected));
411
0
  TALLOC_FREE(subreq);
412
413
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
414
0
    uint16_t error_context_count = CVAL(iov[1].iov_base, 2);
415
0
    uint32_t byte_count = IVAL(iov[1].iov_base, 4);
416
0
    size_t unparsed_unix_len = 0;
417
418
0
    NTSTATUS symlink_status;
419
420
0
    symlink_status = smb2cli_create_error_data_parse(
421
0
      state->protocol,
422
0
      error_context_count,
423
0
      byte_count,
424
0
      iov[2].iov_base,
425
0
      iov[2].iov_len,
426
0
      state,
427
0
      &state->symlink);
428
0
    if (tevent_req_nterror(req, symlink_status)) {
429
0
      return;
430
0
    }
431
432
    /*
433
     * Our callers want to know the unparsed length in
434
     * unix encoding.
435
     */
436
0
    symlink_status = smb2cli_create_unparsed_unix_len(
437
0
      state->symlink->unparsed_path_length,
438
0
      state->name_utf16,
439
0
      state->name_utf16_len,
440
0
      &unparsed_unix_len);
441
0
    if (tevent_req_nterror(req, symlink_status)) {
442
0
      return;
443
0
    }
444
0
    state->symlink->unparsed_path_length = unparsed_unix_len;
445
0
  }
446
447
0
  if (tevent_req_nterror(req, status)) {
448
0
    return;
449
0
  }
450
451
0
  body = (uint8_t *)iov[1].iov_base;
452
453
0
  state->cr.oplock_level  = CVAL(body, 2);
454
0
  state->cr.flags         = CVAL(body, 3);
455
0
  state->cr.create_action = IVAL(body, 4);
456
0
  state->cr.creation_time = BVAL(body, 8);
457
0
  state->cr.last_access_time = BVAL(body, 16);
458
0
  state->cr.last_write_time = BVAL(body, 24);
459
0
  state->cr.change_time   = BVAL(body, 32);
460
0
  state->cr.allocation_size = BVAL(body, 40);
461
0
  state->cr.end_of_file   = BVAL(body, 48);
462
0
  state->cr.file_attributes = IVAL(body, 56);
463
0
  state->fid_persistent = BVAL(body, 64);
464
0
  state->fid_volatile = BVAL(body, 72);
465
466
0
  offset = IVAL(body, 80);
467
0
  length = IVAL(body, 84);
468
469
0
  if ((offset != 0) && (length != 0)) {
470
0
    if ((offset != SMB2_HDR_BODY + 88) ||
471
0
        (length > iov[2].iov_len)) {
472
0
      tevent_req_nterror(
473
0
        req, NT_STATUS_INVALID_NETWORK_RESPONSE);
474
0
      return;
475
0
    }
476
0
    status = smb2_create_blob_parse(
477
0
      state, data_blob_const(iov[2].iov_base, length),
478
0
      &state->blobs);
479
0
    if (tevent_req_nterror(req, status)) {
480
0
      return;
481
0
    }
482
0
  }
483
0
  tevent_req_done(req);
484
0
}
485
486
NTSTATUS smb2cli_create_recv(struct tevent_req *req,
487
           uint64_t *fid_persistent,
488
           uint64_t *fid_volatile,
489
           struct smb_create_returns *cr,
490
           TALLOC_CTX *mem_ctx,
491
           struct smb2_create_blobs *blobs,
492
           struct symlink_reparse_struct **psymlink)
493
0
{
494
0
  struct smb2cli_create_state *state =
495
0
    tevent_req_data(req,
496
0
    struct smb2cli_create_state);
497
0
  NTSTATUS status;
498
499
0
  if (tevent_req_is_nterror(req, &status)) {
500
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
501
0
        (psymlink != NULL)) {
502
0
      *psymlink = talloc_move(mem_ctx, &state->symlink);
503
0
    }
504
0
    tevent_req_received(req);
505
0
    return status;
506
0
  }
507
0
  *fid_persistent = state->fid_persistent;
508
0
  *fid_volatile = state->fid_volatile;
509
0
  if (cr) {
510
0
    *cr = state->cr;
511
0
  }
512
0
  if (blobs) {
513
0
    blobs->num_blobs = state->blobs.num_blobs;
514
0
    blobs->blobs = talloc_move(mem_ctx, &state->blobs.blobs);
515
0
  }
516
0
  tevent_req_received(req);
517
0
  return NT_STATUS_OK;
518
0
}
519
520
NTSTATUS smb2cli_create(struct smbXcli_conn *conn,
521
      uint32_t timeout_msec,
522
      struct smbXcli_session *session,
523
      struct smbXcli_tcon *tcon,
524
      const char *filename,
525
      uint8_t  oplock_level,       /* SMB2_OPLOCK_LEVEL_* */
526
      uint32_t impersonation_level, /* SMB2_IMPERSONATION_* */
527
      uint32_t desired_access,
528
      uint32_t file_attributes,
529
      uint32_t share_access,
530
      uint32_t create_disposition,
531
      uint32_t create_options,
532
      struct smb2_create_blobs *blobs,
533
      uint64_t *fid_persistent,
534
      uint64_t *fid_volatile,
535
      struct smb_create_returns *cr,
536
      TALLOC_CTX *mem_ctx,
537
      struct smb2_create_blobs *ret_blobs,
538
      struct symlink_reparse_struct **psymlink)
539
0
{
540
0
  TALLOC_CTX *frame = talloc_stackframe();
541
0
  struct tevent_context *ev;
542
0
  struct tevent_req *req;
543
0
  NTSTATUS status = NT_STATUS_NO_MEMORY;
544
545
0
  if (smbXcli_conn_has_async_calls(conn)) {
546
    /*
547
     * Can't use sync call while an async call is in flight
548
     */
549
0
    status = NT_STATUS_INVALID_PARAMETER;
550
0
    goto fail;
551
0
  }
552
0
  ev = samba_tevent_context_init(frame);
553
0
  if (ev == NULL) {
554
0
    goto fail;
555
0
  }
556
0
  req = smb2cli_create_send(frame, ev, conn, timeout_msec,
557
0
          session, tcon,
558
0
          filename, oplock_level,
559
0
          impersonation_level, desired_access,
560
0
          file_attributes, share_access,
561
0
          create_disposition, create_options,
562
0
          blobs);
563
0
  if (req == NULL) {
564
0
    goto fail;
565
0
  }
566
0
  if (!tevent_req_poll_ntstatus(req, ev, &status)) {
567
0
    goto fail;
568
0
  }
569
0
  status = smb2cli_create_recv(
570
0
    req,
571
0
    fid_persistent,
572
0
    fid_volatile,
573
0
    cr,
574
0
    mem_ctx,
575
0
    ret_blobs,
576
0
    psymlink);
577
0
 fail:
578
  TALLOC_FREE(frame);
579
0
  return status;
580
0
}