Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/file_access.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Check access to files based on security descriptors.
4
   Copyright (C) Jeremy Allison 2005-2006.
5
   Copyright (C) Michael Adam 2007.
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "system/filesys.h"
23
#include "../libcli/security/security.h"
24
#include "../librpc/gen_ndr/ndr_security.h"
25
#include "smbd/smbd.h"
26
#include "source3/smbd/dir.h"
27
28
#undef  DBGC_CLASS
29
0
#define DBGC_CLASS DBGC_ACLS
30
31
/****************************************************************************
32
 Actually emulate the in-kernel access checking for delete access. We need
33
 this to successfully return ACCESS_DENIED on a file open for delete access.
34
****************************************************************************/
35
36
bool can_delete_file_in_directory(connection_struct *conn,
37
      struct files_struct *dirfsp,
38
      const struct smb_filename *smb_fname)
39
0
{
40
0
  struct smb_filename *smb_fname_parent = NULL;
41
0
  bool ret;
42
0
  NTSTATUS status;
43
44
0
  if (!CAN_WRITE(conn)) {
45
0
    return False;
46
0
  }
47
48
0
  if (!lp_acl_check_permissions(SNUM(conn))) {
49
    /* This option means don't check. */
50
0
    return true;
51
0
  }
52
53
0
  if (get_current_uid(conn) == (uid_t)0) {
54
    /* I'm sorry sir, I didn't know you were root... */
55
0
    return true;
56
0
  }
57
58
0
  if (dirfsp != conn->cwd_fsp) {
59
0
    smb_fname_parent = dirfsp->fsp_name;
60
0
  } else {
61
0
    struct smb_filename *atname = NULL;
62
    /*
63
     * Get a pathref on the parent.
64
     */
65
0
    status = parent_pathref(talloc_tos(),
66
0
          conn->cwd_fsp,
67
0
          smb_fname,
68
0
          &smb_fname_parent,
69
0
          &atname);
70
0
    if (!NT_STATUS_IS_OK(status)) {
71
0
      return false;
72
0
    }
73
0
  }
74
75
0
  SMB_ASSERT(VALID_STAT(smb_fname_parent->st));
76
77
  /* fast paths first */
78
79
0
  if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) {
80
0
    ret = false;
81
0
    goto out;
82
0
  }
83
84
0
#ifdef S_ISVTX
85
  /* sticky bit means delete only by owner of file or by root or
86
   * by owner of directory. */
87
0
  if (smb_fname_parent->st.st_ex_mode & S_ISVTX) {
88
0
    if (!VALID_STAT(smb_fname->st)) {
89
      /* If the file doesn't already exist then
90
       * yes we'll be able to delete it. */
91
0
      ret = true;
92
0
      goto out;
93
0
    }
94
95
    /*
96
     * Patch from SATOH Fumiyasu <fumiyas@miraclelinux.com>
97
     * for bug #3348. Don't assume owning sticky bit
98
     * directory means write access allowed.
99
     * Fail to delete if we're not the owner of the file,
100
     * or the owner of the directory as we have no possible
101
     * chance of deleting. Otherwise, go on and check the ACL.
102
     */
103
0
    if ((get_current_uid(conn) !=
104
0
      smb_fname_parent->st.st_ex_uid) &&
105
0
        (get_current_uid(conn) != smb_fname->st.st_ex_uid)) {
106
0
      DBG_DEBUG("not owner of file %s or directory %s\n",
107
0
          smb_fname_str_dbg(smb_fname),
108
0
          smb_fname_str_dbg(smb_fname_parent));
109
0
      ret = false;
110
0
      goto out;
111
0
    }
112
0
  }
113
0
#endif
114
115
  /* now for ACL checks */
116
117
  /*
118
   * There's two ways to get the permission to delete a file: First by
119
   * having the DELETE bit on the file itself and second if that does
120
   * not help, by the DELETE_CHILD bit on the containing directory.
121
   *
122
   * Here we only check the directory permissions, we will
123
   * check the file DELETE permission separately.
124
   */
125
126
0
  ret = NT_STATUS_IS_OK(smbd_check_access_rights_fsp(
127
0
        conn->cwd_fsp,
128
0
        smb_fname_parent->fsp,
129
0
        false,
130
0
        FILE_DELETE_CHILD));
131
0
 out:
132
0
  if (smb_fname_parent != dirfsp->fsp_name) {
133
0
    TALLOC_FREE(smb_fname_parent);
134
0
  }
135
0
  return ret;
136
0
}
137
138
/****************************************************************************
139
 Userspace check for write access to fsp.
140
****************************************************************************/
141
142
bool can_write_to_fsp(struct files_struct *fsp)
143
0
{
144
0
  return NT_STATUS_IS_OK(smbd_check_access_rights_fsp(
145
0
              fsp->conn->cwd_fsp,
146
0
              fsp,
147
0
              false,
148
0
              FILE_WRITE_DATA));
149
0
}
150
151
/****************************************************************************
152
 Check for an existing default Windows ACL on a directory fsp.
153
****************************************************************************/
154
155
bool directory_has_default_acl_fsp(struct files_struct *fsp)
156
0
{
157
0
  struct security_descriptor *secdesc = NULL;
158
0
  unsigned int i;
159
0
  NTSTATUS status;
160
161
0
  status = SMB_VFS_FGET_NT_ACL(metadata_fsp(fsp),
162
0
        SECINFO_DACL,
163
0
        talloc_tos(),
164
0
        &secdesc);
165
166
0
  if (!NT_STATUS_IS_OK(status) ||
167
0
      secdesc == NULL ||
168
0
      secdesc->dacl == NULL)
169
0
  {
170
0
    TALLOC_FREE(secdesc);
171
0
    return false;
172
0
  }
173
174
0
  for (i = 0; i < secdesc->dacl->num_aces; i++) {
175
0
    struct security_ace *psa = &secdesc->dacl->aces[i];
176
177
0
    if (psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
178
0
        SEC_ACE_FLAG_CONTAINER_INHERIT))
179
0
    {
180
0
      TALLOC_FREE(secdesc);
181
0
      return true;
182
0
    }
183
0
  }
184
0
  TALLOC_FREE(secdesc);
185
0
  return false;
186
0
}
187
188
/****************************************************************************
189
 Check if setting delete on close is allowed on this fsp.
190
****************************************************************************/
191
192
NTSTATUS can_set_delete_on_close(files_struct *fsp, uint32_t dosmode)
193
0
{
194
0
  NTSTATUS status;
195
  /*
196
   * Only allow delete on close for writable files.
197
   */
198
199
0
  if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
200
0
      !lp_delete_readonly(SNUM(fsp->conn))) {
201
0
    DEBUG(10,("can_set_delete_on_close: file %s delete on close "
202
0
        "flag set but file attribute is readonly.\n",
203
0
        fsp_str_dbg(fsp)));
204
0
    return NT_STATUS_CANNOT_DELETE;
205
0
  }
206
207
  /*
208
   * Only allow delete on close for writable shares.
209
   */
210
211
0
  if (!CAN_WRITE(fsp->conn)) {
212
0
    DEBUG(10,("can_set_delete_on_close: file %s delete on "
213
0
        "close flag set but write access denied on share.\n",
214
0
        fsp_str_dbg(fsp)));
215
0
    return NT_STATUS_ACCESS_DENIED;
216
0
  }
217
218
  /*
219
   * Only allow delete on close for files/directories opened with delete
220
   * intent.
221
   */
222
223
0
  status = check_any_access_fsp(fsp, DELETE_ACCESS);
224
0
  if (!NT_STATUS_IS_OK(status)) {
225
0
    DBG_DEBUG("file %s delete on "
226
0
        "close flag set but delete access denied.\n",
227
0
        fsp_str_dbg(fsp));
228
0
    return status;
229
0
  }
230
231
  /* Don't allow delete on close for non-empty directories. */
232
0
  if (fsp->fsp_flags.is_directory) {
233
0
    SMB_ASSERT(!fsp_is_alternate_stream(fsp));
234
235
    /* Or the root of a share. */
236
0
    if (ISDOT(fsp->fsp_name->base_name)) {
237
0
      DEBUG(10,("can_set_delete_on_close: can't set delete on "
238
0
          "close for the root of a share.\n"));
239
0
      return NT_STATUS_ACCESS_DENIED;
240
0
    }
241
242
0
    return can_delete_directory_fsp(fsp);
243
0
  }
244
245
0
  return NT_STATUS_OK;
246
0
}