autobuild: Run the samba-minimal-smbd build jobs with -j 2
[samba4-gss.git] / source3 / modules / vfs_fake_acls.c
blobd2b969765821c372a621b1f5797c7334f0f88fd8
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 struct in_pathref_data {
39 bool calling_pathref_fsp;
42 static int fake_acls_fuid(vfs_handle_struct *handle,
43 files_struct *fsp,
44 uid_t *uid)
46 ssize_t size;
47 uint8_t uid_buf[4];
49 size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf));
50 if (size == -1 && ((errno == ENOATTR) || (errno == EBADF))) {
51 return 0;
53 if (size != 4) {
54 return -1;
56 *uid = IVAL(uid_buf, 0);
57 return 0;
60 static int fake_acls_fgid(vfs_handle_struct *handle,
61 files_struct *fsp,
62 uid_t *gid)
64 ssize_t size;
65 uint8_t gid_buf[4];
67 size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf));
68 if (size == -1 && ((errno == ENOATTR) || (errno == EBADF))) {
69 return 0;
71 if (size != 4) {
72 return -1;
74 *gid = IVAL(gid_buf, 0);
75 return 0;
78 static int fake_acls_stat(vfs_handle_struct *handle,
79 struct smb_filename *smb_fname)
81 int ret = -1;
82 struct in_pathref_data *prd = NULL;
83 struct smb_filename *smb_fname_cp = NULL;
84 struct files_struct *fsp = NULL;
86 SMB_VFS_HANDLE_GET_DATA(handle,
87 prd,
88 struct in_pathref_data,
89 return -1);
91 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
92 if (ret != 0) {
93 return ret;
96 if (smb_fname->fsp != NULL) {
97 fsp = metadata_fsp(smb_fname->fsp);
98 } else {
99 NTSTATUS status;
102 * Ensure openat_pathref_fsp()
103 * can't recurse into fake_acls_stat().
104 * openat_pathref_fsp() doesn't care
105 * about the uid/gid values, it only
106 * wants a valid/invalid stat answer
107 * and we know smb_fname exists as
108 * the SMB_VFS_NEXT_STAT() returned
109 * zero above.
111 if (prd->calling_pathref_fsp) {
112 return 0;
116 * openat_pathref_fsp() expects a talloc'ed
117 * smb_filename. stat can be passed a struct
118 * from the stack. Make a talloc'ed copy
119 * so openat_pathref_fsp() can add its
120 * destructor.
122 smb_fname_cp = cp_smb_filename(talloc_tos(),
123 smb_fname);
124 if (smb_fname_cp == NULL) {
125 errno = ENOMEM;
126 return -1;
129 if (fsp_get_pathref_fd(handle->conn->cwd_fsp) == -1) {
131 * No tcon around, fail as if we don't have
132 * the EAs
134 status = NT_STATUS_INVALID_HANDLE;
135 } else {
136 /* Recursion guard. */
137 prd->calling_pathref_fsp = true;
138 status = openat_pathref_fsp(handle->conn->cwd_fsp,
139 smb_fname_cp);
140 /* End recursion guard. */
141 prd->calling_pathref_fsp = false;
144 if (!NT_STATUS_IS_OK(status)) {
146 * Ignore errors here. We know
147 * the path exists (the SMB_VFS_NEXT_STAT()
148 * above succeeded. So being unable to
149 * open a pathref fsp can be due to a
150 * range of errors (startup path beginning
151 * with '/' for example, path = ".." when
152 * enumerating a directory. Just treat this
153 * the same way as the path not having the
154 * FAKE_UID or FAKE_GID EA's present. For the
155 * test purposes of this module (fake NT ACLs
156 * from windows clients) this is close enough.
157 * Just report for debugging purposes.
159 DBG_DEBUG("Unable to get pathref fsp on %s. "
160 "Error %s\n",
161 smb_fname_str_dbg(smb_fname_cp),
162 nt_errstr(status));
163 TALLOC_FREE(smb_fname_cp);
164 return 0;
166 fsp = smb_fname_cp->fsp;
169 ret = fake_acls_fuid(handle,
170 fsp,
171 &smb_fname->st.st_ex_uid);
172 if (ret != 0) {
173 TALLOC_FREE(smb_fname_cp);
174 return ret;
176 ret = fake_acls_fgid(handle,
177 fsp,
178 &smb_fname->st.st_ex_gid);
179 if (ret != 0) {
180 TALLOC_FREE(smb_fname_cp);
181 return ret;
183 TALLOC_FREE(smb_fname_cp);
184 return ret;
187 static int fake_acls_lstat(vfs_handle_struct *handle,
188 struct smb_filename *smb_fname)
190 int ret = -1;
191 struct in_pathref_data *prd = NULL;
193 SMB_VFS_HANDLE_GET_DATA(handle,
194 prd,
195 struct in_pathref_data,
196 return -1);
198 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
199 if (ret == 0) {
200 struct smb_filename *smb_fname_base = NULL;
201 SMB_STRUCT_STAT sbuf = { 0 };
202 NTSTATUS status;
205 * Ensure synthetic_pathref()
206 * can't recurse into fake_acls_lstat().
207 * synthetic_pathref() doesn't care
208 * about the uid/gid values, it only
209 * wants a valid/invalid stat answer
210 * and we know smb_fname exists as
211 * the SMB_VFS_NEXT_LSTAT() returned
212 * zero above.
214 if (prd->calling_pathref_fsp) {
215 return 0;
218 /* Recursion guard. */
219 prd->calling_pathref_fsp = true;
220 status = synthetic_pathref(talloc_tos(),
221 handle->conn->cwd_fsp,
222 smb_fname->base_name,
223 NULL,
224 &sbuf,
225 smb_fname->twrp,
226 0, /* we want stat, not lstat. */
227 &smb_fname_base);
228 /* End recursion guard. */
229 prd->calling_pathref_fsp = false;
230 if (NT_STATUS_IS_OK(status)) {
232 * This isn't quite right (calling fgetxattr not
233 * lgetxattr), but for the test purposes of this
234 * module (fake NT ACLs from windows clients), it is
235 * close enough. We removed the l*xattr functions
236 * because linux doesn't support using them, but we
237 * could fake them in xattr_tdb if we really wanted
238 * to. We ignore errors because the link might not
239 * point anywhere */
240 fake_acls_fuid(handle,
241 smb_fname_base->fsp,
242 &smb_fname->st.st_ex_uid);
243 fake_acls_fgid(handle,
244 smb_fname_base->fsp,
245 &smb_fname->st.st_ex_gid);
247 TALLOC_FREE(smb_fname_base);
250 return ret;
253 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
255 int ret = -1;
257 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
258 if (ret == 0) {
259 ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid);
260 if (ret != 0) {
261 return ret;
263 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
264 if (ret != 0) {
265 return ret;
268 return ret;
271 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
273 enum ndr_err_code ndr_err;
274 struct smb_acl_t *acl = talloc(mem_ctx, struct smb_acl_t);
275 if (!acl) {
276 errno = ENOMEM;
277 return NULL;
280 ndr_err = ndr_pull_struct_blob(blob, acl, acl,
281 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
283 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
285 ndr_errstr(ndr_err)));
286 TALLOC_FREE(acl);
287 return NULL;
289 return acl;
292 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
294 enum ndr_err_code ndr_err;
295 DATA_BLOB blob;
296 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl,
297 (ndr_push_flags_fn_t)ndr_push_smb_acl_t);
299 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
300 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
301 ndr_errstr(ndr_err)));
302 return data_blob_null;
304 return blob;
307 static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle,
308 files_struct *fsp,
309 SMB_ACL_TYPE_T type,
310 TALLOC_CTX *mem_ctx)
312 DATA_BLOB blob = data_blob_null;
313 ssize_t length;
314 const char *name = NULL;
315 struct smb_acl_t *acl = NULL;
316 TALLOC_CTX *frame = talloc_stackframe();
318 switch (type) {
319 case SMB_ACL_TYPE_ACCESS:
320 name = FAKE_ACL_ACCESS_XATTR;
321 break;
322 case SMB_ACL_TYPE_DEFAULT:
323 name = FAKE_ACL_DEFAULT_XATTR;
324 break;
325 default:
326 DBG_ERR("Illegal ACL type %d\n", (int)type);
327 break;
330 if (name == NULL) {
331 TALLOC_FREE(frame);
332 return NULL;
335 do {
336 blob.length += 1000;
337 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
338 if (!blob.data) {
339 errno = ENOMEM;
340 TALLOC_FREE(frame);
341 return NULL;
343 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length);
344 blob.length = length;
345 } while (length == -1 && errno == ERANGE);
346 if (length == -1 && ((errno == ENOATTR) || (errno == EBADF))) {
347 TALLOC_FREE(frame);
348 return NULL;
350 if (length != -1) {
351 acl = fake_acls_blob2acl(&blob, mem_ctx);
353 TALLOC_FREE(frame);
354 return acl;
357 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle,
358 struct files_struct *fsp,
359 SMB_ACL_TYPE_T type,
360 SMB_ACL_T theacl)
362 int ret;
363 const char *name = NULL;
364 TALLOC_CTX *frame = talloc_stackframe();
365 DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
366 if (!blob.data) {
367 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
368 TALLOC_FREE(frame);
369 errno = EINVAL;
370 return -1;
373 switch (type) {
374 case SMB_ACL_TYPE_ACCESS:
375 name = FAKE_ACL_ACCESS_XATTR;
376 break;
377 case SMB_ACL_TYPE_DEFAULT:
378 name = FAKE_ACL_DEFAULT_XATTR;
379 break;
380 default:
381 errno = EINVAL;
382 return -1;
385 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
386 TALLOC_FREE(frame);
387 return ret;
390 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct *handle,
391 struct files_struct *fsp)
393 int ret;
394 const char *name = FAKE_ACL_DEFAULT_XATTR;
396 if (!fsp->fsp_flags.is_directory) {
397 errno = EINVAL;
398 return -1;
401 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
402 if (ret == -1 && ((errno == ENOATTR) || (errno == EBADF))) {
403 ret = 0;
404 errno = 0;
407 return ret;
410 static int fake_acls_lchown(vfs_handle_struct *handle,
411 const struct smb_filename *smb_fname,
412 uid_t uid,
413 gid_t gid)
415 int ret;
416 uint8_t id_buf[4];
417 if (uid != -1) {
418 uid_t current_uid = get_current_uid(handle->conn);
420 if (current_uid != 0 && current_uid != uid) {
421 return EACCES;
424 /* This isn't quite right (calling setxattr not
425 * lsetxattr), but for the test purposes of this
426 * module (fake NT ACLs from windows clients), it is
427 * close enough. We removed the l*xattr functions
428 * because linux doesn't support using them, but we
429 * could fake them in xattr_tdb if we really wanted
430 * to.
432 SIVAL(id_buf, 0, uid);
433 ret = SMB_VFS_NEXT_FSETXATTR(handle,
434 smb_fname->fsp,
435 FAKE_UID,
436 id_buf,
437 sizeof(id_buf),
439 if (ret != 0) {
440 return ret;
443 if (gid != -1) {
444 SIVAL(id_buf, 0, gid);
445 ret = SMB_VFS_NEXT_FSETXATTR(handle,
446 smb_fname->fsp,
447 FAKE_GID,
448 id_buf,
449 sizeof(id_buf),
451 if (ret != 0) {
452 return ret;
455 return 0;
458 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
460 int ret;
461 uint8_t id_buf[4];
462 if (uid != -1) {
463 uid_t current_uid = get_current_uid(handle->conn);
465 if (current_uid != 0 && current_uid != uid) {
466 return EACCES;
469 SIVAL(id_buf, 0, uid);
470 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
471 if (ret != 0) {
472 return ret;
475 if (gid != -1) {
476 SIVAL(id_buf, 0, gid);
477 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
478 if (ret != 0) {
479 return ret;
482 return 0;
486 * Implement the chmod uid/mask/other mode changes on a fake ACL.
489 static int fake_acl_process_chmod(SMB_ACL_T *pp_the_acl,
490 uid_t owner,
491 mode_t mode)
493 bool got_mask = false;
494 int entry_id = SMB_ACL_FIRST_ENTRY;
495 mode_t umode = 0;
496 mode_t mmode = 0;
497 mode_t omode = 0;
498 int ret = -1;
499 SMB_ACL_T the_acl = *pp_the_acl;
501 /* Split the mode into u/mask/other masks. */
502 umode = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
503 mmode = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
504 omode = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
506 while (1) {
507 SMB_ACL_ENTRY_T entry;
508 SMB_ACL_TAG_T tagtype;
509 SMB_ACL_PERMSET_T permset;
510 uid_t *puid = NULL;
512 ret = sys_acl_get_entry(the_acl,
513 entry_id,
514 &entry);
515 if (ret == 0) {
516 /* End of ACL */
517 break;
519 if (ret == -1) {
520 return -1;
523 ret = sys_acl_get_tag_type(entry, &tagtype);
524 if (ret == -1) {
525 return -1;
527 ret = sys_acl_get_permset(entry, &permset);
528 if (ret == -1) {
529 return -1;
531 switch (tagtype) {
532 case SMB_ACL_USER_OBJ:
533 ret = map_acl_perms_to_permset(umode, &permset);
534 if (ret == -1) {
535 return -1;
537 break;
538 case SMB_ACL_USER:
539 puid = (uid_t *)sys_acl_get_qualifier(entry);
540 if (puid == NULL) {
541 return -1;
543 if (owner != *puid) {
544 break;
546 ret = map_acl_perms_to_permset(umode, &permset);
547 if (ret == -1) {
548 return -1;
550 break;
551 case SMB_ACL_GROUP_OBJ:
552 case SMB_ACL_GROUP:
553 /* Ignore all group entries. */
554 break;
555 case SMB_ACL_MASK:
556 ret = map_acl_perms_to_permset(mmode, &permset);
557 if (ret == -1) {
558 return -1;
560 got_mask = true;
561 break;
562 case SMB_ACL_OTHER:
563 ret = map_acl_perms_to_permset(omode, &permset);
564 if (ret == -1) {
565 return -1;
567 break;
568 default:
569 errno = EINVAL;
570 return -1;
572 ret = sys_acl_set_permset(entry, permset);
573 if (ret == -1) {
574 return -1;
576 /* Move to next entry. */
577 entry_id = SMB_ACL_NEXT_ENTRY;
581 * If we didn't see a mask entry, add one.
584 if (!got_mask) {
585 SMB_ACL_ENTRY_T mask_entry;
586 uint32_t mask_perm = 0;
587 SMB_ACL_PERMSET_T mask_permset = &mask_perm;
588 ret = sys_acl_create_entry(&the_acl, &mask_entry);
589 if (ret == -1) {
590 return -1;
592 ret = map_acl_perms_to_permset(mmode, &mask_permset);
593 if (ret == -1) {
594 return -1;
596 ret = sys_acl_set_permset(mask_entry, mask_permset);
597 if (ret == -1) {
598 return -1;
600 ret = sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK);
601 if (ret == -1) {
602 return -1;
604 /* In case we were realloced and moved. */
605 *pp_the_acl = the_acl;
608 return 0;
611 static int fake_acls_fchmod(vfs_handle_struct *handle,
612 files_struct *fsp,
613 mode_t mode)
615 TALLOC_CTX *frame = talloc_stackframe();
616 int ret = -1;
617 SMB_ACL_T the_acl = NULL;
620 * Passthrough first to preserve the
621 * S_ISUID | S_ISGID | S_ISVTX
622 * bits.
625 ret = SMB_VFS_NEXT_FCHMOD(handle,
626 fsp,
627 mode);
628 if (ret == -1) {
629 TALLOC_FREE(frame);
630 return -1;
633 the_acl = fake_acls_sys_acl_get_fd(handle,
634 fsp,
635 SMB_ACL_TYPE_ACCESS,
636 talloc_tos());
637 if (the_acl == NULL) {
638 TALLOC_FREE(frame);
639 if (((errno == ENOATTR) || (errno == EBADF))) {
640 /* No ACL on this file. Just passthrough. */
641 return 0;
643 return -1;
645 ret = fake_acl_process_chmod(&the_acl,
646 fsp->fsp_name->st.st_ex_uid,
647 mode);
648 if (ret == -1) {
649 TALLOC_FREE(frame);
650 return -1;
652 ret = fake_acls_sys_acl_set_fd(handle,
653 fsp,
654 SMB_ACL_TYPE_ACCESS,
655 the_acl);
656 TALLOC_FREE(frame);
657 return ret;
660 static int fake_acls_connect(struct vfs_handle_struct *handle,
661 const char *service,
662 const char *user)
664 struct in_pathref_data *prd = NULL;
665 int ret;
667 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
668 if (ret < 0) {
669 return ret;
672 * Create a struct can tell us if we're recursing
673 * into openat_pathref_fsp() in this module. This will
674 * go away once we have SMB_VFS_STATX() and we will
675 * have a way for a caller to as for specific stat
676 * fields in a granular way. Then we will know exactly
677 * what fields the caller wants, so we won't have to
678 * fill in everything.
680 prd = talloc_zero(handle->conn, struct in_pathref_data);
681 if (prd == NULL) {
682 return -1;
684 SMB_VFS_HANDLE_SET_DATA(handle,
685 prd,
686 NULL,
687 struct in_pathref_data,
688 return -1);
689 return 0;
692 static struct vfs_fn_pointers vfs_fake_acls_fns = {
693 .connect_fn = fake_acls_connect,
694 .stat_fn = fake_acls_stat,
695 .lstat_fn = fake_acls_lstat,
696 .fstat_fn = fake_acls_fstat,
697 .fchmod_fn = fake_acls_fchmod,
698 .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
699 .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
700 .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
701 .sys_acl_delete_def_fd_fn = fake_acls_sys_acl_delete_def_fd,
702 .lchown_fn = fake_acls_lchown,
703 .fchown_fn = fake_acls_fchown,
707 static_decl_vfs;
708 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
710 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
711 &vfs_fake_acls_fns);