Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_setinfo.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 2010
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 "locking/share_mode_lock.h"
24
#include "smbd/smbd.h"
25
#include "smbd/globals.h"
26
#include "../libcli/smb/smb_common.h"
27
#include "trans2.h"
28
#include "../lib/util/tevent_ntstatus.h"
29
#include "../librpc/gen_ndr/open_files.h"
30
#include "source3/lib/dbwrap/dbwrap_watch.h"
31
#include "messages.h"
32
#include "librpc/gen_ndr/ndr_quota.h"
33
#include "libcli/security/security.h"
34
35
#undef DBGC_CLASS
36
0
#define DBGC_CLASS DBGC_SMB2
37
38
static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
39
             struct tevent_context *ev,
40
             struct smbd_smb2_request *smb2req,
41
             struct files_struct *in_fsp,
42
             uint8_t in_info_type,
43
             uint8_t in_file_info_class,
44
             DATA_BLOB in_input_buffer,
45
             uint32_t in_additional_information);
46
static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req);
47
48
static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq);
49
NTSTATUS smbd_smb2_request_process_setinfo(struct smbd_smb2_request *req)
50
0
{
51
0
  struct smbXsrv_connection *xconn = req->xconn;
52
0
  NTSTATUS status;
53
0
  const uint8_t *inbody;
54
0
  uint8_t in_info_type;
55
0
  uint8_t in_file_info_class;
56
0
  uint16_t in_input_buffer_offset;
57
0
  uint32_t in_input_buffer_length;
58
0
  DATA_BLOB in_input_buffer;
59
0
  uint32_t in_additional_information;
60
0
  uint64_t in_file_id_persistent;
61
0
  uint64_t in_file_id_volatile;
62
0
  struct files_struct *in_fsp;
63
0
  struct tevent_req *subreq;
64
65
0
  status = smbd_smb2_request_verify_sizes(req, 0x21);
66
0
  if (!NT_STATUS_IS_OK(status)) {
67
0
    return smbd_smb2_request_error(req, status);
68
0
  }
69
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
70
71
0
  in_info_type      = CVAL(inbody, 0x02);
72
0
  in_file_info_class    = CVAL(inbody, 0x03);
73
0
  in_input_buffer_length    = IVAL(inbody, 0x04);
74
0
  in_input_buffer_offset    = SVAL(inbody, 0x08);
75
  /* 0x0A 2 bytes reserved */
76
0
  in_additional_information = IVAL(inbody, 0x0C);
77
0
  in_file_id_persistent   = BVAL(inbody, 0x10);
78
0
  in_file_id_volatile   = BVAL(inbody, 0x18);
79
80
0
  if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) {
81
    /* This is ok */
82
0
  } else if (in_input_buffer_offset !=
83
0
       (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
84
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
85
0
  }
86
87
0
  if (in_input_buffer_length > SMBD_SMB2_IN_DYN_LEN(req)) {
88
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
89
0
  }
90
91
0
  in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
92
0
  in_input_buffer.length = in_input_buffer_length;
93
94
0
  if (in_input_buffer.length > xconn->smb2.server.max_trans) {
95
0
    DEBUG(2,("smbd_smb2_request_process_setinfo: "
96
0
       "client ignored max trans: %s: 0x%08X: 0x%08X\n",
97
0
       __location__, (unsigned)in_input_buffer.length,
98
0
       (unsigned)xconn->smb2.server.max_trans));
99
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
100
0
  }
101
102
0
  status = smbd_smb2_request_verify_creditcharge(req,
103
0
            in_input_buffer.length);
104
0
  if (!NT_STATUS_IS_OK(status)) {
105
0
    return smbd_smb2_request_error(req, status);
106
0
  }
107
108
0
  in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
109
0
  if (in_fsp == NULL) {
110
0
    return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
111
0
  }
112
113
0
  subreq = smbd_smb2_setinfo_send(req, req->sconn->ev_ctx,
114
0
          req, in_fsp,
115
0
          in_info_type,
116
0
          in_file_info_class,
117
0
          in_input_buffer,
118
0
          in_additional_information);
119
0
  if (subreq == NULL) {
120
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
121
0
  }
122
0
  tevent_req_set_callback(subreq, smbd_smb2_request_setinfo_done, req);
123
124
  /*
125
   * Windows never sends async interim responses if a rename triggers a
126
   * lease break. See test smb2.lease.compound_rename_middle.
127
   */
128
0
  return smbd_smb2_request_pending_queue(req, subreq, 0);
129
0
}
130
131
static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq)
132
0
{
133
0
  struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
134
0
          struct smbd_smb2_request);
135
0
  DATA_BLOB outbody;
136
0
  NTSTATUS status;
137
0
  NTSTATUS error; /* transport error */
138
139
0
  status = smbd_smb2_setinfo_recv(subreq);
140
0
  TALLOC_FREE(subreq);
141
0
  if (!NT_STATUS_IS_OK(status)) {
142
0
    error = smbd_smb2_request_error(req, status);
143
0
    if (!NT_STATUS_IS_OK(error)) {
144
0
      smbd_server_connection_terminate(req->xconn,
145
0
               nt_errstr(error));
146
0
      return;
147
0
    }
148
0
    return;
149
0
  }
150
151
0
  outbody = smbd_smb2_generate_outbody(req, 0x02);
152
0
  if (outbody.data == NULL) {
153
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
154
0
    if (!NT_STATUS_IS_OK(error)) {
155
0
      smbd_server_connection_terminate(req->xconn,
156
0
               nt_errstr(error));
157
0
      return;
158
0
    }
159
0
    return;
160
0
  }
161
162
0
  SSVAL(outbody.data, 0x00, 0x02); /* struct size */
163
164
0
  error = smbd_smb2_request_done(req, outbody, NULL);
165
0
  if (!NT_STATUS_IS_OK(error)) {
166
0
    smbd_server_connection_terminate(req->xconn,
167
0
             nt_errstr(error));
168
0
    return;
169
0
  }
170
0
}
171
172
struct smbd_smb2_setinfo_state {
173
  struct tevent_context *ev;
174
  struct smbd_smb2_request *smb2req;
175
  struct files_struct *fsp;
176
  struct share_mode_lock *lck;
177
  struct files_struct *dstfsp;
178
  struct files_struct *dst_parent_dirfsp;
179
  uint16_t file_info_level;
180
  DATA_BLOB data;
181
  bool delay;
182
  bool rename_dst_check_done;
183
  bool rename_dst_parent_check_done;
184
};
185
186
static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req);
187
188
static void smbd_smb2_setinfo_cleanup(struct tevent_req *req,
189
              enum tevent_req_state req_state)
190
0
{
191
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
192
0
    req, struct smbd_smb2_setinfo_state);
193
194
0
  if (req_state == TEVENT_REQ_DONE) {
195
0
    return;
196
0
  }
197
0
  TALLOC_FREE(state->lck);
198
0
}
199
200
static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
201
             struct tevent_context *ev,
202
             struct smbd_smb2_request *smb2req,
203
             struct files_struct *fsp,
204
             uint8_t in_info_type,
205
             uint8_t in_file_info_class,
206
             DATA_BLOB in_input_buffer,
207
             uint32_t in_additional_information)
208
0
{
209
0
  struct tevent_req *req = NULL;
210
0
  struct smbd_smb2_setinfo_state *state = NULL;
211
0
  struct smb_request *smbreq = NULL;
212
0
  connection_struct *conn = smb2req->tcon->compat;
213
0
  NTSTATUS status;
214
0
  int ret;
215
216
0
  req = tevent_req_create(mem_ctx, &state,
217
0
        struct smbd_smb2_setinfo_state);
218
0
  if (req == NULL) {
219
0
    return NULL;
220
0
  }
221
0
  state->ev = ev;
222
0
  state->smb2req = smb2req;
223
0
  state->fsp = fsp;
224
0
  state->data = in_input_buffer;
225
226
0
  tevent_req_set_cleanup_fn(req, smbd_smb2_setinfo_cleanup);
227
228
0
  DEBUG(10,("smbd_smb2_setinfo_send: %s - %s\n",
229
0
      fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
230
231
0
  smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
232
0
  if (tevent_req_nomem(smbreq, req)) {
233
0
    return tevent_req_post(req, ev);
234
0
  }
235
236
0
  if (IS_IPC(conn)) {
237
0
    tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
238
0
    return tevent_req_post(req, ev);
239
0
  }
240
241
0
  switch (in_info_type) {
242
0
  case SMB2_0_INFO_FILE:
243
0
  {
244
0
    uint16_t file_info_level;
245
246
0
    file_info_level = in_file_info_class + 1000;
247
0
    if (file_info_level == SMB_FILE_RENAME_INFORMATION) {
248
      /* SMB2_FILE_RENAME_INFORMATION_INTERNAL == 0xFF00 + in_file_info_class */
249
0
      file_info_level = SMB2_FILE_RENAME_INFORMATION_INTERNAL;
250
0
    }
251
0
    state->file_info_level = file_info_level;
252
253
0
    if (fsp_get_pathref_fd(fsp) == -1) {
254
      /*
255
       * This is actually a SETFILEINFO on a directory
256
       * handle (returned from an NT SMB). NT5.0 seems
257
       * to do this call. JRA.
258
       */
259
0
      ret = vfs_stat(fsp->conn, fsp->fsp_name);
260
0
      if (ret != 0) {
261
0
        DBG_WARNING("vfs_stat() of %s failed (%s)\n",
262
0
              fsp_str_dbg(fsp),
263
0
              strerror(errno));
264
0
        status = map_nt_error_from_unix(errno);
265
0
        tevent_req_nterror(req, status);
266
0
        return tevent_req_post(req, ev);
267
0
      }
268
0
    } else if (fsp->print_file) {
269
      /*
270
       * Doing a DELETE_ON_CLOSE should cancel a print job.
271
       */
272
0
      if ((file_info_level == SMB_SET_FILE_DISPOSITION_INFO)
273
0
          && in_input_buffer.length >= 1
274
0
          && CVAL(in_input_buffer.data,0)) {
275
0
        fsp->fsp_flags.delete_on_close = true;
276
277
0
        DEBUG(3,("smbd_smb2_setinfo_send: "
278
0
           "Cancelling print job (%s)\n",
279
0
           fsp_str_dbg(fsp)));
280
281
0
        tevent_req_done(req);
282
0
        return tevent_req_post(req, ev);
283
0
      }
284
0
      tevent_req_nterror(req, NT_STATUS_OBJECT_PATH_INVALID);
285
0
      return tevent_req_post(req, ev);
286
0
    } else {
287
      /*
288
       * Original code - this is an open file.
289
       */
290
291
0
      status = vfs_stat_fsp(fsp);
292
0
      if (!NT_STATUS_IS_OK(status)) {
293
0
        DEBUG(3,("smbd_smb2_setinfo_send: fstat "
294
0
           "of %s failed (%s)\n",
295
0
           fsp_fnum_dbg(fsp),
296
0
           nt_errstr(status)));
297
0
        tevent_req_nterror(req, status);
298
0
        return tevent_req_post(req, ev);
299
0
      }
300
0
    }
301
302
0
    smbd_smb2_setinfo_lease_break_check(req);
303
0
    if (!tevent_req_is_in_progress(req)) {
304
0
      return tevent_req_post(req, ev);
305
0
    }
306
0
    SMB_ASSERT(state->delay);
307
0
    return req;
308
0
  }
309
310
0
  case SMB2_0_INFO_FILESYSTEM:
311
0
  {
312
0
    uint16_t file_info_level = in_file_info_class + 1000;
313
314
0
    status = smbd_do_setfsinfo(conn, smbreq, state,
315
0
          file_info_level,
316
0
          fsp,
317
0
          &in_input_buffer);
318
0
    if (!NT_STATUS_IS_OK(status)) {
319
0
      if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
320
0
        status = NT_STATUS_INVALID_INFO_CLASS;
321
0
      }
322
0
      tevent_req_nterror(req, status);
323
0
      return tevent_req_post(req, ev);
324
0
    }
325
0
    break;
326
0
  }
327
328
0
  case SMB2_0_INFO_SECURITY:
329
0
  {
330
0
    if (!CAN_WRITE(conn)) {
331
0
      tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
332
0
      return tevent_req_post(req, ev);
333
0
    }
334
335
0
    status = set_sd_blob(fsp,
336
0
        in_input_buffer.data,
337
0
        in_input_buffer.length,
338
0
        in_additional_information &
339
0
        SMB_SUPPORTED_SECINFO_FLAGS);
340
0
    if (!NT_STATUS_IS_OK(status)) {
341
0
      tevent_req_nterror(req, status);
342
0
      return tevent_req_post(req, ev);
343
0
    }
344
0
    break;
345
0
  }
346
347
0
  case SMB2_0_INFO_QUOTA:
348
0
  {
349
0
#ifdef HAVE_SYS_QUOTAS
350
0
    struct file_quota_information info = {0};
351
0
    SMB_NTQUOTA_STRUCT qt = {0};
352
0
    enum ndr_err_code err;
353
354
0
    if (!fsp->fake_file_handle) {
355
0
      tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
356
0
      return tevent_req_post(req, ev);
357
0
    }
358
0
    err = ndr_pull_struct_blob(
359
0
      &in_input_buffer, state, &info,
360
0
      (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
361
0
    if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
362
0
      tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
363
0
      return tevent_req_post(req, ev);
364
0
    }
365
366
0
    qt.usedspace = info.quota_used;
367
368
0
    qt.softlim = info.quota_threshold;
369
370
0
    qt.hardlim = info.quota_limit;
371
372
0
    qt.sid = info.sid;
373
0
    ret = vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt);
374
0
    if (ret !=0 ) {
375
0
      status = map_nt_error_from_unix(errno);
376
0
      tevent_req_nterror(req, status);
377
0
      return tevent_req_post(req, ev);
378
0
    }
379
0
    status = NT_STATUS_OK;
380
0
    break;
381
#else
382
    tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
383
    return tevent_req_post(req, ev);
384
#endif
385
0
  }
386
0
  default:
387
0
    tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
388
0
    return tevent_req_post(req, ev);
389
0
  }
390
391
0
  tevent_req_done(req);
392
0
  return tevent_req_post(req, ev);
393
0
}
394
395
static NTSTATUS smb2_parse_file_rename_information_dst(
396
  TALLOC_CTX *mem_ctx,
397
  struct connection_struct *conn,
398
  struct smb_request *req,
399
  const char *pdata,
400
  int total_data,
401
  files_struct *fsp,
402
  struct smb_filename *smb_fname_src,
403
  char **_newname,
404
  bool *_overwrite,
405
  struct files_struct **_dst_dirfsp,
406
  struct smb_filename **_smb_fname_dst)
407
0
{
408
0
  char *newname = NULL;
409
0
  bool overwrite = false;
410
0
  struct files_struct *dst_dirfsp = NULL;
411
0
  struct smb_filename *smb_fname_dst = NULL;
412
0
  uint32_t ucf_flags = ucf_flags_from_smb_request(req);
413
0
  NTSTATUS status;
414
415
0
  status = smb2_parse_file_rename_information(
416
0
    mem_ctx,
417
0
    conn,
418
0
    req,
419
0
    pdata,
420
0
    total_data,
421
0
    fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH,
422
0
    &newname,
423
0
    &overwrite);
424
0
  if (!NT_STATUS_IS_OK(status)) {
425
0
    return status;
426
0
  }
427
428
  /* SMB2 rename paths are never DFS. */
429
0
  req->flags2 &= ~FLAGS2_DFS_PATHNAMES;
430
0
  ucf_flags &= ~UCF_DFS_PATHNAME;
431
432
0
  if (newname[0] == ':') {
433
    /* Create an smb_fname to call rename_internals_fsp() with. */
434
0
    smb_fname_dst = synthetic_smb_fname(
435
0
      mem_ctx,
436
0
      fsp->base_fsp->fsp_name->base_name,
437
0
      newname,
438
0
      NULL,
439
0
      fsp->base_fsp->fsp_name->twrp,
440
0
      fsp->base_fsp->fsp_name->flags);
441
0
    if (smb_fname_dst == NULL) {
442
0
      TALLOC_FREE(newname);
443
0
      return NT_STATUS_NO_MEMORY;
444
0
    }
445
0
    goto done;
446
0
  }
447
448
0
  status = filename_convert_dirfsp(mem_ctx,
449
0
           conn,
450
0
           newname,
451
0
           ucf_flags,
452
0
           0, /* Never a TWRP. */
453
0
           &dst_dirfsp,
454
0
           &smb_fname_dst);
455
0
  if (!NT_STATUS_IS_OK(status)) {
456
0
    return status;
457
0
  }
458
459
0
done:
460
0
  *_newname = newname;
461
0
  *_overwrite = overwrite;
462
0
  *_dst_dirfsp = dst_dirfsp;
463
0
  *_smb_fname_dst = smb_fname_dst;
464
0
  return NT_STATUS_OK;
465
0
}
466
467
468
static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req);
469
static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq);
470
static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req);
471
static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq);
472
static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req);
473
static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
474
  struct tevent_req *subreq);
475
476
static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req)
477
0
{
478
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
479
0
    req, struct smbd_smb2_setinfo_state);
480
0
  int ret_size;
481
0
  NTSTATUS status;
482
483
0
  state->delay = false;
484
485
0
  smbd_smb2_setinfo_rename_dst_check(req);
486
0
  if (!tevent_req_is_in_progress(req)) {
487
0
    return;
488
0
  }
489
0
  if (state->delay) {
490
0
    DBG_DEBUG("Waiting for h-lease breaks on rename destination\n");
491
0
    return;
492
0
  }
493
494
0
  smbd_smb2_setinfo_rename_dst_parent_check(req);
495
0
  if (!tevent_req_is_in_progress(req)) {
496
0
    return;
497
0
  }
498
0
  if (state->delay) {
499
0
    DBG_DEBUG("Waiting for h-lease breaks on rename destination "
500
0
        "parent directory\n");
501
0
    return;
502
0
  }
503
504
0
  smbd_smb2_setinfo_lease_break_fsp_check(req);
505
0
  if (!tevent_req_is_in_progress(req)) {
506
0
    return;
507
0
  }
508
0
  if (state->delay) {
509
0
    TALLOC_FREE(state->lck);
510
0
    DBG_DEBUG("Waiting for h-lease breaks on fsp [%s]\n",
511
0
        fsp_str_dbg(state->fsp));
512
0
    return;
513
0
  }
514
515
0
  status = smbd_do_setfilepathinfo(state->fsp->conn,
516
0
           state->smb2req->smb1req,
517
0
           state,
518
0
           state->file_info_level,
519
0
           state->fsp,
520
0
           &state->lck,
521
0
           state->fsp->fsp_name,
522
0
           (char *)state->data.data,
523
0
           state->data.length,
524
0
           &ret_size);
525
0
  TALLOC_FREE(state->lck);
526
0
  if (!NT_STATUS_IS_OK(status)) {
527
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
528
0
      status = NT_STATUS_INVALID_INFO_CLASS;
529
0
    }
530
0
    tevent_req_nterror(req, status);
531
0
    return;
532
0
  }
533
534
0
  tevent_req_done(req);
535
0
}
536
537
static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req)
538
0
{
539
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
540
0
    req, struct smbd_smb2_setinfo_state);
541
0
  struct smbd_smb2_request *smb2req = state->smb2req;
542
0
  struct files_struct *fsp = state->fsp;
543
0
  uint16_t file_info_level = state->file_info_level;
544
0
  struct tevent_req *subreq = NULL;
545
0
  struct timeval timeout;
546
0
  bool rename;
547
0
  bool delete_on_close = false;
548
0
  NTSTATUS status;
549
550
0
  rename = (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL);
551
552
0
  if (file_info_level == SMB_FILE_DISPOSITION_INFORMATION) {
553
0
    status = smb_check_file_disposition_info(
554
0
      fsp,
555
0
      (char *)state->data.data,
556
0
      state->data.length,
557
0
      &delete_on_close);
558
0
    if (tevent_req_nterror(req, status)) {
559
0
      return;
560
0
    }
561
0
  }
562
563
0
  if (!rename && !delete_on_close) {
564
0
    return;
565
0
  }
566
567
0
  state->lck = get_existing_share_mode_lock(state, fsp->file_id);
568
0
  if (state->lck == NULL) {
569
0
    tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
570
0
    return;
571
0
  }
572
573
0
  timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
574
0
  timeout = timeval_sum(&smb2req->request_time, &timeout);
575
576
0
  subreq = delay_for_handle_lease_break_send(state,
577
0
               state->ev,
578
0
               timeout,
579
0
               fsp,
580
0
               SEC_RIGHTS_DIR_ALL,
581
0
               rename,
582
0
               &state->lck);
583
0
  if (tevent_req_nomem(subreq, req)) {
584
0
    return;
585
0
  }
586
0
  if (tevent_req_is_in_progress(subreq)) {
587
0
    state->delay = true;
588
0
    tevent_req_set_callback(subreq,
589
0
          smbd_smb2_setinfo_lease_break_fsp_done,
590
0
          req);
591
0
    return;
592
0
  }
593
594
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
595
0
  TALLOC_FREE(subreq);
596
0
  if (tevent_req_nterror(req, status)) {
597
0
    return;
598
0
  }
599
0
}
600
601
static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq)
602
0
{
603
0
  struct tevent_req *req = tevent_req_callback_data(
604
0
    subreq, struct tevent_req);
605
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
606
0
    req, struct smbd_smb2_setinfo_state);
607
0
  int ret_size;
608
0
  NTSTATUS status;
609
0
  bool ok;
610
611
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
612
0
  TALLOC_FREE(subreq);
613
0
  if (tevent_req_nterror(req, status)) {
614
0
    return;
615
0
  }
616
617
  /*
618
   * Make sure we run as the user again
619
   */
620
0
  ok = change_to_user_and_service(
621
0
    state->smb2req->tcon->compat,
622
0
    state->smb2req->session->global->session_wire_id);
623
0
  if (!ok) {
624
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
625
0
    return;
626
0
  }
627
628
  /* Do the setinfo again under the lock. */
629
0
  status = smbd_do_setfilepathinfo(state->fsp->conn,
630
0
        state->smb2req->smb1req,
631
0
        state,
632
0
        state->file_info_level,
633
0
        state->fsp,
634
0
        &state->lck,
635
0
        state->fsp->fsp_name,
636
0
        (char *)state->data.data,
637
0
        state->data.length,
638
0
        &ret_size);
639
0
  TALLOC_FREE(state->lck);
640
0
  if (tevent_req_nterror(req, status)) {
641
0
    return;
642
0
  }
643
644
0
  tevent_req_done(req);
645
0
}
646
647
static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req)
648
0
{
649
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
650
0
    req, struct smbd_smb2_setinfo_state);
651
0
  struct tevent_req *subreq = NULL;
652
0
  struct timeval timeout;
653
0
  char *newname = NULL;
654
0
  bool overwrite = false;
655
0
  struct files_struct *fsp = state->fsp;
656
0
  struct files_struct *dst_dirfsp = NULL;
657
0
  struct smb_filename *smb_fname_dst = NULL;
658
0
  bool has_other_open;
659
0
  NTSTATUS status;
660
0
  NTSTATUS close_status;
661
662
0
  if (state->file_info_level != SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
663
0
    return;
664
0
  }
665
666
0
  if (state->rename_dst_check_done) {
667
0
    return;
668
0
  }
669
670
0
  status = smb2_parse_file_rename_information_dst(
671
0
    state,
672
0
    fsp->conn,
673
0
    state->smb2req->smb1req,
674
0
    (char *)state->data.data,
675
0
    state->data.length,
676
0
    fsp,
677
0
    fsp->fsp_name,
678
0
    &newname,
679
0
    &overwrite,
680
0
    &dst_dirfsp,
681
0
    &smb_fname_dst);
682
0
  if (tevent_req_nterror(req, status)) {
683
0
    return;
684
0
  }
685
686
0
  if (!VALID_STAT(smb_fname_dst->st)) {
687
    /* Doesn't exist, nothing to do here */
688
0
    return;
689
0
  }
690
0
  if (strequal(fsp->fsp_name->base_name, smb_fname_dst->base_name) &&
691
0
      strequal(fsp->fsp_name->stream_name, smb_fname_dst->stream_name))
692
0
  {
693
0
    return;
694
0
  }
695
0
  if (!overwrite) {
696
    /* Return the correct error */
697
0
    tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_COLLISION);
698
0
    return;
699
0
  }
700
701
0
  status = SMB_VFS_CREATE_FILE(
702
0
    fsp->conn,
703
0
    NULL,
704
0
    dst_dirfsp,
705
0
    smb_fname_dst,
706
0
    FILE_READ_ATTRIBUTES,
707
0
    FILE_SHARE_READ
708
0
    | FILE_SHARE_WRITE
709
0
    | FILE_SHARE_DELETE,
710
0
    FILE_OPEN,    /* create_disposition*/
711
0
    0,      /* create_options */
712
0
    FILE_ATTRIBUTE_NORMAL,
713
0
    INTERNAL_OPEN_ONLY, /* oplock_request */
714
0
    NULL,     /* lease */
715
0
    0,      /* allocation_size */
716
0
    0,      /* private_flags */
717
0
    NULL,     /* sd */
718
0
    NULL,     /* ea_list */
719
0
    &state->dstfsp,   /* result */
720
0
    NULL,     /* psbuf */
721
0
    NULL,
722
0
    NULL);      /* create context */
723
0
  if (!NT_STATUS_IS_OK(status)) {
724
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
725
      /* A race, file was deleted */
726
0
      return;
727
0
    }
728
0
    tevent_req_nterror(req, status);
729
0
    return;
730
0
  }
731
732
0
  state->lck = get_existing_share_mode_lock(state, state->dstfsp->file_id);
733
0
  if (state->lck == NULL) {
734
0
    close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE);
735
0
    if (!NT_STATUS_IS_OK(close_status)) {
736
0
      DBG_ERR("close_file_free failed\n");
737
0
    }
738
0
    return;
739
0
  }
740
741
0
  timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
742
0
  timeout = timeval_sum(&state->smb2req->request_time, &timeout);
743
744
0
  subreq = delay_for_handle_lease_break_send(state,
745
0
               state->ev,
746
0
               timeout,
747
0
               state->dstfsp,
748
0
               SEC_RIGHTS_DIR_ALL,
749
0
               false,
750
0
               &state->lck);
751
0
  if (subreq == NULL) {
752
0
    close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE);
753
0
    if (!NT_STATUS_IS_OK(close_status)) {
754
0
      DBG_ERR("close_file_free failed\n");
755
0
    }
756
0
    tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
757
0
    return;
758
0
  }
759
0
  if (tevent_req_is_in_progress(subreq)) {
760
0
    state->delay = true;
761
0
    tevent_req_set_callback(subreq,
762
0
          smbd_smb2_setinfo_rename_dst_delay_done,
763
0
          req);
764
0
    return;
765
0
  }
766
767
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
768
0
  TALLOC_FREE(subreq);
769
0
  if (!NT_STATUS_IS_OK(status)) {
770
0
    close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE);
771
0
    if (!NT_STATUS_IS_OK(close_status)) {
772
0
      DBG_ERR("close_file_free failed\n");
773
0
    }
774
0
    tevent_req_nterror(req, status);
775
0
    return;
776
0
  }
777
778
0
  has_other_open = has_other_nonposix_opens(state->lck, state->dstfsp);
779
0
  TALLOC_FREE(state->lck);
780
781
0
  status = close_file_free(NULL, &state->dstfsp, NORMAL_CLOSE);
782
0
  if (tevent_req_nterror(req, status)) {
783
0
    DBG_ERR("close_file_free failed\n");
784
0
    return;
785
0
  }
786
787
0
  if (has_other_open) {
788
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
789
0
    return;
790
0
  }
791
0
  state->rename_dst_check_done = true;
792
0
  return;
793
0
}
794
795
static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq)
796
0
{
797
0
  struct tevent_req *req = tevent_req_callback_data(
798
0
    subreq, struct tevent_req);
799
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
800
0
    req, struct smbd_smb2_setinfo_state);
801
0
  struct smbXsrv_session *session = state->smb2req->session;
802
0
  bool has_other_open;
803
0
  NTSTATUS close_status;
804
0
  NTSTATUS status;
805
0
  bool ok;
806
807
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
808
0
  TALLOC_FREE(subreq);
809
0
  if (!NT_STATUS_IS_OK(status)) {
810
0
    close_status = close_file_free(NULL,
811
0
                 &state->dstfsp,
812
0
                 NORMAL_CLOSE);
813
0
    if (!NT_STATUS_IS_OK(close_status)) {
814
0
      DBG_ERR("close_file_free failed\n");
815
0
    }
816
0
    tevent_req_nterror(req, status);
817
0
    return;
818
0
  }
819
820
  /*
821
   * Make sure we run as the user again
822
   */
823
0
  ok = change_to_user_and_service(state->dstfsp->conn,
824
0
          session->global->session_wire_id);
825
0
  if (!ok) {
826
0
    close_status = close_file_free(NULL,
827
0
                 &state->dstfsp,
828
0
                 NORMAL_CLOSE);
829
0
    if (!NT_STATUS_IS_OK(close_status)) {
830
0
      DBG_ERR("close_file_free failed\n");
831
0
    }
832
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
833
0
    return;
834
0
  }
835
836
0
  has_other_open = has_other_nonposix_opens(state->lck, state->dstfsp);
837
0
  TALLOC_FREE(state->lck);
838
839
0
  status = close_file_free(NULL, &state->dstfsp, NORMAL_CLOSE);
840
0
  if (tevent_req_nterror(req, status)) {
841
0
    DBG_ERR("close_file_free failed\n");
842
0
    return;
843
0
  }
844
845
0
  if (has_other_open) {
846
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
847
0
    return;
848
0
  }
849
850
  /*
851
   * We've finished breaking H-lease on the rename destination, now
852
   * trigger the fsp check.
853
   */
854
0
  state->rename_dst_check_done = true;
855
0
  smbd_smb2_setinfo_lease_break_check(req);
856
0
}
857
858
static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req)
859
0
{
860
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
861
0
    req, struct smbd_smb2_setinfo_state);
862
0
  struct tevent_req *subreq = NULL;
863
0
  struct timeval timeout;
864
0
  char *newname = NULL;
865
0
  bool overwrite = false;
866
0
  struct files_struct *fsp = state->fsp;
867
0
  struct files_struct *dst_parent_dirfsp = NULL;
868
0
  struct smb_filename *smb_fname_dst = NULL;
869
0
  NTSTATUS status;
870
871
0
  if (state->rename_dst_parent_check_done) {
872
0
    return;
873
0
  }
874
0
  state->rename_dst_parent_check_done = true;
875
876
0
  if (state->file_info_level != SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
877
0
    return;
878
0
  }
879
0
  if (is_named_stream(fsp->fsp_name)) {
880
0
    return;
881
0
  }
882
0
  status = smb2_parse_file_rename_information_dst(
883
0
    state,
884
0
    fsp->conn,
885
0
    state->smb2req->smb1req,
886
0
    (char *)state->data.data,
887
0
    state->data.length,
888
0
    fsp,
889
0
    fsp->fsp_name,
890
0
    &newname,
891
0
    &overwrite,
892
0
    &dst_parent_dirfsp,
893
0
    &smb_fname_dst);
894
0
  if (tevent_req_nterror(req, status)) {
895
0
    return;
896
0
  }
897
0
  SMB_ASSERT(dst_parent_dirfsp != NULL);
898
0
  state->dst_parent_dirfsp = dst_parent_dirfsp;
899
900
0
  state->lck = get_existing_share_mode_lock(state,
901
0
              dst_parent_dirfsp->file_id);
902
0
  if (state->lck == NULL) {
903
    /* No opens around */
904
0
    return;
905
0
  }
906
907
0
  timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
908
0
  timeout = timeval_sum(&state->smb2req->request_time, &timeout);
909
910
0
  subreq = delay_for_handle_lease_break_send(state,
911
0
               state->ev,
912
0
               timeout,
913
0
               dst_parent_dirfsp,
914
0
               SEC_STD_DELETE,
915
0
               false,
916
0
               &state->lck);
917
0
  if (tevent_req_nomem(subreq, req)) {
918
0
    return;
919
0
  }
920
0
  if (tevent_req_is_in_progress(subreq)) {
921
0
    state->delay = true;
922
0
    tevent_req_set_callback(
923
0
      subreq,
924
0
      smbd_smb2_setinfo_rename_dst_parent_delay_done,
925
0
      req);
926
0
    return;
927
0
  }
928
929
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
930
0
  TALLOC_FREE(subreq);
931
0
  if (tevent_req_nterror(req, status)) {
932
0
    return;
933
0
  }
934
935
0
  if (state->lck != NULL) {
936
0
    bool delete_open;
937
0
    bool detete_pending;
938
0
    bool ok;
939
940
0
    ok = has_delete_opens(dst_parent_dirfsp,
941
0
              state->lck,
942
0
              &delete_open,
943
0
              &detete_pending);
944
0
    TALLOC_FREE(state->lck);
945
0
    if (!ok) {
946
0
      tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
947
0
      return;
948
0
    }
949
950
0
    if (detete_pending) {
951
0
      tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
952
0
      return;
953
0
    }
954
0
    if (delete_open) {
955
0
      tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
956
0
      return;
957
0
    }
958
0
  }
959
960
0
  return;
961
0
}
962
963
static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
964
  struct tevent_req *subreq)
965
0
{
966
0
  struct tevent_req *req = tevent_req_callback_data(
967
0
    subreq, struct tevent_req);
968
0
  struct smbd_smb2_setinfo_state *state = tevent_req_data(
969
0
    req, struct smbd_smb2_setinfo_state);
970
0
  struct smbXsrv_session *session = state->smb2req->session;
971
0
  NTSTATUS status;
972
0
  bool ok;
973
974
0
  status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
975
0
  TALLOC_FREE(subreq);
976
0
  if (tevent_req_nterror(req, status)) {
977
0
    return;
978
0
  }
979
980
  /*
981
   * Make sure we run as the user again
982
   */
983
0
  ok = change_to_user_and_service(state->fsp->conn,
984
0
          session->global->session_wire_id);
985
0
  if (!ok) {
986
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
987
0
    return;
988
0
  }
989
990
0
  if (state->lck != NULL) {
991
0
    bool delete_open;
992
0
    bool detete_pending;
993
994
0
    ok = has_delete_opens(state->dst_parent_dirfsp,
995
0
              state->lck,
996
0
              &delete_open,
997
0
              &detete_pending);
998
0
    TALLOC_FREE(state->lck);
999
0
    if (!ok) {
1000
0
      tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
1001
0
      return;
1002
0
    }
1003
1004
0
    if (detete_pending) {
1005
0
      tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
1006
0
      return;
1007
0
    }
1008
0
    if (delete_open) {
1009
0
      tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
1010
0
      return;
1011
0
    }
1012
0
  }
1013
1014
  /*
1015
   * We've finished breaking H-lease on the rename destination, now
1016
   * trigger the fsp check.
1017
   */
1018
0
  state->rename_dst_parent_check_done = true;
1019
0
  smbd_smb2_setinfo_lease_break_check(req);
1020
0
}
1021
1022
static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
1023
0
{
1024
0
  NTSTATUS status;
1025
1026
0
  if (tevent_req_is_nterror(req, &status)) {
1027
0
    tevent_req_received(req);
1028
0
    return status;
1029
0
  }
1030
1031
0
  tevent_req_received(req);
1032
0
  return NT_STATUS_OK;
1033
0
}