r4056: modified the access check code based on results from RAW-ACLS
[samba.git] / source4 / ntvfs / posix / pvfs_acl.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - ACL support
5
6    Copyright (C) Andrew Tridgell 2004
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "auth/auth.h"
25 #include "system/filesys.h"
26 #include "vfs_posix.h"
27 #include "librpc/gen_ndr/ndr_xattr.h"
28
29
30 /*
31   map a single access_mask from generic to specific bits for files/dirs
32 */
33 static uint32_t pvfs_translate_mask(uint32_t access_mask)
34 {
35         if (access_mask & SEC_MASK_GENERIC) {
36                 if (access_mask & SEC_GENERIC_READ)    access_mask |= SEC_RIGHTS_FILE_READ;
37                 if (access_mask & SEC_GENERIC_WRITE)   access_mask |= SEC_RIGHTS_FILE_WRITE;
38                 if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
39                 if (access_mask & SEC_GENERIC_ALL)     access_mask |= SEC_RIGHTS_FILE_ALL;
40                 access_mask &= ~SEC_MASK_GENERIC;
41         }
42         return access_mask;
43 }
44
45
46 /*
47   map any generic access bits in the given acl
48   this relies on the fact that the mappings for files and directories
49   are the same
50 */
51 static void pvfs_translate_generic_bits(struct security_acl *acl)
52 {
53         unsigned i;
54
55         for (i=0;i<acl->num_aces;i++) {
56                 struct security_ace *ace = &acl->aces[i];
57                 ace->access_mask = pvfs_translate_mask(ace->access_mask);
58         }
59 }
60
61
62 /*
63   setup a default ACL for a file
64 */
65 static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
66                                  struct smbsrv_request *req,
67                                  struct pvfs_filename *name, int fd, 
68                                  struct xattr_NTACL *acl)
69 {
70         struct security_descriptor *sd;
71         int i;
72         struct security_ace ace;
73         NTSTATUS status;
74         const char *sid_names[] = {
75                 SID_BUILTIN_ADMINISTRATORS,
76                 SID_CREATOR_OWNER,
77                 SID_CREATOR_GROUP,
78                 SID_WORLD
79         };
80         uint32_t access_masks[4];
81         mode_t mode;
82
83         sd = security_descriptor_initialise(req);
84         if (sd == NULL) {
85                 return NT_STATUS_NO_MEMORY;
86         }
87
88         status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
89         if (!NT_STATUS_IS_OK(status)) {
90                 return status;
91         }
92         status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
93         if (!NT_STATUS_IS_OK(status)) {
94                 return status;
95         }
96
97         sd->type |= SEC_DESC_DACL_PRESENT;
98
99         /*
100           we provide 4 ACEs
101             - Administrator
102             - Owner
103             - Group
104             - Everyone
105          */
106         access_masks[0] = SEC_RIGHTS_FILE_ALL;
107         access_masks[1] = 0;
108         access_masks[2] = 0;
109         access_masks[3] = 0;
110
111         mode = name->st.st_mode;
112
113         if (mode & S_IRUSR) {
114                 access_masks[1] |= 
115                         SEC_FILE_READ_DATA | 
116                         SEC_FILE_READ_EA |
117                         SEC_FILE_READ_ATTRIBUTE |
118                         SEC_FILE_EXECUTE |
119                         SEC_STD_SYNCHRONIZE |
120                         SEC_STD_READ_CONTROL;
121         }
122         if (mode & S_IWUSR) {
123                 access_masks[1] |= 
124                         SEC_FILE_WRITE_DATA | 
125                         SEC_FILE_APPEND_DATA |
126                         SEC_FILE_WRITE_EA |
127                         SEC_FILE_WRITE_ATTRIBUTE |
128                         SEC_STD_DELETE;
129         }
130
131         if (mode & S_IRGRP) {
132                 access_masks[2] |= 
133                         SEC_FILE_READ_DATA | 
134                         SEC_FILE_READ_EA |
135                         SEC_FILE_READ_ATTRIBUTE |
136                         SEC_FILE_EXECUTE |
137                         SEC_STD_SYNCHRONIZE |
138                         SEC_STD_READ_CONTROL;
139         }
140         if (mode & S_IWGRP) {
141                 access_masks[2] |= 
142                         SEC_FILE_WRITE_DATA | 
143                         SEC_FILE_APPEND_DATA |
144                         SEC_FILE_WRITE_EA |
145                         SEC_FILE_WRITE_ATTRIBUTE;
146         }
147
148         if (mode & S_IROTH) {
149                 access_masks[3] |= 
150                         SEC_FILE_READ_DATA | 
151                         SEC_FILE_READ_EA |
152                         SEC_FILE_READ_ATTRIBUTE |
153                         SEC_FILE_EXECUTE |
154                         SEC_STD_SYNCHRONIZE |
155                         SEC_STD_READ_CONTROL;
156         }
157         if (mode & S_IWOTH) {
158                 access_masks[3] |= 
159                         SEC_FILE_WRITE_DATA | 
160                         SEC_FILE_APPEND_DATA |
161                         SEC_FILE_WRITE_EA |
162                         SEC_FILE_WRITE_ATTRIBUTE;
163         }
164
165         ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
166         ace.flags = 0;
167
168         for (i=0;i<ARRAY_SIZE(sid_names);i++) {
169                 struct dom_sid *sid;
170
171                 ace.access_mask = access_masks[i];
172
173                 sid = dom_sid_parse_talloc(sd, sid_names[i]);
174                 if (sid == NULL) {
175                         return NT_STATUS_NO_MEMORY;
176                 }
177                 ace.trustee = *sid;
178
179                 status = security_descriptor_dacl_add(sd, &ace);
180                 if (!NT_STATUS_IS_OK(status)) {
181                         return status;
182                 }
183         }
184         
185         acl->version = 1;
186         acl->info.sd = sd;
187
188         return NT_STATUS_OK;
189 }
190                                  
191
192 /*
193   omit any security_descriptor elements not specified in the given
194   secinfo flags
195 */
196 static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
197 {
198         if (!(secinfo_flags & SECINFO_OWNER)) {
199                 sd->owner_sid = NULL;
200         }
201         if (!(secinfo_flags & SECINFO_GROUP)) {
202                 sd->group_sid = NULL;
203         }
204         if (!(secinfo_flags & SECINFO_DACL)) {
205                 sd->dacl = NULL;
206         }
207         if (!(secinfo_flags & SECINFO_SACL)) {
208                 sd->sacl = NULL;
209         }
210 }
211
212 /*
213   answer a setfileinfo for an ACL
214 */
215 NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs, 
216                       struct smbsrv_request *req,
217                       struct pvfs_filename *name, int fd, 
218                       union smb_setfileinfo *info)
219 {
220         struct xattr_NTACL *acl;
221         uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
222         struct security_descriptor *new_sd, *sd;
223         NTSTATUS status;
224
225         acl = talloc_p(req, struct xattr_NTACL);
226         if (acl == NULL) {
227                 return NT_STATUS_NO_MEMORY;
228         }
229
230         status = pvfs_acl_load(pvfs, name, fd, acl);
231         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
232                 status = pvfs_default_acl(pvfs, req, name, fd, acl);
233         }
234         if (!NT_STATUS_IS_OK(status)) {
235                 return status;
236         }
237
238         switch (acl->version) {
239         case 1:
240                 sd = acl->info.sd;
241                 break;
242         default:
243                 return NT_STATUS_INVALID_ACL;
244         }
245
246         new_sd = info->set_secdesc.in.sd;
247
248         /* only set the elements that have been specified */
249         if (secinfo_flags & SECINFO_OWNER) {
250                 sd->owner_sid = new_sd->owner_sid;
251         }
252         if (secinfo_flags & SECINFO_GROUP) {
253                 sd->group_sid = new_sd->group_sid;
254         }
255         if (secinfo_flags & SECINFO_DACL) {
256                 sd->dacl = new_sd->dacl;
257                 pvfs_translate_generic_bits(sd->dacl);
258         }
259         if (secinfo_flags & SECINFO_SACL) {
260                 sd->sacl = new_sd->sacl;
261                 pvfs_translate_generic_bits(sd->sacl);
262         }
263
264         status = pvfs_acl_save(pvfs, name, fd, acl);
265
266         return status;
267 }
268
269
270 /*
271   answer a fileinfo query for the ACL
272 */
273 NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs, 
274                         struct smbsrv_request *req,
275                         struct pvfs_filename *name, int fd, 
276                         union smb_fileinfo *info)
277 {
278         struct xattr_NTACL *acl;
279         NTSTATUS status;
280         struct security_descriptor *sd;
281
282         acl = talloc_p(req, struct xattr_NTACL);
283         if (acl == NULL) {
284                 return NT_STATUS_NO_MEMORY;
285         }
286
287         status = pvfs_acl_load(pvfs, name, fd, acl);
288         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
289                 status = pvfs_default_acl(pvfs, req, name, fd, acl);
290         }
291         if (!NT_STATUS_IS_OK(status)) {
292                 return status;
293         }
294
295         switch (acl->version) {
296         case 1:
297                 sd = acl->info.sd;
298                 break;
299         default:
300                 return NT_STATUS_INVALID_ACL;
301         }
302
303         normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
304
305         info->query_secdesc.out.sd = sd;
306
307         return NT_STATUS_OK;
308 }
309
310
311 /*
312   default access check function based on unix permissions
313   doing this saves on building a full security descriptor
314   for the common case of access check on files with no 
315   specific NT ACL
316 */
317 NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, 
318                                 struct smbsrv_request *req,
319                                 struct pvfs_filename *name,
320                                 uint32_t *access_mask)
321 {
322         uid_t uid = geteuid();
323         uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
324
325         /* owner and root get extra permissions */
326         if (uid == 0 || uid == name->st.st_uid) {
327                 max_bits |= SEC_STD_ALL;
328         }
329
330         if (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
331                 *access_mask = max_bits;
332                 return NT_STATUS_OK;
333         }
334
335         if (*access_mask & ~max_bits) {
336                 return NT_STATUS_ACCESS_DENIED;
337         }
338
339         return NT_STATUS_OK;
340 }
341
342
343 /*
344   check the security descriptor on a file, if any
345   
346   *access_mask is modified with the access actually granted
347 */
348 NTSTATUS pvfs_access_check(struct pvfs_state *pvfs, 
349                            struct smbsrv_request *req,
350                            struct pvfs_filename *name,
351                            uint32_t *access_mask)
352 {
353         struct nt_user_token *token = req->session->session_info->nt_user_token;
354         struct xattr_NTACL *acl;
355         NTSTATUS status;
356         struct security_descriptor *sd;
357
358         acl = talloc_p(req, struct xattr_NTACL);
359         if (acl == NULL) {
360                 return NT_STATUS_NO_MEMORY;
361         }
362
363         status = pvfs_acl_load(pvfs, name, -1, acl);
364         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
365                 talloc_free(acl);
366                 return pvfs_access_check_unix(pvfs, req, name, access_mask);
367         }
368         if (!NT_STATUS_IS_OK(status)) {
369                 return status;
370         }
371
372         switch (acl->version) {
373         case 1:
374                 sd = acl->info.sd;
375                 break;
376         default:
377                 return NT_STATUS_INVALID_ACL;
378         }
379
380         /* expand the generic access bits to file specific bits */
381         *access_mask = pvfs_translate_mask(*access_mask);
382
383         /* check the acl against the required access mask */
384         status = sec_access_check(sd, token, *access_mask, access_mask);
385
386         /* this bit is always granted, even if not asked for */
387         *access_mask |= SEC_FILE_READ_ATTRIBUTE;
388
389         talloc_free(acl);
390         
391         return status;
392 }