vfs_nfs4acl_xattr: modernize ACL inheritance
[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  * Copyright (C) Ralph Boehme 2017
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28
29 #include "includes.h"
30 #include "system/filesys.h"
31 #include "smbd/smbd.h"
32 #include "nfs4_acls.h"
33 #include "librpc/gen_ndr/ndr_nfs4acl.h"
34 #include "nfs4acl_xattr.h"
35
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_VFS
38
39 static const struct enum_list nfs4acl_encoding[] = {
40         {NFS4ACL_ENCODING_NDR, "ndr"},
41         {NFS4ACL_ENCODING_XDR, "xdr"},
42 };
43
44 static struct nfs4acl *nfs4acl_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
45 {
46         enum ndr_err_code ndr_err;
47         struct nfs4acl *acl = talloc(mem_ctx, struct nfs4acl);
48         if (!acl) {
49                 errno = ENOMEM;
50                 return NULL;
51         }
52
53         ndr_err = ndr_pull_struct_blob(blob, acl, acl,
54                 (ndr_pull_flags_fn_t)ndr_pull_nfs4acl);
55
56         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
57                 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
58                           ndr_errstr(ndr_err)));
59                 TALLOC_FREE(acl);
60                 return NULL;
61         }
62         return acl;
63 }
64
65 static DATA_BLOB nfs4acl_acl2blob(TALLOC_CTX *mem_ctx, struct nfs4acl *acl)
66 {
67         enum ndr_err_code ndr_err;
68         DATA_BLOB blob;
69         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl,
70                 (ndr_push_flags_fn_t)ndr_push_nfs4acl);
71
72         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
73                 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
74                           ndr_errstr(ndr_err)));
75                 return data_blob_null;
76         }
77         return blob;
78 }
79
80 static NTSTATUS nfs4_get_nfs4_acl_common(TALLOC_CTX *mem_ctx,
81                                          DATA_BLOB *blob,
82                                          struct SMB4ACL_T **ppacl)
83 {
84         int i;
85         struct nfs4acl *nfs4acl = NULL;
86         struct SMB4ACL_T *pacl = NULL;
87         TALLOC_CTX *frame = talloc_stackframe();
88         nfs4acl = nfs4acl_blob2acl(blob, frame);
89
90         /* create SMB4ACL data */
91         if((pacl = smb_create_smb4acl(mem_ctx)) == NULL) {
92                 TALLOC_FREE(frame);
93                 return NT_STATUS_NO_MEMORY;
94         }
95         for(i=0; i<nfs4acl->a_count; i++) {
96                 SMB_ACE4PROP_T aceprop;
97
98                 aceprop.aceType  = (uint32_t) nfs4acl->ace[i].e_type;
99                 aceprop.aceFlags = (uint32_t) nfs4acl->ace[i].e_flags;
100                 aceprop.aceMask  = (uint32_t) nfs4acl->ace[i].e_mask;
101                 aceprop.who.id   = (uint32_t) nfs4acl->ace[i].e_id;
102                 if (!strcmp(nfs4acl->ace[i].e_who,
103                             NFS4ACL_XATTR_OWNER_WHO)) {
104                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
105                         aceprop.who.special_id = SMB_ACE4_WHO_OWNER;
106                 } else if (!strcmp(nfs4acl->ace[i].e_who,
107                                    NFS4ACL_XATTR_GROUP_WHO)) {
108                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
109                         aceprop.who.special_id = SMB_ACE4_WHO_GROUP;
110                 } else if (!strcmp(nfs4acl->ace[i].e_who,
111                                    NFS4ACL_XATTR_EVERYONE_WHO)) {
112                         aceprop.flags = SMB_ACE4_ID_SPECIAL;
113                         aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE;
114                 } else {
115                         aceprop.flags = 0;
116                 }
117                 if(smb_add_ace4(pacl, &aceprop) == NULL) {
118                         TALLOC_FREE(frame);
119                         return NT_STATUS_NO_MEMORY;
120                 }
121         }
122
123         *ppacl = pacl;
124         TALLOC_FREE(frame);
125         return NT_STATUS_OK;
126 }
127
128 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
129 static NTSTATUS nfs4_fget_nfs4_acl(vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
130                                    files_struct *fsp, struct SMB4ACL_T **ppacl)
131 {
132         NTSTATUS status;
133         DATA_BLOB blob = data_blob_null;
134         ssize_t length;
135         TALLOC_CTX *frame = talloc_stackframe();
136
137         do {
138                 blob.length += 1000;
139                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
140                 if (!blob.data) {
141                         TALLOC_FREE(frame);
142                         errno = ENOMEM;
143                         return NT_STATUS_NO_MEMORY;
144                 }
145                 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, NFS4ACL_NDR_XATTR_NAME, blob.data, blob.length);
146                 blob.length = length;
147         } while (length == -1 && errno == ERANGE);
148         if (length == -1) {
149                 TALLOC_FREE(frame);
150                 return map_nt_error_from_unix(errno);
151         }
152         status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
153         TALLOC_FREE(frame);
154         return status;
155 }
156
157 /* Fetch the NFSv4 ACL from the xattr, and convert into Samba's internal NFSv4 format */
158 static NTSTATUS nfs4_get_nfs4_acl(vfs_handle_struct *handle,
159                                 TALLOC_CTX *mem_ctx,
160                                 const struct smb_filename *smb_fname,
161                                 struct SMB4ACL_T **ppacl)
162 {
163         NTSTATUS status;
164         DATA_BLOB blob = data_blob_null;
165         ssize_t length;
166         TALLOC_CTX *frame = talloc_stackframe();
167
168         do {
169                 blob.length += 1000;
170                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
171                 if (!blob.data) {
172                         TALLOC_FREE(frame);
173                         errno = ENOMEM;
174                         return NT_STATUS_NO_MEMORY;
175                 }
176                 length = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
177                                 NFS4ACL_NDR_XATTR_NAME, blob.data, blob.length);
178                 blob.length = length;
179         } while (length == -1 && errno == ERANGE);
180         if (length == -1) {
181                 TALLOC_FREE(frame);
182                 return map_nt_error_from_unix(errno);
183         }
184         status = nfs4_get_nfs4_acl_common(mem_ctx, &blob, ppacl);
185         TALLOC_FREE(frame);
186         return status;
187 }
188
189 static bool nfs4acl_smb4acl2nfs4acl(TALLOC_CTX *mem_ctx,
190                                     struct SMB4ACL_T *smbacl,
191                                     struct nfs4acl **pnfs4acl,
192                                     bool denymissingspecial)
193 {
194         struct nfs4acl *nfs4acl;
195         struct SMB4ACE_T *smbace;
196         bool have_special_id = false;
197         int i;
198
199         /* allocate the field of NFS4 aces */
200         nfs4acl = talloc_zero(mem_ctx, struct nfs4acl);
201         if(nfs4acl == NULL) {
202                 errno = ENOMEM;
203                 return false;
204         }
205
206         nfs4acl->a_count = smb_get_naces(smbacl);
207
208         nfs4acl->ace = talloc_zero_array(nfs4acl, struct nfs4ace,
209                                          nfs4acl->a_count);
210         if(nfs4acl->ace == NULL) {
211                 TALLOC_FREE(nfs4acl);
212                 errno = ENOMEM;
213                 return false;
214         }
215
216         /* handle all aces */
217         for(smbace = smb_first_ace4(smbacl), i = 0;
218                         smbace!=NULL;
219                         smbace = smb_next_ace4(smbace), i++) {
220                 SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace);
221
222                 nfs4acl->ace[i].e_type        = aceprop->aceType;
223                 nfs4acl->ace[i].e_flags       = aceprop->aceFlags;
224                 nfs4acl->ace[i].e_mask        = aceprop->aceMask;
225                 nfs4acl->ace[i].e_id          = aceprop->who.id;
226                 if(aceprop->flags & SMB_ACE4_ID_SPECIAL) {
227                         switch(aceprop->who.special_id) {
228                         case SMB_ACE4_WHO_EVERYONE:
229                                 nfs4acl->ace[i].e_who =
230                                         NFS4ACL_XATTR_EVERYONE_WHO;
231                                 break;
232                         case SMB_ACE4_WHO_OWNER:
233                                 nfs4acl->ace[i].e_who =
234                                         NFS4ACL_XATTR_OWNER_WHO;
235                                 break;
236                         case SMB_ACE4_WHO_GROUP:
237                                 nfs4acl->ace[i].e_who =
238                                         NFS4ACL_XATTR_GROUP_WHO;
239                                 break;
240                         default:
241                                 DEBUG(8, ("unsupported special_id %d\n", \
242                                         aceprop->who.special_id));
243                                 continue; /* don't add it !!! */
244                         }
245                         have_special_id = true;
246                 } else {
247                         nfs4acl->ace[i].e_who = "";
248                 }
249         }
250
251         if (!have_special_id && denymissingspecial) {
252                 TALLOC_FREE(nfs4acl);
253                 errno = EACCES;
254                 return false;
255         }
256
257         SMB_ASSERT(i == nfs4acl->a_count);
258
259         *pnfs4acl = nfs4acl;
260         return true;
261 }
262
263 /* call-back function processing the NT acl -> NFS4 acl using NFSv4 conv. */
264 static bool nfs4acl_xattr_fset_smb4acl(vfs_handle_struct *handle,
265                                        files_struct *fsp,
266                                        struct SMB4ACL_T *smbacl)
267 {
268         TALLOC_CTX *frame = talloc_stackframe();
269         struct nfs4acl *nfs4acl;
270         int ret;
271         bool denymissingspecial;
272         DATA_BLOB blob;
273
274         denymissingspecial = lp_parm_bool(fsp->conn->params->service,
275                                           "nfs4acl_xattr",
276                                           "denymissingspecial", false);
277
278         if (!nfs4acl_smb4acl2nfs4acl(frame, smbacl, &nfs4acl,
279                                      denymissingspecial)) {
280                 DEBUG(0, ("Failed to convert smb ACL to nfs4 ACL.\n"));
281                 TALLOC_FREE(frame);
282                 return false;
283         }
284
285         blob = nfs4acl_acl2blob(frame, nfs4acl);
286         if (!blob.data) {
287                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr\n"));
288                 TALLOC_FREE(frame);
289                 errno = EINVAL;
290                 return false;
291         }
292         if (fsp->fh->fd == -1) {
293                 DEBUG(0, ("Error: fsp->fh->fd == -1\n"));
294         }
295         ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, NFS4ACL_NDR_XATTR_NAME,
296                                      blob.data, blob.length, 0);
297         if (ret != 0) {
298                 DEBUG(0, ("can't store acl in xattr: %s\n", strerror(errno)));
299         }
300         TALLOC_FREE(frame);
301         return ret == 0;
302 }
303
304 static NTSTATUS nfs4acl_xattr_default_sd(
305         struct vfs_handle_struct *handle,
306         const struct smb_filename *smb_fname,
307         TALLOC_CTX *mem_ctx,
308         struct security_descriptor **sd)
309 {
310         struct nfs4acl_config *config = NULL;
311         enum default_acl_style default_acl_style;
312         mode_t required_mode;
313         SMB_STRUCT_STAT sbuf = smb_fname->st;
314         int ret;
315
316         SMB_VFS_HANDLE_GET_DATA(handle, config,
317                                 struct nfs4acl_config,
318                                 return NT_STATUS_INTERNAL_ERROR);
319
320         default_acl_style = config->default_acl_style;
321
322         if (!VALID_STAT(sbuf)) {
323                 ret = vfs_stat_smb_basename(handle->conn,
324                                             smb_fname,
325                                             &sbuf);
326                 if (ret != 0) {
327                         return map_nt_error_from_unix(errno);
328                 }
329         }
330
331         if (S_ISDIR(sbuf.st_ex_mode)) {
332                 required_mode = 0777;
333         } else {
334                 required_mode = 0666;
335         }
336         if ((sbuf.st_ex_mode & required_mode) != required_mode) {
337                 default_acl_style = DEFAULT_ACL_POSIX;
338         }
339
340         return make_default_filesystem_acl(mem_ctx,
341                                            default_acl_style,
342                                            smb_fname->base_name,
343                                            &sbuf,
344                                            sd);
345 }
346
347 static NTSTATUS nfs4acl_xattr_fget_nt_acl(struct vfs_handle_struct *handle,
348                                    struct files_struct *fsp,
349                                    uint32_t security_info,
350                                    TALLOC_CTX *mem_ctx,
351                                    struct security_descriptor **ppdesc)
352 {
353         struct SMB4ACL_T *pacl;
354         NTSTATUS status;
355         TALLOC_CTX *frame = talloc_stackframe();
356
357         status = nfs4_fget_nfs4_acl(handle, frame, fsp, &pacl);
358         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
359                 TALLOC_FREE(frame);
360                 return nfs4acl_xattr_default_sd(
361                         handle, fsp->fsp_name, mem_ctx, ppdesc);
362         }
363         if (!NT_STATUS_IS_OK(status)) {
364                 TALLOC_FREE(frame);
365                 return status;
366         }
367
368         status = smb_fget_nt_acl_nfs4(fsp, NULL, security_info, mem_ctx,
369                                       ppdesc, pacl);
370         TALLOC_FREE(frame);
371         return status;
372 }
373
374 static NTSTATUS nfs4acl_xattr_get_nt_acl(struct vfs_handle_struct *handle,
375                                   const struct smb_filename *smb_fname,
376                                   uint32_t security_info,
377                                   TALLOC_CTX *mem_ctx,
378                                   struct security_descriptor **ppdesc)
379 {
380         struct SMB4ACL_T *pacl;
381         NTSTATUS status;
382         TALLOC_CTX *frame = talloc_stackframe();
383
384         status = nfs4_get_nfs4_acl(handle, frame, smb_fname, &pacl);
385         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
386                 TALLOC_FREE(frame);
387                 return nfs4acl_xattr_default_sd(
388                         handle, smb_fname, mem_ctx, ppdesc);
389         }
390         if (!NT_STATUS_IS_OK(status)) {
391                 TALLOC_FREE(frame);
392                 return status;
393         }
394
395         status = smb_get_nt_acl_nfs4(handle->conn, smb_fname, NULL,
396                                      security_info, mem_ctx, ppdesc,
397                                      pacl);
398         TALLOC_FREE(frame);
399         return status;
400 }
401
402 static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle,
403                          files_struct *fsp,
404                          uint32_t security_info_sent,
405                          const struct security_descriptor *psd)
406 {
407         return smb_set_nt_acl_nfs4(handle, fsp, NULL, security_info_sent,
408                                    psd, nfs4acl_xattr_fset_smb4acl);
409 }
410
411 static int nfs4acl_connect(struct vfs_handle_struct *handle,
412                            const char *service,
413                            const char *user)
414 {
415         struct nfs4acl_config *config = NULL;
416         const struct enum_list *default_acl_style_list = NULL;
417         const char *default_xattr_name = NULL;
418         int enumval;
419         unsigned nfs_version;
420         int ret;
421
422         default_acl_style_list = get_default_acl_style_list();
423
424         config = talloc_zero(handle->conn, struct nfs4acl_config);
425         if (config == NULL) {
426                 DBG_ERR("talloc_zero() failed\n");
427                 return -1;
428         }
429
430         ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
431         if (ret < 0) {
432                 TALLOC_FREE(config);
433                 return ret;
434         }
435
436         ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
437         if (ret < 0) {
438                 TALLOC_FREE(config);
439                 return ret;
440         }
441
442         enumval = lp_parm_enum(SNUM(handle->conn),
443                                "nfs4acl_xattr",
444                                "encoding",
445                                nfs4acl_encoding,
446                                NFS4ACL_ENCODING_NDR);
447         if (enumval == -1) {
448                 DBG_ERR("Invalid \"nfs4acl_xattr:encoding\" parameter\n");
449                 return -1;
450         }
451         config->encoding = (enum nfs4acl_encoding)enumval;
452
453         switch (config->encoding) {
454         case NFS4ACL_ENCODING_NDR:
455         default:
456                 default_xattr_name = NFS4ACL_NDR_XATTR_NAME;
457                 break;
458         }
459
460         nfs_version = (unsigned)lp_parm_int(SNUM(handle->conn),
461                                             "nfs4acl_xattr",
462                                             "version",
463                                             40);
464         switch (nfs_version) {
465         case 40:
466                 config->nfs_version = ACL4_XATTR_VERSION_40;
467                 break;
468         default:
469                 config->nfs_version = ACL4_XATTR_VERSION_DEFAULT;
470                 break;
471         }
472
473         config->default_acl_style = lp_parm_enum(SNUM(handle->conn),
474                                                  "nfs4acl_xattr",
475                                                  "default acl style",
476                                                  default_acl_style_list,
477                                                  DEFAULT_ACL_EVERYONE);
478
479         config->xattr_name = lp_parm_talloc_string(config,
480                                                    SNUM(handle->conn),
481                                                    "nfs4acl_xattr",
482                                                    "xattr_name",
483                                                    default_xattr_name);
484
485         SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct nfs4acl_config,
486                                 return -1);
487
488         /*
489          * Ensure we have the parameters correct if we're using this module.
490          */
491         DBG_NOTICE("Setting 'inherit acls = true', "
492                    "'dos filemode = true', "
493                    "'force unknown acl user = true', "
494                    "'create mask = 0666', "
495                    "'directory mask = 0777' and "
496                    "'store dos attributes = yes' "
497                    "for service [%s]\n", service);
498
499         lp_do_parameter(SNUM(handle->conn), "inherit acls", "true");
500         lp_do_parameter(SNUM(handle->conn), "dos filemode", "true");
501         lp_do_parameter(SNUM(handle->conn), "force unknown acl user", "true");
502         lp_do_parameter(SNUM(handle->conn), "create mask", "0666");
503         lp_do_parameter(SNUM(handle->conn), "directory mask", "0777");
504         lp_do_parameter(SNUM(handle->conn), "store dos attributes", "yes");
505
506         return 0;
507 }
508
509 /*
510    As long as Samba does not support an exiplicit method for a module
511    to define conflicting vfs methods, we should override all conflicting
512    methods here.  That way, we know we are using the NFSv4 storage
513
514    Function declarations taken from vfs_solarisacl
515 */
516
517 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_file(vfs_handle_struct *handle,
518                                         const struct smb_filename *smb_fname,
519                                         SMB_ACL_TYPE_T type,
520                                         TALLOC_CTX *mem_ctx)
521 {
522         return (SMB_ACL_T)NULL;
523 }
524
525 static SMB_ACL_T nfs4acl_xattr_fail__sys_acl_get_fd(vfs_handle_struct *handle,
526                                                     files_struct *fsp,
527                                                     TALLOC_CTX *mem_ctx)
528 {
529         return (SMB_ACL_T)NULL;
530 }
531
532 static int nfs4acl_xattr_fail__sys_acl_set_file(vfs_handle_struct *handle,
533                                          const struct smb_filename *smb_fname,
534                                          SMB_ACL_TYPE_T type,
535                                          SMB_ACL_T theacl)
536 {
537         return -1;
538 }
539
540 static int nfs4acl_xattr_fail__sys_acl_set_fd(vfs_handle_struct *handle,
541                                        files_struct *fsp,
542                                        SMB_ACL_T theacl)
543 {
544         return -1;
545 }
546
547 static int nfs4acl_xattr_fail__sys_acl_delete_def_file(vfs_handle_struct *handle,
548                         const struct smb_filename *smb_fname)
549 {
550         return -1;
551 }
552
553 static int nfs4acl_xattr_fail__sys_acl_blob_get_file(vfs_handle_struct *handle,
554                         const struct smb_filename *smb_fname,
555                         TALLOC_CTX *mem_ctx,
556                         char **blob_description,
557                         DATA_BLOB *blob)
558 {
559         return -1;
560 }
561
562 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)
563 {
564         return -1;
565 }
566
567 /* VFS operations structure */
568
569 static struct vfs_fn_pointers nfs4acl_xattr_fns = {
570         .connect_fn = nfs4acl_connect,
571         .fget_nt_acl_fn = nfs4acl_xattr_fget_nt_acl,
572         .get_nt_acl_fn = nfs4acl_xattr_get_nt_acl,
573         .fset_nt_acl_fn = nfs4acl_xattr_fset_nt_acl,
574
575         .sys_acl_get_file_fn = nfs4acl_xattr_fail__sys_acl_get_file,
576         .sys_acl_get_fd_fn = nfs4acl_xattr_fail__sys_acl_get_fd,
577         .sys_acl_blob_get_file_fn = nfs4acl_xattr_fail__sys_acl_blob_get_file,
578         .sys_acl_blob_get_fd_fn = nfs4acl_xattr_fail__sys_acl_blob_get_fd,
579         .sys_acl_set_file_fn = nfs4acl_xattr_fail__sys_acl_set_file,
580         .sys_acl_set_fd_fn = nfs4acl_xattr_fail__sys_acl_set_fd,
581         .sys_acl_delete_def_file_fn = nfs4acl_xattr_fail__sys_acl_delete_def_file,
582 };
583
584 NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *);
585 NTSTATUS vfs_nfs4acl_xattr_init(TALLOC_CTX *ctx)
586 {
587         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "nfs4acl_xattr",
588                                 &nfs4acl_xattr_fns);
589 }