vfs/nfs4_acls: move special handling of SMB_ACE4_SYNCHRONIZE to vfs_zfsacl
[samba.git] / source3 / modules / vfs_zfsacl.c
1 /*
2  * Convert ZFS/NFSv4 acls to NT acls and vice versa.
3  *
4  * Copyright (C) Jiri Sasek, 2007
5  * based on the foobar.c module which is copyrighted by Volker Lendecke
6  *
7  * Many thanks to Axel Apitz for help to fix the special ace's handling
8  * issues.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "smbd/smbd.h"
28 #include "nfs4_acls.h"
29
30 #if HAVE_FREEBSD_SUNACL_H
31 #include "sunacl.h"
32 #endif
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_VFS
36
37 #define ZFSACL_MODULE_NAME "zfsacl"
38
39 /* zfs_get_nt_acl()
40  * read the local file's acls and return it in NT form
41  * using the NFSv4 format conversion
42  */
43 static NTSTATUS zfs_get_nt_acl_common(TALLOC_CTX *mem_ctx,
44                                       const char *name,
45                                       struct SMB4ACL_T **ppacl)
46 {
47         int naces, i;
48         ace_t *acebuf;
49         struct SMB4ACL_T *pacl;
50
51         /* read the number of file aces */
52         if((naces = acl(name, ACE_GETACLCNT, 0, NULL)) == -1) {
53                 if(errno == ENOSYS) {
54                         DEBUG(9, ("acl(ACE_GETACLCNT, %s): Operation is not "
55                                   "supported on the filesystem where the file "
56                                   "reside\n", name));
57                 } else {
58                         DEBUG(9, ("acl(ACE_GETACLCNT, %s): %s ", name,
59                                         strerror(errno)));
60                 }
61                 return map_nt_error_from_unix(errno);
62         }
63         /* allocate the field of ZFS aces */
64         mem_ctx = talloc_tos();
65         acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
66         if(acebuf == NULL) {
67                 return NT_STATUS_NO_MEMORY;
68         }
69         /* read the aces into the field */
70         if(acl(name, ACE_GETACL, naces, acebuf) < 0) {
71                 DEBUG(9, ("acl(ACE_GETACL, %s): %s ", name,
72                                 strerror(errno)));
73                 return map_nt_error_from_unix(errno);
74         }
75         /* create SMB4ACL data */
76         if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
77                 return NT_STATUS_NO_MEMORY;
78         }
79         for(i=0; i<naces; i++) {
80                 SMB_ACE4PROP_T aceprop;
81
82                 aceprop.aceType  = (uint32_t) acebuf[i].a_type;
83                 aceprop.aceFlags = (uint32_t) acebuf[i].a_flags;
84                 aceprop.aceMask  = (uint32_t) acebuf[i].a_access_mask;
85                 aceprop.who.id   = (uint32_t) acebuf[i].a_who;
86
87                 /*
88                  * Windows clients expect SYNC on acls to correctly allow
89                  * rename, cf bug #7909. But not on DENY ace entries, cf bug
90                  * #8442.
91                  */
92                 if (aceprop.aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) {
93                         aceprop.aceMask |= SMB_ACE4_SYNCHRONIZE;
94                 }
95
96                 if(aceprop.aceFlags & ACE_OWNER) {
97                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
98                         aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
99                 } else if(aceprop.aceFlags & ACE_GROUP) {
100                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
101                         aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
102                 } else if(aceprop.aceFlags & ACE_EVERYONE) {
103                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
104                         aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
105                 } else {
106                         aceprop.flags   = 0;
107                 }
108                 if(smb_add_ace4(pacl, &aceprop) == NULL)
109                         return NT_STATUS_NO_MEMORY;
110         }
111
112         *ppacl = pacl;
113         return NT_STATUS_OK;
114 }
115
116 /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */
117 static bool zfs_process_smbacl(vfs_handle_struct *handle, files_struct *fsp,
118                                struct SMB4ACL_T *smbacl)
119 {
120         int naces = smb_get_naces(smbacl), i;
121         ace_t *acebuf;
122         struct SMB4ACE_T *smbace;
123         TALLOC_CTX      *mem_ctx;
124         bool have_special_id = false;
125
126         /* allocate the field of ZFS aces */
127         mem_ctx = talloc_tos();
128         acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces);
129         if(acebuf == NULL) {
130                 errno = ENOMEM;
131                 return False;
132         }
133         /* handle all aces */
134         for(smbace = smb_first_ace4(smbacl), i = 0;
135                         smbace!=NULL;
136                         smbace = smb_next_ace4(smbace), i++) {
137                 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
138
139                 acebuf[i].a_type        = aceprop->aceType;
140                 acebuf[i].a_flags       = aceprop->aceFlags;
141                 acebuf[i].a_access_mask = aceprop->aceMask;
142                 /* SYNC on acls is a no-op on ZFS.
143                    See bug #7909. */
144                 acebuf[i].a_access_mask &= ~SMB_ACE4_SYNCHRONIZE;
145                 acebuf[i].a_who         = aceprop->who.id;
146                 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
147                         switch(aceprop->who.special_id) {
148                         case SMB_ACE4_WHO_EVERYONE:
149                                 acebuf[i].a_flags |= ACE_EVERYONE;
150                                 break;
151                         case SMB_ACE4_WHO_OWNER:
152                                 acebuf[i].a_flags |= ACE_OWNER;
153                                 break;
154                         case SMB_ACE4_WHO_GROUP:
155                                 acebuf[i].a_flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP;
156                                 break;
157                         default:
158                                 DEBUG(8, ("unsupported special_id %d\n", \
159                                         aceprop->who.special_id));
160                                 continue; /* don't add it !!! */
161                         }
162                         have_special_id = true;
163                 }
164         }
165
166         if (!have_special_id
167             && lp_parm_bool(fsp->conn->params->service, "zfsacl",
168                             "denymissingspecial", false)) {
169                 errno = EACCES;
170                 return false;
171         }
172
173         SMB_ASSERT(i == naces);
174
175         /* store acl */
176         if(acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf)) {
177                 if(errno == ENOSYS) {
178                         DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not "
179                                   "supported on the filesystem where the file "
180                                   "reside", fsp_str_dbg(fsp)));
181                 } else {
182                         DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp_str_dbg(fsp),
183                                   strerror(errno)));
184                 }
185                 return 0;
186         }
187
188         return True;
189 }
190
191 /* zfs_set_nt_acl()
192  * set the local file's acls obtaining it in NT form
193  * using the NFSv4 format conversion
194  */
195 static NTSTATUS zfs_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
196                            uint32_t security_info_sent,
197                            const struct security_descriptor *psd)
198 {
199         return smb_set_nt_acl_nfs4(handle, fsp, NULL, security_info_sent, psd,
200                                    zfs_process_smbacl);
201 }
202
203 static NTSTATUS zfsacl_fget_nt_acl(struct vfs_handle_struct *handle,
204                                    struct files_struct *fsp,
205                                    uint32_t security_info,
206                                    TALLOC_CTX *mem_ctx,
207                                    struct security_descriptor **ppdesc)
208 {
209         struct SMB4ACL_T *pacl;
210         NTSTATUS status;
211         TALLOC_CTX *frame = talloc_stackframe();
212
213         status = zfs_get_nt_acl_common(frame,
214                                        fsp->fsp_name->base_name,
215                                        &pacl);
216         if (!NT_STATUS_IS_OK(status)) {
217                 TALLOC_FREE(frame);
218                 return status;
219         }
220
221         status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
222                                       ppdesc, pacl);
223         TALLOC_FREE(frame);
224         return status;
225 }
226
227 static NTSTATUS zfsacl_get_nt_acl(struct vfs_handle_struct *handle,
228                                 const struct smb_filename *smb_fname,
229                                 uint32_t security_info,
230                                 TALLOC_CTX *mem_ctx,
231                                 struct security_descriptor **ppdesc)
232 {
233         struct SMB4ACL_T *pacl;
234         NTSTATUS status;
235         TALLOC_CTX *frame = talloc_stackframe();
236
237         status = zfs_get_nt_acl_common(frame,
238                                         smb_fname->base_name,
239                                         &pacl);
240         if (!NT_STATUS_IS_OK(status)) {
241                 TALLOC_FREE(frame);
242                 return status;
243         }
244
245         status = smb_get_nt_acl_nfs4(handle->conn,
246                                         smb_fname,
247                                         NULL,
248                                         security_info,
249                                         mem_ctx,
250                                         ppdesc,
251                                         pacl);
252         TALLOC_FREE(frame);
253         return status;
254 }
255
256 static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle,
257                          files_struct *fsp,
258                          uint32_t security_info_sent,
259                          const struct security_descriptor *psd)
260 {
261         return zfs_set_nt_acl(handle, fsp, security_info_sent, psd);
262 }
263
264 /* nils.goroll@hamburg.de 2008-06-16 :
265
266    See also
267    - https://bugzilla.samba.org/show_bug.cgi?id=5446
268    - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240
269
270    Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2)
271    with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for
272    use by samba in this module.
273
274    As the acl(2) interface is identical for ZFS and for NFS, this module,
275    vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4
276    mounts on Solaris.
277
278    But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL
279    / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client
280    implemets a compatibility wrapper, which will make calls to
281    traditional ACL calls though vfs_solarisacl succeed. As the
282    compatibility wrapper's implementation is (by design) incomplete,
283    we want to make sure that it is never being called.
284
285    As long as Samba does not support an exiplicit method for a module
286    to define conflicting vfs methods, we should override all conflicting
287    methods here.
288
289    For this to work, we need to make sure that this module is initialised
290    *after* vfs_solarisacl
291
292    Function declarations taken from vfs_solarisacl
293 */
294
295 static SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle,
296                                         const struct smb_filename *smb_fname,
297                                         SMB_ACL_TYPE_T type,
298                                         TALLOC_CTX *mem_ctx)
299 {
300         return (SMB_ACL_T)NULL;
301 }
302
303 static SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle,
304                                              files_struct *fsp,
305                                              TALLOC_CTX *mem_ctx)
306 {
307         return (SMB_ACL_T)NULL;
308 }
309
310 static int zfsacl_fail__sys_acl_set_file(vfs_handle_struct *handle,
311                                          const struct smb_filename *smb_fname,
312                                          SMB_ACL_TYPE_T type,
313                                          SMB_ACL_T theacl)
314 {
315         return -1;
316 }
317
318 static int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle,
319                                        files_struct *fsp,
320                                        SMB_ACL_T theacl)
321 {
322         return -1;
323 }
324
325 static int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
326                         const struct smb_filename *smb_fname)
327 {
328         return -1;
329 }
330
331 static int zfsacl_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
332                         const struct smb_filename *smb_fname,
333                         TALLOC_CTX *mem_ctx,
334                         char **blob_description,
335                         DATA_BLOB *blob)
336 {
337         return -1;
338 }
339
340 static int zfsacl_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, files_struct *fsp, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob)
341 {
342         return -1;
343 }
344
345 /* VFS operations structure */
346
347 static struct vfs_fn_pointers zfsacl_fns = {
348         .sys_acl_get_file_fn = zfsacl_fail__sys_acl_get_file,
349         .sys_acl_get_fd_fn = zfsacl_fail__sys_acl_get_fd,
350         .sys_acl_blob_get_file_fn = zfsacl_fail__sys_acl_blob_get_file,
351         .sys_acl_blob_get_fd_fn = zfsacl_fail__sys_acl_blob_get_fd,
352         .sys_acl_set_file_fn = zfsacl_fail__sys_acl_set_file,
353         .sys_acl_set_fd_fn = zfsacl_fail__sys_acl_set_fd,
354         .sys_acl_delete_def_file_fn = zfsacl_fail__sys_acl_delete_def_file,
355         .fget_nt_acl_fn = zfsacl_fget_nt_acl,
356         .get_nt_acl_fn = zfsacl_get_nt_acl,
357         .fset_nt_acl_fn = zfsacl_fset_nt_acl,
358 };
359
360 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *);
361 NTSTATUS vfs_zfsacl_init(TALLOC_CTX *ctx)
362 {
363         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfsacl",
364                                 &zfsacl_fns);
365 }