s3: VFS: fake_acls: Add fake_acls_sys_acl_delete_def_fd().
[samba.git] / source3 / modules / vfs_fake_acls.c
1 /* 
2  * Fake ACLs VFS module.  Implements passthrough operation of all VFS
3  * calls to disk functions, except for file ownership and ACLs, which
4  * are stored in xattrs.
5  *
6  * Copyright (C) Tim Potter, 1999-2000
7  * Copyright (C) Alexander Bokovoy, 2002
8  * Copyright (C) Andrew Bartlett, 2002,2012
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 #include "includes.h"
25 #include "smbd/smbd.h"
26 #include "system/filesys.h"
27 #include "auth.h"
28 #include "librpc/gen_ndr/ndr_smb_acl.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_VFS
32
33 #define FAKE_UID "system.fake_uid"
34 #define FAKE_GID "system.fake_gid"
35 #define FAKE_ACL_ACCESS_XATTR "system.fake_access_acl"
36 #define FAKE_ACL_DEFAULT_XATTR "system.fake_default_acl"
37
38 static int fake_acls_uid(vfs_handle_struct *handle,
39                          struct smb_filename *smb_fname,
40                          uid_t *uid)
41 {
42         ssize_t size;
43         uint8_t uid_buf[4];
44         size = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
45                         FAKE_UID, uid_buf, sizeof(uid_buf));
46         if (size == -1 && errno == ENOATTR) {
47                 return 0;
48         }
49         if (size != 4) {
50                 return -1;
51         }
52         *uid = IVAL(uid_buf, 0);
53         return 0;
54 }
55
56 static int fake_acls_gid(vfs_handle_struct *handle,
57                          struct smb_filename *smb_fname,
58                          uid_t *gid)
59 {
60         ssize_t size;
61         uint8_t gid_buf[4];
62
63         size = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
64                         FAKE_GID, gid_buf, sizeof(gid_buf));
65         if (size == -1 && errno == ENOATTR) {
66                 return 0;
67         }
68         if (size != 4) {
69                 return -1;
70         }
71         *gid = IVAL(gid_buf, 0);
72         return 0;
73 }
74
75 static int fake_acls_fuid(vfs_handle_struct *handle,
76                            files_struct *fsp,
77                            uid_t *uid)
78 {
79         ssize_t size;
80         uint8_t uid_buf[4];
81
82         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf));
83         if (size == -1 && errno == ENOATTR) {
84                 return 0;
85         }
86         if (size != 4) {
87                 return -1;
88         }
89         *uid = IVAL(uid_buf, 0);
90         return 0;
91 }
92
93 static int fake_acls_fgid(vfs_handle_struct *handle,
94                            files_struct *fsp,
95                           uid_t *gid)
96 {
97         ssize_t size;
98         uint8_t gid_buf[4];
99
100         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf));
101         if (size == -1 && errno == ENOATTR) {
102                 return 0;
103         }
104         if (size != 4) {
105                 return -1;
106         }
107         *gid = IVAL(gid_buf, 0);
108         return 0;
109 }
110
111 static int fake_acls_stat(vfs_handle_struct *handle,
112                            struct smb_filename *smb_fname)
113 {
114         int ret = -1;
115
116         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
117         if (ret == 0) {
118                 TALLOC_CTX *frame = talloc_stackframe();
119                 char *path;
120                 struct smb_filename smb_fname_base = {
121                         .base_name = smb_fname->base_name
122                 };
123                 NTSTATUS status;
124                 /*
125                  * As we're calling getxattr directly here
126                  * we need to use only the base_name, not
127                  * the full name containing any stream name.
128                  */
129                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
130                 if (!NT_STATUS_IS_OK(status)) {
131                         errno = map_errno_from_nt_status(status);
132                         TALLOC_FREE(frame);
133                         return -1;
134                 }
135                 
136                 ret = fake_acls_uid(handle, &smb_fname_base,
137                                         &smb_fname->st.st_ex_uid);
138                 if (ret != 0) {
139                         TALLOC_FREE(frame);
140                         return ret;
141                 }
142                 ret = fake_acls_gid(handle, &smb_fname_base,
143                                         &smb_fname->st.st_ex_gid);
144                 if (ret != 0) {
145                         TALLOC_FREE(frame);
146                         return ret;
147                 }
148                 TALLOC_FREE(frame);
149         }
150
151         return ret;
152 }
153
154 static int fake_acls_lstat(vfs_handle_struct *handle,
155                            struct smb_filename *smb_fname)
156 {
157         int ret = -1;
158
159         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
160         if (ret == 0) {
161                 TALLOC_CTX *frame = talloc_stackframe();
162                 char *path;
163                 struct smb_filename smb_fname_base = {
164                         .base_name = smb_fname->base_name
165                 };
166                 NTSTATUS status;
167                 /*
168                  * As we're calling getxattr directly here
169                  * we need to use only the base_name, not
170                  * the full name containing any stream name.
171                  */
172                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
173                 if (!NT_STATUS_IS_OK(status)) {
174                         errno = map_errno_from_nt_status(status);
175                         TALLOC_FREE(frame);
176                         return -1;
177                 }
178
179                 /* This isn't quite right (calling getxattr not
180                  * lgetxattr), but for the test purposes of this
181                  * module (fake NT ACLs from windows clients), it is
182                  * close enough.  We removed the l*xattr functions
183                  * because linux doesn't support using them, but we
184                  * could fake them in xattr_tdb if we really wanted
185                  * to.  We ignore errors because the link might not point anywhere */
186                 fake_acls_uid(handle, &smb_fname_base,
187                         &smb_fname->st.st_ex_uid);
188                 fake_acls_gid(handle, &smb_fname_base,
189                         &smb_fname->st.st_ex_gid);
190                 TALLOC_FREE(frame);
191         }
192
193         return ret;
194 }
195
196 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
197 {
198         int ret = -1;
199
200         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
201         if (ret == 0) {
202                 ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid);
203                 if (ret != 0) {
204                         return ret;
205                 }
206                 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
207                 if (ret != 0) {
208                         return ret;
209                 }
210         }
211         return ret;
212 }
213
214 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
215 {
216         enum ndr_err_code ndr_err;
217         struct smb_acl_t *acl = talloc(mem_ctx, struct smb_acl_t);
218         if (!acl) {
219                 errno = ENOMEM;
220                 return NULL;
221         }
222
223         ndr_err = ndr_pull_struct_blob(blob, acl, acl, 
224                 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
225
226         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
227                 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
228                           ndr_errstr(ndr_err)));
229                 TALLOC_FREE(acl);
230                 return NULL;
231         }
232         return acl;
233 }
234
235 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
236 {
237         enum ndr_err_code ndr_err;
238         DATA_BLOB blob;
239         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl, 
240                 (ndr_push_flags_fn_t)ndr_push_smb_acl_t);
241
242         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
243                 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
244                           ndr_errstr(ndr_err)));
245                 return data_blob_null;
246         }
247         return blob;
248 }
249
250 static SMB_ACL_T fake_acls_sys_acl_get_file(struct vfs_handle_struct *handle,
251                                 const struct smb_filename *smb_fname,
252                                 SMB_ACL_TYPE_T type,
253                                 TALLOC_CTX *mem_ctx)
254 {
255         DATA_BLOB blob = data_blob_null;
256         ssize_t length;
257         const char *name = NULL;
258         struct smb_acl_t *acl = NULL;
259         TALLOC_CTX *frame = talloc_stackframe();
260         switch (type) {
261         case SMB_ACL_TYPE_ACCESS:
262                 name = FAKE_ACL_ACCESS_XATTR;
263                 break;
264         case SMB_ACL_TYPE_DEFAULT:
265                 name = FAKE_ACL_DEFAULT_XATTR;
266                 break;
267         }
268
269         do {
270                 blob.length += 1000;
271                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
272                 if (!blob.data) {
273                         errno = ENOMEM;
274                         TALLOC_FREE(frame);
275                         return NULL;
276                 }
277                 length = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
278                                 name, blob.data, blob.length);
279                 blob.length = length;
280         } while (length == -1 && errno == ERANGE);
281         if (length == -1 && errno == ENOATTR) {
282                 TALLOC_FREE(frame);
283                 return NULL;
284         }
285         if (length != -1) {
286                 acl = fake_acls_blob2acl(&blob, mem_ctx);
287         }
288         TALLOC_FREE(frame);
289         return acl;
290 }
291
292 static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle,
293                                           files_struct *fsp,
294                                           TALLOC_CTX *mem_ctx)
295 {
296         DATA_BLOB blob = data_blob_null;
297         ssize_t length;
298         const char *name = FAKE_ACL_ACCESS_XATTR;
299         struct smb_acl_t *acl = NULL;
300         TALLOC_CTX *frame = talloc_stackframe();
301                 
302         do {
303                 blob.length += 1000;
304                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
305                 if (!blob.data) {
306                         errno = ENOMEM;
307                         TALLOC_FREE(frame);
308                         return NULL;
309                 }
310                 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length);
311                 blob.length = length;
312         } while (length == -1 && errno == ERANGE);
313         if (length == -1 && errno == ENOATTR) {
314                 TALLOC_FREE(frame);
315                 return NULL;
316         }
317         if (length != -1) {
318                 acl = fake_acls_blob2acl(&blob, mem_ctx);
319         }
320         TALLOC_FREE(frame);
321         return acl;
322 }
323
324 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle,
325                                     struct files_struct *fsp,
326                                     SMB_ACL_TYPE_T type,
327                                     SMB_ACL_T theacl)
328 {
329         int ret;
330         const char *name = NULL;
331         TALLOC_CTX *frame = talloc_stackframe();
332         DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
333         if (!blob.data) {
334                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
335                 TALLOC_FREE(frame);
336                 errno = EINVAL;
337                 return -1;
338         }
339
340         switch (type) {
341         case SMB_ACL_TYPE_ACCESS:
342                 name = FAKE_ACL_ACCESS_XATTR;
343                 break;
344         case SMB_ACL_TYPE_DEFAULT:
345                 name = FAKE_ACL_DEFAULT_XATTR;
346                 break;
347         default:
348                 errno = EINVAL;
349                 return -1;
350         }
351
352         ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
353         TALLOC_FREE(frame);
354         return ret;
355 }
356
357 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle,
358                         const struct smb_filename *smb_fname)
359 {
360         int ret;
361         const char *name = FAKE_ACL_DEFAULT_XATTR;
362
363         if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
364                 errno = EINVAL;
365                 return -1;
366         }
367
368         ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, smb_fname->fsp, name);
369         if (ret == -1 && errno == ENOATTR) {
370                 ret = 0;
371                 errno = 0;
372         }
373
374         return ret;
375 }
376
377 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct *handle,
378                         struct files_struct *fsp)
379 {
380         int ret;
381         const char *name = FAKE_ACL_DEFAULT_XATTR;
382
383         if (!fsp->fsp_flags.is_directory) {
384                 errno = EINVAL;
385                 return -1;
386         }
387
388         ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
389         if (ret == -1 && errno == ENOATTR) {
390                 ret = 0;
391                 errno = 0;
392         }
393
394         return ret;
395 }
396
397 static int fake_acls_lchown(vfs_handle_struct *handle,
398                         const struct smb_filename *smb_fname,
399                         uid_t uid,
400                         gid_t gid)
401 {
402         int ret;
403         uint8_t id_buf[4];
404         if (uid != -1) {
405                 uid_t current_uid = get_current_uid(handle->conn);
406
407                 if (current_uid != 0 && current_uid != uid) {
408                         return EACCES;
409                 }
410
411                 /* This isn't quite right (calling setxattr not
412                  * lsetxattr), but for the test purposes of this
413                  * module (fake NT ACLs from windows clients), it is
414                  * close enough.  We removed the l*xattr functions
415                  * because linux doesn't support using them, but we
416                  * could fake them in xattr_tdb if we really wanted
417                  * to.
418                  */
419                 SIVAL(id_buf, 0, uid);
420                 ret = SMB_VFS_NEXT_FSETXATTR(handle,
421                                 smb_fname->fsp,
422                                 FAKE_UID,
423                                 id_buf,
424                                 sizeof(id_buf),
425                                 0);
426                 if (ret != 0) {
427                         return ret;
428                 }
429         }
430         if (gid != -1) {
431                 SIVAL(id_buf, 0, gid);
432                 ret = SMB_VFS_NEXT_FSETXATTR(handle,
433                                 smb_fname->fsp,
434                                 FAKE_GID,
435                                 id_buf,
436                                 sizeof(id_buf),
437                                 0);
438                 if (ret != 0) {
439                         return ret;
440                 }
441         }
442         return 0;
443 }
444
445 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
446 {
447         int ret;
448         uint8_t id_buf[4];
449         if (uid != -1) {
450                 uid_t current_uid = get_current_uid(handle->conn);
451
452                 if (current_uid != 0 && current_uid != uid) {
453                         return EACCES;
454                 }
455
456                 SIVAL(id_buf, 0, uid);
457                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
458                 if (ret != 0) {
459                         return ret;
460                 }
461         }
462         if (gid != -1) {
463                 SIVAL(id_buf, 0, gid);
464                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
465                 if (ret != 0) {
466                         return ret;
467                 }
468         }
469         return 0;
470 }
471
472 /*
473  * Implement the chmod uid/mask/other mode changes on a fake ACL.
474  */
475
476 static int fake_acl_process_chmod(SMB_ACL_T *pp_the_acl,
477                                 uid_t owner,
478                                 mode_t mode)
479 {
480         bool got_mask = false;
481         int entry_id = SMB_ACL_FIRST_ENTRY;
482         mode_t umode = 0;
483         mode_t mmode = 0;
484         mode_t omode = 0;
485         int ret = -1;
486         SMB_ACL_T the_acl = *pp_the_acl;
487
488         /* Split the mode into u/mask/other masks. */
489         umode = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
490         mmode = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
491         omode = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
492
493         while (1) {
494                 SMB_ACL_ENTRY_T entry;
495                 SMB_ACL_TAG_T tagtype;
496                 SMB_ACL_PERMSET_T permset;
497                 uid_t *puid = NULL;
498
499                 ret = sys_acl_get_entry(the_acl,
500                                         entry_id,
501                                         &entry);
502                 if (ret == 0) {
503                         /* End of ACL */
504                         break;
505                 }
506                 if (ret == -1) {
507                         return -1;
508                 }
509
510                 ret = sys_acl_get_tag_type(entry, &tagtype);
511                 if (ret == -1) {
512                         return -1;
513                 }
514                 ret = sys_acl_get_permset(entry, &permset);
515                 if (ret == -1) {
516                         return -1;
517                 }
518                 switch (tagtype) {
519                         case SMB_ACL_USER_OBJ:
520                                 ret = map_acl_perms_to_permset(umode, &permset);
521                                 if (ret == -1) {
522                                         return -1;
523                                 }
524                                 break;
525                         case SMB_ACL_USER:
526                                 puid = (uid_t *)sys_acl_get_qualifier(entry);
527                                 if (puid == NULL) {
528                                         return -1;
529                                 }
530                                 if (owner != *puid) {
531                                         break;
532                                 }
533                                 ret = map_acl_perms_to_permset(umode, &permset);
534                                 if (ret == -1) {
535                                         return -1;
536                                 }
537                                 break;
538                         case SMB_ACL_GROUP_OBJ:
539                         case SMB_ACL_GROUP:
540                                 /* Ignore all group entries. */
541                                 break;
542                         case SMB_ACL_MASK:
543                                 ret = map_acl_perms_to_permset(mmode, &permset);
544                                 if (ret == -1) {
545                                         return -1;
546                                 }
547                                 got_mask = true;
548                                 break;
549                         case SMB_ACL_OTHER:
550                                 ret = map_acl_perms_to_permset(omode, &permset);
551                                 if (ret == -1) {
552                                         return -1;
553                                 }
554                                 break;
555                         default:
556                                 errno = EINVAL;
557                                 return -1;
558                 }
559                 ret = sys_acl_set_permset(entry, permset);
560                 if (ret == -1) {
561                         return -1;
562                 }
563                 /* Move to next entry. */
564                 entry_id = SMB_ACL_NEXT_ENTRY;
565         }
566
567         /*
568          * If we didn't see a mask entry, add one.
569          */
570
571         if (!got_mask) {
572                 SMB_ACL_ENTRY_T mask_entry;
573                 SMB_ACL_PERMSET_T mask_permset;
574                 ret = sys_acl_create_entry(&the_acl, &mask_entry);
575                 if (ret == -1) {
576                         return -1;
577                 }
578                 ret = map_acl_perms_to_permset(mmode, &mask_permset);
579                 if (ret == -1) {
580                         return -1;
581                 }
582                 ret = sys_acl_set_permset(mask_entry, mask_permset);
583                 if (ret == -1) {
584                         return -1;
585                 }
586                 ret = sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK);
587                 if (ret == -1) {
588                         return -1;
589                 }
590                 /* In case we were realloced and moved. */
591                 *pp_the_acl = the_acl;
592         }
593
594         return 0;
595 }
596
597 static int fake_acls_fchmod(vfs_handle_struct *handle,
598                         files_struct *fsp,
599                         mode_t mode)
600 {
601         TALLOC_CTX *frame = talloc_stackframe();
602         int ret = -1;
603         SMB_ACL_T the_acl = NULL;
604
605         /*
606          * Passthrough first to preserve the
607          * S_ISUID | S_ISGID | S_ISVTX
608          * bits.
609          */
610
611         ret = SMB_VFS_NEXT_FCHMOD(handle,
612                                 fsp,
613                                 mode);
614         if (ret == -1) {
615                 TALLOC_FREE(frame);
616                 return -1;
617         }
618
619         the_acl = fake_acls_sys_acl_get_fd(handle,
620                                 fsp,
621                                 talloc_tos());
622         if (the_acl == NULL) {
623                 TALLOC_FREE(frame);
624                 if (errno == ENOATTR) {
625                         /* No ACL on this file. Just passthrough. */
626                         return 0;
627                 }
628                 return -1;
629         }
630         ret = fake_acl_process_chmod(&the_acl,
631                         fsp->fsp_name->st.st_ex_uid,
632                         mode);
633         if (ret == -1) {
634                 TALLOC_FREE(frame);
635                 return -1;
636         }
637         ret = fake_acls_sys_acl_set_fd(handle,
638                                 fsp,
639                                 SMB_ACL_TYPE_ACCESS,
640                                 the_acl);
641         TALLOC_FREE(frame);
642         return ret;
643 }
644
645 static struct vfs_fn_pointers vfs_fake_acls_fns = {
646         .stat_fn = fake_acls_stat,
647         .lstat_fn = fake_acls_lstat,
648         .fstat_fn = fake_acls_fstat,
649         .fchmod_fn = fake_acls_fchmod,
650         .sys_acl_get_file_fn = fake_acls_sys_acl_get_file,
651         .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
652         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
653         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
654         .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
655         .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file,
656         .sys_acl_delete_def_fd_fn = fake_acls_sys_acl_delete_def_fd,
657         .lchown_fn = fake_acls_lchown,
658         .fchown_fn = fake_acls_fchown,
659         
660 };
661
662 static_decl_vfs;
663 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
664 {
665         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
666                                 &vfs_fake_acls_fns);
667 }