c2203673f3d86236eb1abb4b1c946232536a9227
[kai/samba.git] / source3 / smbd / file_access.c
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
27 #undef  DBGC_CLASS
28 #define DBGC_CLASS DBGC_ACLS
29
30 /**
31  * Security descriptor / NT Token level access check function.
32  */
33 bool can_access_file_acl(struct connection_struct *conn,
34                          const struct smb_filename *smb_fname,
35                          uint32_t access_mask)
36 {
37         NTSTATUS status;
38         uint32_t access_granted;
39         struct security_descriptor *secdesc = NULL;
40         bool ret;
41
42         if (get_current_uid(conn) == (uid_t)0) {
43                 /* I'm sorry sir, I didn't know you were root... */
44                 return true;
45         }
46
47         status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
48                                     (SECINFO_OWNER |
49                                      SECINFO_GROUP |
50                                      SECINFO_DACL),
51                                     &secdesc);
52         if (!NT_STATUS_IS_OK(status)) {
53                 DEBUG(5, ("Could not get acl: %s\n", nt_errstr(status)));
54                 ret = false;
55                 goto out;
56         }
57
58         status = se_access_check(secdesc, get_current_nttok(conn),
59                                  access_mask, &access_granted);
60         ret = NT_STATUS_IS_OK(status);
61
62         if (DEBUGLEVEL >= 10) {
63                 DEBUG(10,("can_access_file_acl for file %s "
64                         "access_mask 0x%x, access_granted 0x%x "
65                         "access %s\n",
66                         smb_fname_str_dbg(smb_fname),
67                         (unsigned int)access_mask,
68                         (unsigned int)access_granted,
69                         ret ? "ALLOWED" : "DENIED" ));
70                 NDR_PRINT_DEBUG(security_descriptor, secdesc);
71         }
72  out:
73         TALLOC_FREE(secdesc);
74         return ret;
75 }
76
77 /****************************************************************************
78  Actually emulate the in-kernel access checking for delete access. We need
79  this to successfully return ACCESS_DENIED on a file open for delete access.
80 ****************************************************************************/
81
82 bool can_delete_file_in_directory(connection_struct *conn,
83                                   const struct smb_filename *smb_fname)
84 {
85         TALLOC_CTX *ctx = talloc_tos();
86         char *dname = NULL;
87         struct smb_filename *smb_fname_parent = NULL;
88         NTSTATUS status;
89         bool ret;
90
91         if (!CAN_WRITE(conn)) {
92                 return False;
93         }
94
95         if (!lp_acl_check_permissions(SNUM(conn))) {
96                 /* This option means don't check. */
97                 return true;
98         }
99
100         /* Get the parent directory permission mask and owners. */
101         if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) {
102                 return False;
103         }
104
105         status = create_synthetic_smb_fname(ctx, dname, NULL, NULL,
106                                             &smb_fname_parent);
107         if (!NT_STATUS_IS_OK(status)) {
108                 ret = false;
109                 goto out;
110         }
111
112         if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
113                 ret = false;
114                 goto out;
115         }
116
117         /* fast paths first */
118
119         if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) {
120                 ret = false;
121                 goto out;
122         }
123         if (get_current_uid(conn) == (uid_t)0) {
124                 /* I'm sorry sir, I didn't know you were root... */
125                 ret = true;
126                 goto out;
127         }
128
129 #ifdef S_ISVTX
130         /* sticky bit means delete only by owner of file or by root or
131          * by owner of directory. */
132         if (smb_fname_parent->st.st_ex_mode & S_ISVTX) {
133                 if (!VALID_STAT(smb_fname->st)) {
134                         /* If the file doesn't already exist then
135                          * yes we'll be able to delete it. */
136                         ret = true;
137                         goto out;
138                 }
139
140                 /*
141                  * Patch from SATOH Fumiyasu <fumiyas@miraclelinux.com>
142                  * for bug #3348. Don't assume owning sticky bit
143                  * directory means write access allowed.
144                  * Fail to delete if we're not the owner of the file,
145                  * or the owner of the directory as we have no possible
146                  * chance of deleting. Otherwise, go on and check the ACL.
147                  */
148                 if ((get_current_uid(conn) !=
149                         smb_fname_parent->st.st_ex_uid) &&
150                     (get_current_uid(conn) != smb_fname->st.st_ex_uid)) {
151                         DEBUG(10,("can_delete_file_in_directory: not "
152                                   "owner of file %s or directory %s",
153                                   smb_fname_str_dbg(smb_fname),
154                                   smb_fname_str_dbg(smb_fname_parent)));
155                         ret = false;
156                         goto out;
157                 }
158         }
159 #endif
160
161         /* now for ACL checks */
162
163         /*
164          * There's two ways to get the permission to delete a file: First by
165          * having the DELETE bit on the file itself and second if that does
166          * not help, by the DELETE_CHILD bit on the containing directory.
167          *
168          * Here we only check the directory permissions, we will
169          * check the file DELETE permission separately.
170          */
171
172         ret = can_access_file_acl(conn, smb_fname_parent, FILE_DELETE_CHILD);
173  out:
174         TALLOC_FREE(dname);
175         TALLOC_FREE(smb_fname_parent);
176         return ret;
177 }
178
179 /****************************************************************************
180  Actually emulate the in-kernel access checking for read/write access. We need
181  this to successfully check for ability to write for dos filetimes.
182  Note this doesn't take into account share write permissions.
183 ****************************************************************************/
184
185 bool can_access_file_data(connection_struct *conn,
186                           const struct smb_filename *smb_fname,
187                           uint32 access_mask)
188 {
189         if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
190                 return False;
191         }
192         access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);
193
194         /* some fast paths first */
195
196         DEBUG(10,("can_access_file_data: requesting 0x%x on file %s\n",
197                   (unsigned int)access_mask, smb_fname_str_dbg(smb_fname)));
198
199         if (get_current_uid(conn) == (uid_t)0) {
200                 /* I'm sorry sir, I didn't know you were root... */
201                 return True;
202         }
203
204         SMB_ASSERT(VALID_STAT(smb_fname->st));
205
206         /* Check primary owner access. */
207         if (get_current_uid(conn) == smb_fname->st.st_ex_uid) {
208                 switch (access_mask) {
209                         case FILE_READ_DATA:
210                                 return (smb_fname->st.st_ex_mode & S_IRUSR) ?
211                                     True : False;
212
213                         case FILE_WRITE_DATA:
214                                 return (smb_fname->st.st_ex_mode & S_IWUSR) ?
215                                     True : False;
216
217                         default: /* FILE_READ_DATA|FILE_WRITE_DATA */
218
219                                 if ((smb_fname->st.st_ex_mode &
220                                         (S_IWUSR|S_IRUSR)) ==
221                                     (S_IWUSR|S_IRUSR)) {
222                                         return True;
223                                 } else {
224                                         return False;
225                                 }
226                 }
227         }
228
229         /* now for ACL checks */
230
231         return can_access_file_acl(conn, smb_fname, access_mask);
232 }
233
234 /****************************************************************************
235  Userspace check for write access.
236  Note this doesn't take into account share write permissions.
237 ****************************************************************************/
238
239 bool can_write_to_file(connection_struct *conn,
240                        const struct smb_filename *smb_fname)
241 {
242         return can_access_file_data(conn, smb_fname, FILE_WRITE_DATA);
243 }
244
245 /****************************************************************************
246  Check for an existing default Windows ACL on a directory.
247 ****************************************************************************/
248
249 bool directory_has_default_acl(connection_struct *conn, const char *fname)
250 {
251         /* returns talloced off tos. */
252         struct security_descriptor *secdesc = NULL;
253         unsigned int i;
254         NTSTATUS status = SMB_VFS_GET_NT_ACL(conn, fname,
255                                 SECINFO_DACL, &secdesc);
256
257         if (!NT_STATUS_IS_OK(status) || secdesc == NULL) {
258                 return false;
259         }
260
261         for (i = 0; i < secdesc->dacl->num_aces; i++) {
262                 struct security_ace *psa = &secdesc->dacl->aces[i];
263                 if (psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
264                                 SEC_ACE_FLAG_CONTAINER_INHERIT)) {
265                         TALLOC_FREE(secdesc);
266                         return true;
267                 }
268         }
269         TALLOC_FREE(secdesc);
270         return false;
271 }