vfs: Add new VFS module vfs_nfs4acl_xattr to use nfs4acl.idl
[samba.git] / source3 / modules / vfs_nfs4acl_xattr.c
1 /*
2  * Convert NFSv4 acls stored per http://www.suse.de/~agruen/nfs4acl/ 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  * based on pvfs_acl_nfs4.c  Copyright (C) Andrew Tridgell 2006
7  *
8  * based on vfs_fake_acls:
9  * Copyright (C) Tim Potter, 1999-2000
10  * Copyright (C) Alexander Bokovoy, 2002
11  * Copyright (C) Andrew Bartlett, 2002,2012
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include "smbd/smbd.h"
31 #include "nfs4_acls.h"
32 #include "librpc/gen_ndr/ndr_nfs4acl.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_VFS
36
37 static struct nfs4acl *nfs4acl_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
38 {
39         enum ndr_err_code ndr_err;
40         struct nfs4acl *acl = talloc(mem_ctx, struct nfs4acl);
41         if (!acl) {
42                 errno = ENOMEM;
43                 return NULL;
44         }
45
46         ndr_err = ndr_pull_struct_blob(blob, acl, acl,
47                 (ndr_pull_flags_fn_t)ndr_pull_nfs4acl);
48
49         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
50                 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
51                           ndr_errstr(ndr_err)));
52                 TALLOC_FREE(acl);
53                 return NULL;
54         }
55         return acl;
56 }
57
58 static DATA_BLOB nfs4acl_acl2blob(TALLOC_CTX *mem_ctx, struct nfs4acl *acl)
59 {
60         enum ndr_err_code ndr_err;
61         DATA_BLOB blob;
62         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl,
63                 (ndr_push_flags_fn_t)ndr_push_nfs4acl);
64
65         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
66                 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
67                           ndr_errstr(ndr_err)));
68                 return data_blob_null;
69         }
70         return blob;
71 }
72
73 static NTSTATUS nfs4_get_nfs4_acl_common(TALLOC_CTX *mem_ctx,
74                                          DATA_BLOB *blob,
75                                          SMB4ACL_T **ppacl)
76 {
77         int i;
78         struct nfs4acl *nfs4acl = NULL;
79         SMB4ACL_T *pacl = NULL;
80         TALLOC_CTX *frame = talloc_stackframe();
81         nfs4acl = nfs4acl_blob2acl(blob, frame);
82
83         /* create SMB4ACL data */
84         if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
85                 TALLOC_FREE(frame);
86                 return NT_STATUS_NO_MEMORY;
87         }
88         for(i=0; i<nfs4acl->a_count; i++) {
89                 SMB_ACE4PROP_T aceprop;
90
91                 aceprop.aceType  = (uint32) nfs4acl->ace[i].e_type;
92                 aceprop.aceFlags = (uint32) nfs4acl->ace[i].e_flags;
93                 aceprop.aceMask  = (uint32) nfs4acl->ace[i].e_mask;
94                 aceprop.who.id   = (uint32) nfs4acl->ace[i].e_id;
95                 if (!strcmp(nfs4acl->ace[i].e_who,
96                             NFS4ACL_XATTR_OWNER_WHO)) {
97                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
98                         aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
99                 } else if (!strcmp(nfs4acl->ace[i].e_who,
100                                    NFS4ACL_XATTR_GROUP_WHO)) {
101                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
102                         aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
103                 } else if (!strcmp(nfs4acl->ace[i].e_who,
104                                    NFS4ACL_XATTR_EVERYONE_WHO)) {
105                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
106                         aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
107                 } else {
108                         aceprop.flags = 0;
109                 }
110                 if(smb_add_ace4(pacl, &aceprop) == NULL) {
111                         TALLOC_FREE(frame);
112                         return NT_STATUS_NO_MEMORY;
113                 }
114         }
115
116         *ppacl = pacl;
117         TALLOC_FREE(frame);
118         return NT_STATUS_OK;
119 }
120
121 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
122 static NTSTATUS nfs4_fget_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
123                                    files_struct *fsp, SMB4ACL_T **ppacl)
124 {
125         NTSTATUS status;
126         DATA_BLOB blob = data_blob_null;
127         ssize_t length;
128         TALLOC_CTX *frame = talloc_stackframe();
129
130         do {
131                 blob.length += 1000;
132                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
133                 if (!blob.data) {
134                         TALLOC_FREE(frame);
135                         errno = ENOMEM;
136                         return NT_STATUS_NO_MEMORY;
137                 }
138                 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, NFS4ACL_XATTR_NAME, blob.data, blob.length);
139                 blob.length = length;
140         } while (length == -1 && errno == ERANGE);
141         if (length == -1) {
142                 TALLOC_FREE(frame);
143                 return map_nt_error_from_unix(errno);
144         }
145         status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
146         TALLOC_FREE(frame);
147         return status;
148 }
149
150 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
151 static NTSTATUS nfs4_get_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
152                                   const char *path, SMB4ACL_T **ppacl)
153 {
154         NTSTATUS status;
155         DATA_BLOB blob = data_blob_null;
156         ssize_t length;
157         TALLOC_CTX *frame = talloc_stackframe();
158
159         do {
160                 blob.length += 1000;
161                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
162                 if (!blob.data) {
163                         TALLOC_FREE(frame);
164                         errno = ENOMEM;
165                         return NT_STATUS_NO_MEMORY;
166                 }
167                 length = SMB_VFS_NEXT_GETXATTR(handle, path, NFS4ACL_XATTR_NAME, blob.data, blob.length);
168                 blob.length = length;
169         } while (length == -1 && errno == ERANGE);
170         if (length == -1) {
171                 TALLOC_FREE(frame);
172                 return map_nt_error_from_unix(errno);
173         }
174         status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
175         TALLOC_FREE(frame);
176         return status;
177 }
178
179 /* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
180 static bool nfs4_process_smbacl(vfs_handle_struct *handle, files_struct *fsp, SMB4ACL_T *smbacl)
181 {
182         TALLOC_CTX *frame = talloc_stackframe();
183         int i;
184         struct nfs4acl *nfs4acl;
185         SMB4ACE_T *smbace;
186         bool have_special_id = false;
187         int ret;
188         DATA_BLOB blob;
189
190         /* allocate the field of NFS4 aces */
191         nfs4acl = talloc_zero(frame, struct nfs4acl);
192         if(nfs4acl == NULL) {
193                 TALLOC_FREE(frame);
194                 errno = ENOMEM;
195                 return false;
196         }
197
198         nfs4acl->a_count = smb_get_naces(smbacl);
199
200         nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace, nfs4acl->a_count);
201         if(nfs4acl->ace == NULL) {
202                 TALLOC_FREE(frame);
203                 errno = ENOMEM;
204                 return false;
205         }
206
207         /* handle all aces */
208         for(smbace = smb_first_ace4(smbacl), i = 0;
209                         smbace!=NULL;
210                         smbace = smb_next_ace4(smbace), i++) {
211                 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
212
213                 nfs4acl->ace[i].e_type        = aceprop->aceType;
214                 nfs4acl->ace[i].e_flags       = aceprop->aceFlags;
215                 nfs4acl->ace[i].e_mask        = aceprop->aceMask;
216                 nfs4acl->ace[i].e_id          = aceprop->who.id;
217                 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
218                         switch(aceprop->who.special_id) {
219                         case SMB_ACE4_WHO_EVERYONE:
220                                 nfs4acl->ace[i].e_who =
221                                         NFS4ACL_XATTR_EVERYONE_WHO;
222                                 break;
223                         case SMB_ACE4_WHO_OWNER:
224                                 nfs4acl->ace[i].e_who =
225                                         NFS4ACL_XATTR_OWNER_WHO;
226                                 break;
227                         case SMB_ACE4_WHO_GROUP:
228                                 nfs4acl->ace[i].e_who =
229                                         NFS4ACL_XATTR_GROUP_WHO;
230                                 break;
231                         default:
232                                 DEBUG(8, ("unsupported special_id %d\n", \
233                                         aceprop->who.special_id));
234                                 continue; /* don't add it !!! */
235                         }
236                         have_special_id = true;
237                 } else {
238                         nfs4acl->ace[i].e_who = "";
239                 }
240         }
241
242         if (!have_special_id
243             && lp_parm_bool(fsp->conn->params->service, "nfs4acl_xattr",
244                             "denymissingspecial", false)) {
245                 TALLOC_FREE(frame);
246                 errno = EACCES;
247                 return false;
248         }
249
250         SMB_ASSERT(i == nfs4acl->a_count);
251
252         blob = nfs4acl_acl2blob(frame, nfs4acl);
253         if (!blob.data) {
254                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
255                 TALLOC_FREE(frame);
256                 errno = EINVAL;
257                 return false;
258         }
259         ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_XATTR_NAME, blob.data, blob.length, 0);
260         TALLOC_FREE(frame);
261
262         return ret == 0;
263 }
264
265 /* nfs4_set_nt_acl()
266  * set the local file's acls obtaining it in NT form
267  * using the NFSv4 format conversion
268  */
269 static NTSTATUS nfs4_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
270                            uint32 security_info_sent,
271                            const struct security_descriptor *psd)
272 {
273         return smb_set_nt_acl_nfs4(handle, fsp, security_info_sent, psd,
274                         nfs4_process_smbacl);
275 }
276
277 static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
278                                    struct files_struct *fsp,
279                                    uint32 security_info,
280                                    TALLOC_CTX *mem_ctx,
281                                    struct security_descriptor **ppdesc)
282 {
283         SMB4ACL_T *pacl;
284         NTSTATUS status;
285         TALLOC_CTX *frame = talloc_stackframe();
286
287         status = nfs4_fget_nfs4_acl(handle, frame, fsp, &pacl);
288         if (!NT_STATUS_IS_OK(status)) {
289                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
290                         status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
291                                                           mem_ctx, ppdesc);
292                 }
293                 TALLOC_FREE(frame);
294                 return status;
295         }
296
297         status = smb_fget_nt_acl_nfs4(fsp, security_info, mem_ctx, ppdesc, pacl);
298         TALLOC_FREE(frame);
299         return status;
300 }
301
302 static NTSTATUS nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct *handle,
303                                   const char *name, uint32 security_info,
304                                   TALLOC_CTX *mem_ctx,
305                                   struct security_descriptor **ppdesc)
306 {
307         SMB4ACL_T *pacl;
308         NTSTATUS status;
309         TALLOC_CTX *frame = talloc_stackframe();
310
311         status = nfs4_get_nfs4_acl(handle, frame, name, &pacl);
312         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
313                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
314                         status = SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
315                                                          mem_ctx, ppdesc);
316                 }
317                 TALLOC_FREE(frame);
318                 return status;
319         }
320         if (!NT_STATUS_IS_OK(status)) {
321                 TALLOC_FREE(frame);
322                 return status;
323         }
324
325         status = smb_get_nt_acl_nfs4(handle->conn, name, security_info,
326                                      mem_ctx, ppdesc,
327                                      pacl);
328         TALLOC_FREE(frame);
329         return status;
330 }
331
332 static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
333                          files_struct *fsp,
334                          uint32 security_info_sent,
335                          const struct security_descriptor *psd)
336 {
337         return nfs4_set_nt_acl(handle, fsp, security_info_sent, psd);
338 }
339
340 /*
341    As long as Samba does not support an exiplicit method for a module
342    to define conflicting vfs methods, we should override all conflicting
343    methods here.  That way, we know we are using the NFSv4 storage
344
345    Function declarations taken from vfs_solarisacl
346 */
347
348 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_file(vfs_handle_struct *handle,
349                                                       const char *path_p,
350                                                       SMB_ACL_TYPE_T type,
351                                                       TALLOC_CTX *mem_ctx)
352 {
353         return (SMB_ACL_T)NULL;
354 }
355
356 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_fd(vfs_handle_struct *handle,
357                                                     files_struct *fsp,
358                                                     TALLOC_CTX *mem_ctx)
359 {
360         return (SMB_ACL_T)NULL;
361 }
362
363 static int nfs4acl_xattr_fail__sys_acl_set_file(vfs_handle_struct *handle,
364                                          const char *name,
365                                          SMB_ACL_TYPE_T type,
366                                          SMB_ACL_T theacl)
367 {
368         return -1;
369 }
370
371 static int nfs4acl_xattr_fail__sys_acl_set_fd(vfs_handle_struct *handle,
372                                        files_struct *fsp,
373                                        SMB_ACL_T theacl)
374 {
375         return -1;
376 }
377
378 static int nfs4acl_xattr_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
379                                                 const char *path)
380 {
381         return -1;
382 }
383
384 static int nfs4acl_xattr_fail__sys_acl_blob_get_file(vfs_handle_struct *handle, const char *path_p, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob)
385 {
386         return -1;
387 }
388
389 static int nfs4acl_xattr_fail__sys_acl_blob_get_fd(vfs_handle_struct *handle, files_struct *fsp, TALLOC_CTX *mem_ctx, char **blob_description, DATA_BLOB *blob)
390 {
391         return -1;
392 }
393
394 /* VFS operations structure */
395
396 static struct vfs_fn_pointers nfs4acl_xattr_fns = {
397         .sys_acl_get_file_fn = nfs4acl_xattr_fail__sys_acl_get_file,
398         .sys_acl_get_fd_fn = nfs4acl_xattr_fail__sys_acl_get_fd,
399         .sys_acl_blob_get_file_fn = nfs4acl_xattr_fail__sys_acl_blob_get_file,
400         .sys_acl_blob_get_fd_fn = nfs4acl_xattr_fail__sys_acl_blob_get_fd,
401         .sys_acl_set_file_fn = nfs4acl_xattr_fail__sys_acl_set_file,
402         .sys_acl_set_fd_fn = nfs4acl_xattr_fail__sys_acl_set_fd,
403         .sys_acl_delete_def_file_fn = nfs4acl_xattr_fail__sys_acl_delete_def_file,
404         .fget_nt_acl_fn = nfs4acl_xattr_fget_nt_acl,
405         .get_nt_acl_fn = nfs4acl_xattr_get_nt_acl,
406         .fset_nt_acl_fn = nfs4acl_xattr_fset_nt_acl,
407 };
408
409 NTSTATUS vfs_nfs4acl_xattr_init(void);
410 NTSTATUS vfs_nfs4acl_xattr_init(void)
411 {
412         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "nfs4acl_xattr",
413                                 &nfs4acl_xattr_fns);
414 }