Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/notify.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   change notify handling
4
   Copyright (C) Andrew Tridgell 2000
5
   Copyright (C) Jeremy Allison 1994-1998
6
   Copyright (C) Volker Lendecke 2007
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 "../librpc/gen_ndr/ndr_notify.h"
26
#include "librpc/gen_ndr/ndr_file_id.h"
27
#include "libcli/security/privileges.h"
28
#include "libcli/security/security.h"
29
30
struct notify_change_event {
31
  struct timespec when;
32
  uint32_t action;
33
  const char *name;
34
};
35
36
struct notify_change_buf {
37
  /*
38
   * Filters for reinitializing after notifyd has been restarted
39
   */
40
  uint32_t filter;
41
  uint32_t subdir_filter;
42
43
  /*
44
   * If no requests are pending, changes are queued here. Simple array,
45
   * we only append.
46
   */
47
48
  uint32_t max_buffer_size;
49
50
  /*
51
   * num_changes == -1 means that we have got a catch-all change, when
52
   * asked we just return NT_STATUS_OK without specific changes.
53
   */
54
  int num_changes;
55
  struct notify_change_event *changes;
56
57
  /*
58
   * If no changes are around requests are queued here. Using a linked
59
   * list, because we have to append at the end and delete from the top.
60
   */
61
  struct notify_change_request *requests;
62
};
63
64
struct notify_change_request {
65
  struct notify_change_request *prev, *next;
66
  struct files_struct *fsp; /* backpointer for cancel by mid */
67
  struct smb_request *req;
68
  uint32_t filter;
69
  uint32_t max_param;
70
  void (*reply_fn)(struct smb_request *req,
71
       NTSTATUS error_code,
72
       uint8_t *buf, size_t len);
73
  struct notify_mid_map *mid_map;
74
  void *backend_data;
75
};
76
77
static void notify_fsp(files_struct *fsp, struct timespec when,
78
           uint32_t action, const char *name);
79
80
bool change_notify_fsp_has_changes(struct files_struct *fsp)
81
0
{
82
0
  if (fsp == NULL) {
83
0
    return false;
84
0
  }
85
86
0
  if (fsp->notify == NULL) {
87
0
    return false;
88
0
  }
89
90
0
  if (fsp->notify->num_changes == 0) {
91
0
    return false;
92
0
  }
93
94
0
  return true;
95
0
}
96
97
/*
98
 * For NTCancel, we need to find the notify_change_request indexed by
99
 * mid. Separate list here.
100
 */
101
102
struct notify_mid_map {
103
  struct notify_mid_map *prev, *next;
104
  struct notify_change_request *req;
105
  uint64_t mid;
106
};
107
108
static bool notify_change_record_identical(struct notify_change_event *c1,
109
             struct notify_change_event *c2)
110
0
{
111
  /* Note this is deliberately case sensitive. */
112
0
  if (c1->action == c2->action &&
113
0
      strcmp(c1->name, c2->name) == 0) {
114
0
    return True;
115
0
  }
116
0
  return False;
117
0
}
118
119
static int compare_notify_change_events(const void *p1, const void *p2)
120
0
{
121
0
  const struct notify_change_event *e1 = p1;
122
0
  const struct notify_change_event *e2 = p2;
123
124
0
  return timespec_compare(&e1->when, &e2->when);
125
0
}
126
127
static bool notify_marshall_changes(int num_changes,
128
        uint32_t max_offset,
129
        struct notify_change_event *changes,
130
        DATA_BLOB *final_blob)
131
0
{
132
0
  int i;
133
134
0
  if (num_changes == -1) {
135
0
    return false;
136
0
  }
137
138
  /*
139
   * Sort the notifies by timestamp when the event happened to avoid
140
   * coalescing and thus dropping events.
141
   */
142
143
0
  qsort(changes, num_changes,
144
0
        sizeof(*changes), compare_notify_change_events);
145
146
0
  for (i=0; i<num_changes; i++) {
147
0
    enum ndr_err_code ndr_err;
148
0
    struct notify_change_event *c;
149
0
    struct FILE_NOTIFY_INFORMATION m;
150
0
    DATA_BLOB blob;
151
0
    uint16_t pad = 0;
152
153
    /* Coalesce any identical records. */
154
0
    while (i+1 < num_changes &&
155
0
      notify_change_record_identical(&changes[i],
156
0
            &changes[i+1])) {
157
0
      i++;
158
0
    }
159
160
0
    c = &changes[i];
161
162
0
    m.FileName1 = c->name;
163
0
    m.FileNameLength = strlen_m(c->name)*2;
164
0
    m.Action = c->action;
165
166
0
    m._pad = data_blob_null;
167
168
    /*
169
     * Offset to next entry, only if there is one
170
     */
171
172
0
    if (i == (num_changes-1)) {
173
0
      m.NextEntryOffset = 0;
174
0
    } else {
175
0
      if ((m.FileNameLength % 4) == 2) {
176
0
        m._pad = data_blob_const(&pad, 2);
177
0
      }
178
0
      m.NextEntryOffset =
179
0
        ndr_size_FILE_NOTIFY_INFORMATION(&m, 0);
180
0
    }
181
182
0
    ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &m,
183
0
      (ndr_push_flags_fn_t)ndr_push_FILE_NOTIFY_INFORMATION);
184
0
    if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
185
0
      return false;
186
0
    }
187
188
0
    if (DEBUGLEVEL >= 10) {
189
0
      NDR_PRINT_DEBUG(FILE_NOTIFY_INFORMATION, &m);
190
0
    }
191
192
0
    if (!data_blob_append(talloc_tos(), final_blob,
193
0
              blob.data, blob.length)) {
194
0
      data_blob_free(&blob);
195
0
      return false;
196
0
    }
197
198
0
    data_blob_free(&blob);
199
200
0
    if (final_blob->length > max_offset) {
201
      /* Too much data for client. */
202
0
      DEBUG(10, ("Client only wanted %d bytes, trying to "
203
0
           "marshall %d bytes\n", (int)max_offset,
204
0
           (int)final_blob->length));
205
0
      return False;
206
0
    }
207
0
  }
208
209
0
  return True;
210
0
}
211
212
/****************************************************************************
213
 Setup the common parts of the return packet and send it.
214
*****************************************************************************/
215
216
void change_notify_reply(struct smb_request *req,
217
       NTSTATUS error_code,
218
       uint32_t max_param,
219
       struct notify_change_buf *notify_buf,
220
       void (*reply_fn)(struct smb_request *req,
221
            NTSTATUS error_code,
222
            uint8_t *buf, size_t len))
223
0
{
224
0
  DATA_BLOB blob = data_blob_null;
225
226
0
  if (!NT_STATUS_IS_OK(error_code)) {
227
0
    reply_fn(req, error_code, NULL, 0);
228
0
    return;
229
0
  }
230
231
0
  if (notify_buf == NULL) {
232
0
    reply_fn(req, NT_STATUS_OK, NULL, 0);
233
0
    return;
234
0
  }
235
236
0
  max_param = MIN(max_param, notify_buf->max_buffer_size);
237
238
0
  if (!notify_marshall_changes(notify_buf->num_changes, max_param,
239
0
          notify_buf->changes, &blob)) {
240
    /*
241
     * We exceed what the client is willing to accept. Send
242
     * nothing.
243
     */
244
0
    data_blob_free(&blob);
245
0
  }
246
247
0
  reply_fn(req, NT_STATUS_OK, blob.data, blob.length);
248
249
0
  data_blob_free(&blob);
250
251
0
  TALLOC_FREE(notify_buf->changes);
252
0
  notify_buf->num_changes = 0;
253
0
}
254
255
struct notify_fsp_state {
256
  struct files_struct *notified_fsp;
257
  struct timespec when;
258
  const struct notify_event *e;
259
};
260
261
static struct files_struct *notify_fsp_cb(struct files_struct *fsp,
262
            void *private_data)
263
0
{
264
0
  struct notify_fsp_state *state = private_data;
265
266
0
  if (fsp == state->notified_fsp) {
267
0
    DBG_DEBUG("notify_callback called for %s\n", fsp_str_dbg(fsp));
268
0
    notify_fsp(fsp, state->when, state->e->action, state->e->path);
269
0
    return fsp;
270
0
  }
271
272
0
  return NULL;
273
0
}
274
275
void notify_callback(struct smbd_server_connection *sconn,
276
         void *private_data, struct timespec when,
277
         const struct notify_event *e)
278
0
{
279
0
  struct notify_fsp_state state = {
280
0
    .notified_fsp = private_data, .when = when, .e = e
281
0
  };
282
0
  files_forall(sconn, notify_fsp_cb, &state);
283
0
}
284
285
NTSTATUS change_notify_create(struct files_struct *fsp,
286
            uint32_t max_buffer_size,
287
            uint32_t filter,
288
            bool recursive)
289
0
{
290
0
  size_t len = fsp_fullbasepath(fsp, NULL, 0);
291
0
  char fullpath[len+1];
292
0
  NTSTATUS status = NT_STATUS_NOT_IMPLEMENTED;
293
294
  /*
295
   * Setting a changenotify needs READ/LIST access
296
   * on the directory handle.
297
   */
298
0
  status = check_any_access_fsp(fsp, SEC_DIR_LIST);
299
0
  if (!NT_STATUS_IS_OK(status)) {
300
0
    return status;
301
0
  }
302
303
0
  if (fsp->notify != NULL) {
304
0
    DEBUG(1, ("change_notify_create: fsp->notify != NULL, "
305
0
        "fname = %s\n", fsp_str_dbg(fsp)));
306
0
    return NT_STATUS_INVALID_PARAMETER;
307
0
  }
308
309
0
  if (!(fsp->notify = talloc_zero(NULL, struct notify_change_buf))) {
310
0
    DEBUG(0, ("talloc failed\n"));
311
0
    return NT_STATUS_NO_MEMORY;
312
0
  }
313
0
  fsp->notify->filter = filter;
314
0
  fsp->notify->subdir_filter = recursive ? filter : 0;
315
0
  fsp->notify->max_buffer_size = max_buffer_size;
316
317
0
  fsp_fullbasepath(fsp, fullpath, sizeof(fullpath));
318
319
0
  if ((fsp->notify->filter != 0) ||
320
0
      (fsp->notify->subdir_filter != 0)) {
321
0
    status = notify_add(fsp->conn->sconn->notify_ctx,
322
0
            fullpath, fsp->notify->filter,
323
0
            fsp->notify->subdir_filter, fsp);
324
0
  }
325
326
0
  return status;
327
0
}
328
329
NTSTATUS change_notify_add_request(struct smb_request *req,
330
        uint32_t max_param,
331
        uint32_t filter, bool recursive,
332
        struct files_struct *fsp,
333
        void (*reply_fn)(struct smb_request *req,
334
          NTSTATUS error_code,
335
          uint8_t *buf, size_t len))
336
0
{
337
0
  struct notify_change_request *request = NULL;
338
0
  struct notify_mid_map *map = NULL;
339
0
  struct smbd_server_connection *sconn = req->sconn;
340
341
0
  DEBUG(10, ("change_notify_add_request: Adding request for %s: "
342
0
       "max_param = %d\n", fsp_str_dbg(fsp), (int)max_param));
343
344
0
  if (!(request = talloc(NULL, struct notify_change_request))
345
0
      || !(map = talloc(request, struct notify_mid_map))) {
346
0
    TALLOC_FREE(request);
347
0
    return NT_STATUS_NO_MEMORY;
348
0
  }
349
350
0
  request->mid_map = map;
351
0
  map->req = request;
352
353
0
  request->req = talloc_move(request, &req);
354
0
  request->max_param = max_param;
355
0
  request->filter = filter;
356
0
  request->fsp = fsp;
357
0
  request->reply_fn = reply_fn;
358
0
  request->backend_data = NULL;
359
360
0
  DLIST_ADD_END(fsp->notify->requests, request);
361
362
0
  map->mid = request->req->mid;
363
0
  DLIST_ADD(sconn->notify_mid_maps, map);
364
365
0
  return NT_STATUS_OK;
366
0
}
367
368
static void change_notify_remove_request(struct smbd_server_connection *sconn,
369
           struct notify_change_request *remove_req)
370
0
{
371
0
  files_struct *fsp;
372
0
  struct notify_change_request *req;
373
374
  /*
375
   * Paranoia checks, the fsp referenced must must have the request in
376
   * its list of pending requests
377
   */
378
379
0
  fsp = remove_req->fsp;
380
0
  SMB_ASSERT(fsp->notify != NULL);
381
382
0
  for (req = fsp->notify->requests; req; req = req->next) {
383
0
    if (req == remove_req) {
384
0
      break;
385
0
    }
386
0
  }
387
388
0
  if (req == NULL) {
389
0
    smb_panic("notify_req not found in fsp's requests");
390
0
  }
391
392
0
  DLIST_REMOVE(fsp->notify->requests, req);
393
0
  DLIST_REMOVE(sconn->notify_mid_maps, req->mid_map);
394
0
  TALLOC_FREE(req);
395
0
}
396
397
static void smbd_notify_cancel_by_map(struct notify_mid_map *map)
398
0
{
399
0
  struct smb_request *smbreq = map->req->req;
400
0
  struct smbd_server_connection *sconn = smbreq->sconn;
401
0
  struct smbd_smb2_request *smb2req = smbreq->smb2req;
402
0
  NTSTATUS notify_status = NT_STATUS_CANCELLED;
403
404
0
  if (smb2req != NULL) {
405
0
    NTSTATUS sstatus;
406
407
0
    if (smb2req->session == NULL) {
408
0
      sstatus = NT_STATUS_USER_SESSION_DELETED;
409
0
    } else {
410
0
      sstatus = smb2req->session->status;
411
0
    }
412
413
0
    if (NT_STATUS_EQUAL(sstatus, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
414
0
      sstatus = NT_STATUS_OK;
415
0
    }
416
417
0
    if (!NT_STATUS_IS_OK(sstatus)) {
418
0
      notify_status = NT_STATUS_NOTIFY_CLEANUP;
419
0
    } else if (smb2req->tcon == NULL) {
420
0
      notify_status = NT_STATUS_NOTIFY_CLEANUP;
421
0
    } else if (!NT_STATUS_IS_OK(smb2req->tcon->status)) {
422
0
      notify_status = NT_STATUS_NOTIFY_CLEANUP;
423
0
    }
424
0
  }
425
426
0
  change_notify_reply(smbreq, notify_status,
427
0
          0, NULL, map->req->reply_fn);
428
0
  change_notify_remove_request(sconn, map->req);
429
0
}
430
431
/****************************************************************************
432
 Delete entries by mid from the change notify pending queue. Always send reply.
433
*****************************************************************************/
434
435
bool remove_pending_change_notify_requests_by_mid(
436
  struct smbd_server_connection *sconn, uint64_t mid)
437
0
{
438
0
  struct notify_mid_map *map;
439
440
0
  for (map = sconn->notify_mid_maps; map; map = map->next) {
441
0
    if (map->mid == mid) {
442
0
      break;
443
0
    }
444
0
  }
445
446
0
  if (map == NULL) {
447
0
    return false;
448
0
  }
449
450
0
  smbd_notify_cancel_by_map(map);
451
0
  return true;
452
0
}
453
454
void smbd_notify_cancel_by_smbreq(const struct smb_request *smbreq)
455
0
{
456
0
  struct smbd_server_connection *sconn = smbreq->sconn;
457
0
  struct notify_mid_map *map;
458
459
0
  for (map = sconn->notify_mid_maps; map; map = map->next) {
460
0
    if (map->req->req == smbreq) {
461
0
      break;
462
0
    }
463
0
  }
464
465
0
  if (map == NULL) {
466
0
    return;
467
0
  }
468
469
0
  smbd_notify_cancel_by_map(map);
470
0
}
471
472
static struct files_struct *smbd_notify_cancel_deleted_fn(
473
  struct files_struct *fsp, void *private_data)
474
0
{
475
0
  struct file_id *fid = talloc_get_type_abort(
476
0
    private_data, struct file_id);
477
478
0
  if (file_id_equal(&fsp->file_id, fid)) {
479
0
    remove_pending_change_notify_requests_by_fid(
480
0
      fsp, NT_STATUS_DELETE_PENDING);
481
0
  }
482
0
  return NULL;
483
0
}
484
485
void smbd_notify_cancel_deleted(struct messaging_context *msg,
486
        void *private_data, uint32_t msg_type,
487
        struct server_id server_id, DATA_BLOB *data)
488
0
{
489
0
  struct smbd_server_connection *sconn = talloc_get_type_abort(
490
0
    private_data, struct smbd_server_connection);
491
0
  struct file_id *fid;
492
0
  enum ndr_err_code ndr_err;
493
494
0
  fid = talloc(talloc_tos(), struct file_id);
495
0
  if (fid == NULL) {
496
0
    DEBUG(1, ("talloc failed\n"));
497
0
    return;
498
0
  }
499
500
0
  ndr_err = ndr_pull_struct_blob_all(
501
0
    data, fid, fid, (ndr_pull_flags_fn_t)ndr_pull_file_id);
502
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
503
0
    DEBUG(10, ("%s: ndr_pull_file_id failed: %s\n", __func__,
504
0
         ndr_errstr(ndr_err)));
505
0
    goto done;
506
0
  }
507
508
0
  files_forall(sconn, smbd_notify_cancel_deleted_fn, fid);
509
510
0
done:
511
0
  TALLOC_FREE(fid);
512
0
}
513
514
static struct files_struct *smbd_notifyd_reregister(struct files_struct *fsp,
515
                void *private_data)
516
0
{
517
0
  DBG_DEBUG("reregister %s\n", fsp_str_dbg(fsp));
518
519
0
  if ((fsp->conn->sconn->notify_ctx != NULL) &&
520
0
      (fsp->notify != NULL) &&
521
0
      ((fsp->notify->filter != 0) ||
522
0
       (fsp->notify->subdir_filter != 0))) {
523
0
    size_t len = fsp_fullbasepath(fsp, NULL, 0);
524
0
    char fullpath[len+1];
525
526
0
    NTSTATUS status;
527
528
0
    fsp_fullbasepath(fsp, fullpath, sizeof(fullpath));
529
530
0
    status = notify_add(fsp->conn->sconn->notify_ctx,
531
0
            fullpath, fsp->notify->filter,
532
0
            fsp->notify->subdir_filter, fsp);
533
0
    if (!NT_STATUS_IS_OK(status)) {
534
0
      DBG_DEBUG("notify_add failed: %s\n",
535
0
          nt_errstr(status));
536
0
    }
537
0
  }
538
0
  return NULL;
539
0
}
540
541
void smbd_notifyd_restarted(struct messaging_context *msg,
542
          void *private_data, uint32_t msg_type,
543
          struct server_id server_id, DATA_BLOB *data)
544
0
{
545
0
  struct smbd_server_connection *sconn = talloc_get_type_abort(
546
0
    private_data, struct smbd_server_connection);
547
548
0
  TALLOC_FREE(sconn->notify_ctx);
549
550
0
  sconn->notify_ctx = notify_init(sconn, sconn->msg_ctx,
551
0
          sconn, notify_callback);
552
0
  if (sconn->notify_ctx == NULL) {
553
0
    DBG_DEBUG("notify_init failed\n");
554
0
    return;
555
0
  }
556
557
0
  files_forall(sconn, smbd_notifyd_reregister, sconn->notify_ctx);
558
0
}
559
560
/****************************************************************************
561
 Delete entries by fnum from the change notify pending queue.
562
*****************************************************************************/
563
564
void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
565
              NTSTATUS status)
566
0
{
567
0
  if (fsp->notify == NULL) {
568
0
    return;
569
0
  }
570
571
0
  while (fsp->notify->requests != NULL) {
572
0
    change_notify_reply(fsp->notify->requests->req,
573
0
            status, 0, NULL,
574
0
            fsp->notify->requests->reply_fn);
575
0
    change_notify_remove_request(fsp->conn->sconn,
576
0
               fsp->notify->requests);
577
0
  }
578
0
}
579
580
void notify_fname(struct connection_struct *conn,
581
      uint32_t action,
582
      uint32_t filter,
583
      const struct smb_filename *smb_fname,
584
      const struct smb2_lease *lease)
585
0
{
586
0
  struct notify_context *notify_ctx = conn->sconn->notify_ctx;
587
0
  const char *path = smb_fname->base_name;
588
589
0
  if (action & NOTIFY_ACTION_DIRLEASE_BREAK) {
590
0
    contend_dirleases(conn, smb_fname, lease);
591
0
  }
592
0
  action &= ~NOTIFY_ACTION_DIRLEASE_BREAK;
593
0
  if (action == 0) {
594
0
    return;
595
0
  }
596
597
0
  if (path[0] == '.' && path[1] == '/') {
598
0
    path += 2;
599
0
  }
600
601
0
  notify_trigger(notify_ctx, action, filter, conn->connectpath, path);
602
0
}
603
604
static bool user_can_stat_name_under_fsp(files_struct *fsp, const char *name)
605
0
{
606
0
  uint32_t rights;
607
0
  struct smb_filename *fname = NULL;
608
0
  char *filepath = NULL;
609
0
  NTSTATUS status;
610
0
  char *p = NULL;
611
612
  /*
613
   * Assume we get filepath (relative to the share)
614
   * like this:
615
   *
616
   *  'dir1/dir2/dir3/file'
617
   *
618
   * We start with LIST and TRAVERSE on the
619
   * direct parent ('dir1/dir2/dir3')
620
   *
621
   * Then we switch to just TRAVERSE for
622
   * the rest: 'dir1/dir2', 'dir1', '.'
623
   *
624
   * For a file in the share root, we'll have
625
   *  'file'
626
   * and would just check '.' with LIST and TRAVERSE.
627
   *
628
   * It's important to always check '.' as the last step,
629
   * which means we check the permissions of the share root
630
   * directory.
631
   */
632
633
0
  if (ISDOT(fsp->fsp_name->base_name)) {
634
0
    filepath = talloc_strdup(talloc_tos(), name);
635
0
  } else {
636
0
    filepath = talloc_asprintf(talloc_tos(),
637
0
      "%s/%s",
638
0
      fsp->fsp_name->base_name,
639
0
      name);
640
0
  }
641
0
  if (filepath == NULL) {
642
0
    DBG_ERR("Memory allocation failed\n");
643
0
    return false;
644
0
  }
645
646
0
  rights = SEC_DIR_LIST|SEC_DIR_TRAVERSE;
647
0
  p = strrchr_m(filepath, '/');
648
  /*
649
   * Check each path component, excluding the share root.
650
   *
651
   * We could check all components including root using
652
   * a do { .. } while() loop, but IMHO the logic is clearer
653
   * having the share root check separately afterwards.
654
   */
655
0
  while (p != NULL) {
656
0
    *p = '\0';
657
0
    status = synthetic_pathref(talloc_tos(),
658
0
             fsp->conn->cwd_fsp,
659
0
             filepath,
660
0
             NULL,
661
0
             NULL,
662
0
             0,
663
0
             0,
664
0
             &fname);
665
0
    if (!NT_STATUS_IS_OK(status)) {
666
0
      int dbg_lvl = DBGLVL_ERR;
667
0
      if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
668
0
        dbg_lvl = DBGLVL_DEBUG;
669
0
      }
670
0
      DBG_PREFIX(dbg_lvl, (
671
0
           "synthetic_pathref failed for %s, error %s\n",
672
0
           filepath,
673
0
           nt_errstr(status)));
674
0
      TALLOC_FREE(fname);
675
0
      TALLOC_FREE(filepath);
676
0
      return false;
677
0
    }
678
679
0
    status = smbd_check_access_rights_fsp(fsp->conn->cwd_fsp,
680
0
              fname->fsp,
681
0
              false,
682
0
              rights);
683
0
    if (!NT_STATUS_IS_OK(status)) {
684
0
      DBG_DEBUG("Access rights for %s/%s: %s\n",
685
0
          fsp->conn->connectpath,
686
0
          filepath,
687
0
          nt_errstr(status));
688
0
      TALLOC_FREE(fname);
689
0
      TALLOC_FREE(filepath);
690
0
      return false;
691
0
    }
692
693
0
    TALLOC_FREE(fname);
694
0
    rights = SEC_DIR_TRAVERSE;
695
0
    p = strrchr_m(filepath, '/');
696
0
  }
697
698
0
  TALLOC_FREE(filepath);
699
700
  /* Finally check share root. */
701
0
  filepath = talloc_strdup(talloc_tos(), ".");
702
0
  if (filepath == NULL) {
703
0
    DBG_ERR("Memory allocation failed\n");
704
0
    return false;
705
0
  }
706
0
  status = synthetic_pathref(talloc_tos(),
707
0
           fsp->conn->cwd_fsp,
708
0
           filepath,
709
0
           NULL,
710
0
           NULL,
711
0
           0,
712
0
           0,
713
0
           &fname);
714
0
  if (!NT_STATUS_IS_OK(status)) {
715
0
    DBG_ERR("synthetic_pathref failed for %s, error %s\n",
716
0
      filepath,
717
0
      nt_errstr(status));
718
0
    TALLOC_FREE(fname);
719
0
    TALLOC_FREE(filepath);
720
0
    return false;
721
0
  }
722
0
  status = smbd_check_access_rights_fsp(fsp->conn->cwd_fsp,
723
0
            fname->fsp,
724
0
            false,
725
0
            rights);
726
0
  if (!NT_STATUS_IS_OK(status)) {
727
0
    DBG_DEBUG("TRAVERSE access rights for %s failed with %s\n",
728
0
        fsp->conn->connectpath,
729
0
        nt_errstr(status));
730
0
    TALLOC_FREE(fname);
731
0
    TALLOC_FREE(filepath);
732
0
    return false;
733
0
  }
734
0
  TALLOC_FREE(fname);
735
0
  TALLOC_FREE(filepath);
736
0
  return true;
737
0
}
738
739
static void notify_fsp(files_struct *fsp, struct timespec when,
740
           uint32_t action, const char *name)
741
0
{
742
0
  struct notify_change_event *change, *changes;
743
0
  char *tmp;
744
745
0
  if (fsp->notify == NULL) {
746
    /*
747
     * Nobody is waiting, don't queue
748
     */
749
0
    return;
750
0
  }
751
752
0
  if (lp_honor_change_notify_privilege(SNUM(fsp->conn))) {
753
0
    bool has_sec_change_notify_privilege;
754
0
    bool expose = false;
755
756
0
    has_sec_change_notify_privilege = security_token_has_privilege(
757
0
      fsp->conn->session_info->security_token,
758
0
      SEC_PRIV_CHANGE_NOTIFY);
759
760
0
    if (has_sec_change_notify_privilege) {
761
0
      expose = true;
762
0
    } else {
763
0
      bool ok;
764
765
0
      ok = become_user_without_service_by_fsp(fsp);
766
0
      if (ok) {
767
0
        expose = user_can_stat_name_under_fsp(fsp, name);
768
0
        unbecome_user_without_service();
769
0
      }
770
0
    }
771
0
    DBG_DEBUG("has_sec_change_notify_privilege=%s "
772
0
        "expose=%s for %s notify %s\n",
773
0
        has_sec_change_notify_privilege ? "true" : "false",
774
0
        expose ? "true" : "false",
775
0
        fsp_str_dbg(fsp),
776
0
        name);
777
0
    if (!expose) {
778
0
      return;
779
0
    }
780
0
  }
781
782
  /*
783
   * Someone has triggered a notify previously, queue the change for
784
   * later.
785
   */
786
787
0
  if ((fsp->notify->num_changes > 1000) || (name == NULL)) {
788
    /*
789
     * The real number depends on the client buf, just provide a
790
     * guard against a DoS here.  If name == NULL the CN backend is
791
     * alerting us to a problem.  Possibly dropped events.  Clear
792
     * queued changes and send the catch-all response to the client
793
     * if a request is pending.
794
     */
795
0
    TALLOC_FREE(fsp->notify->changes);
796
0
    fsp->notify->num_changes = -1;
797
0
    if (fsp->notify->requests != NULL) {
798
0
      change_notify_reply(fsp->notify->requests->req,
799
0
              NT_STATUS_OK,
800
0
              fsp->notify->requests->max_param,
801
0
              fsp->notify,
802
0
              fsp->notify->requests->reply_fn);
803
0
      change_notify_remove_request(fsp->conn->sconn,
804
0
                 fsp->notify->requests);
805
0
    }
806
0
    return;
807
0
  }
808
809
  /* If we've exceeded the server side queue or received a NULL name
810
   * from the underlying CN implementation, don't queue up any more
811
   * requests until we can send a catch-all response to the client */
812
0
  if (fsp->notify->num_changes == -1) {
813
0
    return;
814
0
  }
815
816
0
  if (!(changes = talloc_realloc(
817
0
          fsp->notify, fsp->notify->changes,
818
0
          struct notify_change_event,
819
0
          fsp->notify->num_changes+1))) {
820
0
    DEBUG(0, ("talloc_realloc failed\n"));
821
0
    return;
822
0
  }
823
824
0
  fsp->notify->changes = changes;
825
826
0
  change = &(fsp->notify->changes[fsp->notify->num_changes]);
827
828
0
  if (!(tmp = talloc_strdup(changes, name))) {
829
0
    DEBUG(0, ("talloc_strdup failed\n"));
830
0
    return;
831
0
  }
832
833
0
  string_replace(tmp, '/', '\\');
834
0
  change->name = tmp;
835
836
0
  change->when = when;
837
0
  change->action = action;
838
0
  fsp->notify->num_changes += 1;
839
840
0
  if (fsp->notify->requests == NULL) {
841
    /*
842
     * Nobody is waiting, so don't send anything. The ot
843
     */
844
0
    return;
845
0
  }
846
847
0
  if (action == NOTIFY_ACTION_OLD_NAME) {
848
    /*
849
     * We have to send the two rename events in one reply. So hold
850
     * the first part back.
851
     */
852
0
    return;
853
0
  }
854
855
  /*
856
   * Someone is waiting for the change, trigger the reply immediately.
857
   *
858
   * TODO: do we have to walk the lists of requests pending?
859
   */
860
861
0
  change_notify_reply(fsp->notify->requests->req,
862
0
          NT_STATUS_OK,
863
0
          fsp->notify->requests->max_param,
864
0
          fsp->notify,
865
0
          fsp->notify->requests->reply_fn);
866
867
0
  change_notify_remove_request(fsp->conn->sconn, fsp->notify->requests);
868
0
}
869
870
char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32_t filter)
871
0
{
872
0
  char *result = NULL;
873
874
0
  result = talloc_strdup(mem_ctx, "");
875
876
0
  if (filter & FILE_NOTIFY_CHANGE_FILE_NAME) {
877
0
    talloc_asprintf_addbuf(&result, "FILE_NAME|");
878
0
  }
879
0
  if (filter & FILE_NOTIFY_CHANGE_DIR_NAME) {
880
0
    talloc_asprintf_addbuf(&result, "DIR_NAME|");
881
0
  }
882
0
  if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES) {
883
0
    talloc_asprintf_addbuf(&result, "ATTRIBUTES|");
884
0
  }
885
0
  if (filter & FILE_NOTIFY_CHANGE_SIZE) {
886
0
    talloc_asprintf_addbuf(&result, "SIZE|");
887
0
  }
888
0
  if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE) {
889
0
    talloc_asprintf_addbuf(&result, "LAST_WRITE|");
890
0
  }
891
0
  if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS) {
892
0
    talloc_asprintf_addbuf(&result, "LAST_ACCESS|");
893
0
  }
894
0
  if (filter & FILE_NOTIFY_CHANGE_CREATION) {
895
0
    talloc_asprintf_addbuf(&result, "CREATION|");
896
0
  }
897
0
  if (filter & FILE_NOTIFY_CHANGE_EA) {
898
0
    talloc_asprintf_addbuf(&result, "EA|");
899
0
  }
900
0
  if (filter & FILE_NOTIFY_CHANGE_SECURITY) {
901
0
    talloc_asprintf_addbuf(&result, "SECURITY|");
902
0
  }
903
0
  if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME) {
904
0
    talloc_asprintf_addbuf(&result, "STREAM_NAME|");
905
0
  }
906
0
  if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE) {
907
0
    talloc_asprintf_addbuf(&result, "STREAM_SIZE|");
908
0
  }
909
0
  if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE) {
910
0
    talloc_asprintf_addbuf(&result, "STREAM_WRITE|");
911
0
  }
912
913
0
  if (result == NULL) {
914
0
    return NULL;
915
0
  }
916
917
0
  if (*result == '\0') return result;
918
919
0
  result[strlen(result)-1] = '\0';
920
0
  return result;
921
0
}
922
923
struct sys_notify_context *sys_notify_context_create(TALLOC_CTX *mem_ctx,
924
                 struct tevent_context *ev)
925
0
{
926
0
  struct sys_notify_context *ctx;
927
928
0
  if (!(ctx = talloc(mem_ctx, struct sys_notify_context))) {
929
0
    DEBUG(0, ("talloc failed\n"));
930
0
    return NULL;
931
0
  }
932
933
0
  ctx->ev = ev;
934
  ctx->private_data = NULL;
935
0
  return ctx;
936
0
}