s4:librpc: Use C99 initializer for PyGetSetDef in pyrpc
[Samba.git] / source3 / modules / vfs_fake_acls.c
blobba94c8db06f554847bcf0bd6cf7e911c1c84bb15
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.
6 * Copyright (C) Tim Potter, 1999-2000
7 * Copyright (C) Alexander Bokovoy, 2002
8 * Copyright (C) Andrew Bartlett, 2002,2012
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.
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.
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/>.
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"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_VFS
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"
38 static int fake_acls_uid(vfs_handle_struct *handle,
39 struct smb_filename *smb_fname,
40 uid_t *uid)
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;
49 if (size != 4) {
50 return -1;
52 *uid = IVAL(uid_buf, 0);
53 return 0;
56 static int fake_acls_gid(vfs_handle_struct *handle,
57 struct smb_filename *smb_fname,
58 uid_t *gid)
60 ssize_t size;
61 uint8_t gid_buf[4];
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;
68 if (size != 4) {
69 return -1;
71 *gid = IVAL(gid_buf, 0);
72 return 0;
75 static int fake_acls_fuid(vfs_handle_struct *handle,
76 files_struct *fsp,
77 uid_t *uid)
79 ssize_t size;
80 uint8_t uid_buf[4];
82 size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf));
83 if (size == -1 && errno == ENOATTR) {
84 return 0;
86 if (size != 4) {
87 return -1;
89 *uid = IVAL(uid_buf, 0);
90 return 0;
93 static int fake_acls_fgid(vfs_handle_struct *handle,
94 files_struct *fsp,
95 uid_t *gid)
97 ssize_t size;
98 uint8_t gid_buf[4];
100 size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf));
101 if (size == -1 && errno == ENOATTR) {
102 return 0;
104 if (size != 4) {
105 return -1;
107 *gid = IVAL(gid_buf, 0);
108 return 0;
111 static int fake_acls_stat(vfs_handle_struct *handle,
112 struct smb_filename *smb_fname)
114 int ret = -1;
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
123 NTSTATUS status;
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.
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;
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;
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;
148 TALLOC_FREE(frame);
151 return ret;
154 static int fake_acls_lstat(vfs_handle_struct *handle,
155 struct smb_filename *smb_fname)
157 int ret = -1;
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
166 NTSTATUS status;
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.
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;
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);
193 return ret;
196 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
198 int ret = -1;
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;
206 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
207 if (ret != 0) {
208 return ret;
211 return ret;
214 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
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;
223 ndr_err = ndr_pull_struct_blob(blob, acl, acl,
224 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
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;
232 return acl;
235 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
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);
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;
247 return blob;
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)
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;
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;
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;
285 if (length != -1) {
286 acl = fake_acls_blob2acl(&blob, mem_ctx);
288 TALLOC_FREE(frame);
289 return acl;
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)
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();
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;
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;
317 if (length != -1) {
318 acl = fake_acls_blob2acl(&blob, mem_ctx);
320 TALLOC_FREE(frame);
321 return acl;
325 static int fake_acls_sys_acl_set_file(vfs_handle_struct *handle,
326 const struct smb_filename *smb_fname,
327 SMB_ACL_TYPE_T acltype,
328 SMB_ACL_T theacl)
330 int ret;
331 const char *name = NULL;
332 TALLOC_CTX *frame = talloc_stackframe();
333 DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
334 if (!blob.data) {
335 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
336 TALLOC_FREE(frame);
337 errno = EINVAL;
338 return -1;
340 switch (acltype) {
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;
348 ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
349 name, blob.data, blob.length, 0);
350 TALLOC_FREE(frame);
351 return ret;
354 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl)
356 int ret;
357 const char *name = FAKE_ACL_ACCESS_XATTR;
358 TALLOC_CTX *frame = talloc_stackframe();
359 DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
360 if (!blob.data) {
361 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
362 TALLOC_FREE(frame);
363 errno = EINVAL;
364 return -1;
366 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
367 TALLOC_FREE(frame);
368 return ret;
371 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle,
372 const struct smb_filename *smb_fname_in)
374 int ret;
375 const char *name = FAKE_ACL_DEFAULT_XATTR;
376 TALLOC_CTX *frame = talloc_stackframe();
377 struct smb_filename *smb_fname = cp_smb_filename_nostream(talloc_tos(),
378 smb_fname_in);
380 if (smb_fname == NULL) {
381 TALLOC_FREE(frame);
382 errno = ENOMEM;
383 return -1;
386 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
387 if (ret == -1) {
388 TALLOC_FREE(frame);
389 return -1;
392 if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
393 errno = EINVAL;
394 TALLOC_FREE(frame);
395 return -1;
398 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
399 if (ret == -1 && errno == ENOATTR) {
400 ret = 0;
401 errno = 0;
404 TALLOC_FREE(frame);
405 return ret;
408 static int fake_acls_chown(vfs_handle_struct *handle,
409 const struct smb_filename *smb_fname,
410 uid_t uid,
411 gid_t gid)
413 int ret;
414 uint8_t id_buf[4];
415 if (uid != -1) {
416 uid_t current_uid = get_current_uid(handle->conn);
418 if (current_uid != 0 && current_uid != uid) {
419 return EACCES;
422 SIVAL(id_buf, 0, uid);
423 ret = SMB_VFS_NEXT_SETXATTR(handle,
424 smb_fname,
425 FAKE_UID,
426 id_buf,
427 sizeof(id_buf),
429 if (ret != 0) {
430 return ret;
433 if (gid != -1) {
434 SIVAL(id_buf, 0, gid);
435 ret = SMB_VFS_NEXT_SETXATTR(handle,
436 smb_fname,
437 FAKE_GID,
438 id_buf,
439 sizeof(id_buf),
441 if (ret != 0) {
442 return ret;
445 return 0;
448 static int fake_acls_lchown(vfs_handle_struct *handle,
449 const struct smb_filename *smb_fname,
450 uid_t uid,
451 gid_t gid)
453 int ret;
454 uint8_t id_buf[4];
455 if (uid != -1) {
456 uid_t current_uid = get_current_uid(handle->conn);
458 if (current_uid != 0 && current_uid != uid) {
459 return EACCES;
462 /* This isn't quite right (calling setxattr not
463 * lsetxattr), but for the test purposes of this
464 * module (fake NT ACLs from windows clients), it is
465 * close enough. We removed the l*xattr functions
466 * because linux doesn't support using them, but we
467 * could fake them in xattr_tdb if we really wanted
468 * to.
470 SIVAL(id_buf, 0, uid);
471 ret = SMB_VFS_NEXT_SETXATTR(handle,
472 smb_fname,
473 FAKE_UID,
474 id_buf,
475 sizeof(id_buf),
477 if (ret != 0) {
478 return ret;
481 if (gid != -1) {
482 SIVAL(id_buf, 0, gid);
483 ret = SMB_VFS_NEXT_SETXATTR(handle,
484 smb_fname,
485 FAKE_GID,
486 id_buf,
487 sizeof(id_buf),
489 if (ret != 0) {
490 return ret;
493 return 0;
496 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
498 int ret;
499 uint8_t id_buf[4];
500 if (uid != -1) {
501 uid_t current_uid = get_current_uid(handle->conn);
503 if (current_uid != 0 && current_uid != uid) {
504 return EACCES;
507 SIVAL(id_buf, 0, uid);
508 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
509 if (ret != 0) {
510 return ret;
513 if (gid != -1) {
514 SIVAL(id_buf, 0, gid);
515 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
516 if (ret != 0) {
517 return ret;
520 return 0;
524 * Implement the chmod uid/mask/other mode changes on a fake ACL.
527 static int fake_acl_process_chmod(SMB_ACL_T *pp_the_acl,
528 uid_t owner,
529 mode_t mode)
531 bool got_mask = false;
532 int entry_id = SMB_ACL_FIRST_ENTRY;
533 mode_t umode = 0;
534 mode_t mmode = 0;
535 mode_t omode = 0;
536 int ret = -1;
537 SMB_ACL_T the_acl = *pp_the_acl;
539 /* Split the mode into u/mask/other masks. */
540 umode = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
541 mmode = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
542 omode = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
544 while (1) {
545 SMB_ACL_ENTRY_T entry;
546 SMB_ACL_TAG_T tagtype;
547 SMB_ACL_PERMSET_T permset;
548 uid_t *puid = NULL;
550 ret = sys_acl_get_entry(the_acl,
551 entry_id,
552 &entry);
553 if (ret == 0) {
554 /* End of ACL */
555 break;
557 if (ret == -1) {
558 return -1;
561 ret = sys_acl_get_tag_type(entry, &tagtype);
562 if (ret == -1) {
563 return -1;
565 ret = sys_acl_get_permset(entry, &permset);
566 if (ret == -1) {
567 return -1;
569 switch (tagtype) {
570 case SMB_ACL_USER_OBJ:
571 ret = map_acl_perms_to_permset(umode, &permset);
572 if (ret == -1) {
573 return -1;
575 break;
576 case SMB_ACL_USER:
577 puid = (uid_t *)sys_acl_get_qualifier(entry);
578 if (puid == NULL) {
579 return -1;
581 if (owner != *puid) {
582 break;
584 ret = map_acl_perms_to_permset(umode, &permset);
585 if (ret == -1) {
586 return -1;
588 break;
589 case SMB_ACL_GROUP_OBJ:
590 case SMB_ACL_GROUP:
591 /* Ignore all group entries. */
592 break;
593 case SMB_ACL_MASK:
594 ret = map_acl_perms_to_permset(mmode, &permset);
595 if (ret == -1) {
596 return -1;
598 got_mask = true;
599 break;
600 case SMB_ACL_OTHER:
601 ret = map_acl_perms_to_permset(omode, &permset);
602 if (ret == -1) {
603 return -1;
605 break;
606 default:
607 errno = EINVAL;
608 return -1;
610 ret = sys_acl_set_permset(entry, permset);
611 if (ret == -1) {
612 return -1;
614 /* Move to next entry. */
615 entry_id = SMB_ACL_NEXT_ENTRY;
619 * If we didn't see a mask entry, add one.
622 if (!got_mask) {
623 SMB_ACL_ENTRY_T mask_entry;
624 SMB_ACL_PERMSET_T mask_permset;
625 ret = sys_acl_create_entry(&the_acl, &mask_entry);
626 if (ret == -1) {
627 return -1;
629 ret = map_acl_perms_to_permset(mmode, &mask_permset);
630 if (ret == -1) {
631 return -1;
633 ret = sys_acl_set_permset(mask_entry, mask_permset);
634 if (ret == -1) {
635 return -1;
637 ret = sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK);
638 if (ret == -1) {
639 return -1;
641 /* In case we were realloced and moved. */
642 *pp_the_acl = the_acl;
645 return 0;
648 static int fake_acls_chmod(vfs_handle_struct *handle,
649 const struct smb_filename *smb_fname_in,
650 mode_t mode)
652 TALLOC_CTX *frame = talloc_stackframe();
653 int ret = -1;
654 SMB_ACL_T the_acl = NULL;
655 struct smb_filename *smb_fname = cp_smb_filename_nostream(talloc_tos(),
656 smb_fname_in);
658 if (smb_fname == NULL) {
659 TALLOC_FREE(frame);
660 return -1;
664 * Passthrough first to preserve the
665 * S_ISUID | S_ISGID | S_ISVTX
666 * bits.
669 ret = SMB_VFS_NEXT_CHMOD(handle,
670 smb_fname,
671 mode);
672 if (ret == -1) {
673 TALLOC_FREE(frame);
674 return -1;
677 the_acl = fake_acls_sys_acl_get_file(handle,
678 smb_fname,
679 SMB_ACL_TYPE_ACCESS,
680 talloc_tos());
681 if (the_acl == NULL) {
682 TALLOC_FREE(frame);
683 if (errno == ENOATTR) {
684 /* No ACL on this file. Just passthrough. */
685 return 0;
687 return -1;
689 ret = fake_acl_process_chmod(&the_acl,
690 smb_fname->st.st_ex_uid,
691 mode);
692 if (ret == -1) {
693 TALLOC_FREE(frame);
694 return -1;
696 ret = fake_acls_sys_acl_set_file(handle,
697 smb_fname,
698 SMB_ACL_TYPE_ACCESS,
699 the_acl);
700 TALLOC_FREE(frame);
701 return ret;
704 static int fake_acls_fchmod(vfs_handle_struct *handle,
705 files_struct *fsp,
706 mode_t mode)
708 TALLOC_CTX *frame = talloc_stackframe();
709 int ret = -1;
710 SMB_ACL_T the_acl = NULL;
713 * Passthrough first to preserve the
714 * S_ISUID | S_ISGID | S_ISVTX
715 * bits.
718 ret = SMB_VFS_NEXT_FCHMOD(handle,
719 fsp,
720 mode);
721 if (ret == -1) {
722 TALLOC_FREE(frame);
723 return -1;
726 the_acl = fake_acls_sys_acl_get_fd(handle,
727 fsp,
728 talloc_tos());
729 if (the_acl == NULL) {
730 TALLOC_FREE(frame);
731 if (errno == ENOATTR) {
732 /* No ACL on this file. Just passthrough. */
733 return 0;
735 return -1;
737 ret = fake_acl_process_chmod(&the_acl,
738 fsp->fsp_name->st.st_ex_uid,
739 mode);
740 if (ret == -1) {
741 TALLOC_FREE(frame);
742 return -1;
744 ret = fake_acls_sys_acl_set_fd(handle,
745 fsp,
746 the_acl);
747 TALLOC_FREE(frame);
748 return ret;
751 static struct vfs_fn_pointers vfs_fake_acls_fns = {
752 .stat_fn = fake_acls_stat,
753 .lstat_fn = fake_acls_lstat,
754 .fstat_fn = fake_acls_fstat,
755 .chmod_fn = fake_acls_chmod,
756 .fchmod_fn = fake_acls_fchmod,
757 .sys_acl_get_file_fn = fake_acls_sys_acl_get_file,
758 .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
759 .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
760 .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
761 .sys_acl_set_file_fn = fake_acls_sys_acl_set_file,
762 .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
763 .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file,
764 .chown_fn = fake_acls_chown,
765 .lchown_fn = fake_acls_lchown,
766 .fchown_fn = fake_acls_fchown,
770 static_decl_vfs;
771 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
773 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
774 &vfs_fake_acls_fns);