s3:registry: Initialize struct security_ace ace[]
[samba4-gss.git] / source3 / modules / vfs_fake_acls.c
blobfefe6c5483e388ab802a02dee8d7db90fb9a88dd
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) {
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) {
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 /* Recursion guard. */
130 prd->calling_pathref_fsp = true;
131 status = openat_pathref_fsp(handle->conn->cwd_fsp,
132 smb_fname_cp);
133 /* End recursion guard. */
134 prd->calling_pathref_fsp = false;
136 if (!NT_STATUS_IS_OK(status)) {
138 * Ignore errors here. We know
139 * the path exists (the SMB_VFS_NEXT_STAT()
140 * above succeeded. So being unable to
141 * open a pathref fsp can be due to a
142 * range of errors (startup path beginning
143 * with '/' for example, path = ".." when
144 * enumerating a directory. Just treat this
145 * the same way as the path not having the
146 * FAKE_UID or FAKE_GID EA's present. For the
147 * test purposes of this module (fake NT ACLs
148 * from windows clients) this is close enough.
149 * Just report for debugging purposes.
151 DBG_DEBUG("Unable to get pathref fsp on %s. "
152 "Error %s\n",
153 smb_fname_str_dbg(smb_fname_cp),
154 nt_errstr(status));
155 TALLOC_FREE(smb_fname_cp);
156 return 0;
158 fsp = smb_fname_cp->fsp;
161 ret = fake_acls_fuid(handle,
162 fsp,
163 &smb_fname->st.st_ex_uid);
164 if (ret != 0) {
165 TALLOC_FREE(smb_fname_cp);
166 return ret;
168 ret = fake_acls_fgid(handle,
169 fsp,
170 &smb_fname->st.st_ex_gid);
171 if (ret != 0) {
172 TALLOC_FREE(smb_fname_cp);
173 return ret;
175 TALLOC_FREE(smb_fname_cp);
176 return ret;
179 static int fake_acls_lstat(vfs_handle_struct *handle,
180 struct smb_filename *smb_fname)
182 int ret = -1;
183 struct in_pathref_data *prd = NULL;
185 SMB_VFS_HANDLE_GET_DATA(handle,
186 prd,
187 struct in_pathref_data,
188 return -1);
190 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
191 if (ret == 0) {
192 struct smb_filename *smb_fname_base = NULL;
193 SMB_STRUCT_STAT sbuf = { 0 };
194 NTSTATUS status;
197 * Ensure synthetic_pathref()
198 * can't recurse into fake_acls_lstat().
199 * synthetic_pathref() doesn't care
200 * about the uid/gid values, it only
201 * wants a valid/invalid stat answer
202 * and we know smb_fname exists as
203 * the SMB_VFS_NEXT_LSTAT() returned
204 * zero above.
206 if (prd->calling_pathref_fsp) {
207 return 0;
210 /* Recursion guard. */
211 prd->calling_pathref_fsp = true;
212 status = synthetic_pathref(talloc_tos(),
213 handle->conn->cwd_fsp,
214 smb_fname->base_name,
215 NULL,
216 &sbuf,
217 smb_fname->twrp,
218 0, /* we want stat, not lstat. */
219 &smb_fname_base);
220 /* End recursion guard. */
221 prd->calling_pathref_fsp = false;
222 if (NT_STATUS_IS_OK(status)) {
224 * This isn't quite right (calling fgetxattr not
225 * lgetxattr), but for the test purposes of this
226 * module (fake NT ACLs from windows clients), it is
227 * close enough. We removed the l*xattr functions
228 * because linux doesn't support using them, but we
229 * could fake them in xattr_tdb if we really wanted
230 * to. We ignore errors because the link might not
231 * point anywhere */
232 fake_acls_fuid(handle,
233 smb_fname_base->fsp,
234 &smb_fname->st.st_ex_uid);
235 fake_acls_fgid(handle,
236 smb_fname_base->fsp,
237 &smb_fname->st.st_ex_gid);
239 TALLOC_FREE(smb_fname_base);
242 return ret;
245 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
247 int ret = -1;
249 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
250 if (ret == 0) {
251 ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid);
252 if (ret != 0) {
253 return ret;
255 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
256 if (ret != 0) {
257 return ret;
260 return ret;
263 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
265 enum ndr_err_code ndr_err;
266 struct smb_acl_t *acl = talloc(mem_ctx, struct smb_acl_t);
267 if (!acl) {
268 errno = ENOMEM;
269 return NULL;
272 ndr_err = ndr_pull_struct_blob(blob, acl, acl,
273 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
275 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
276 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
277 ndr_errstr(ndr_err)));
278 TALLOC_FREE(acl);
279 return NULL;
281 return acl;
284 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
286 enum ndr_err_code ndr_err;
287 DATA_BLOB blob;
288 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl,
289 (ndr_push_flags_fn_t)ndr_push_smb_acl_t);
291 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
292 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
293 ndr_errstr(ndr_err)));
294 return data_blob_null;
296 return blob;
299 static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle,
300 files_struct *fsp,
301 SMB_ACL_TYPE_T type,
302 TALLOC_CTX *mem_ctx)
304 DATA_BLOB blob = data_blob_null;
305 ssize_t length;
306 const char *name = NULL;
307 struct smb_acl_t *acl = NULL;
308 TALLOC_CTX *frame = talloc_stackframe();
310 switch (type) {
311 case SMB_ACL_TYPE_ACCESS:
312 name = FAKE_ACL_ACCESS_XATTR;
313 break;
314 case SMB_ACL_TYPE_DEFAULT:
315 name = FAKE_ACL_DEFAULT_XATTR;
316 break;
317 default:
318 DBG_ERR("Illegal ACL type %d\n", (int)type);
319 break;
322 if (name == NULL) {
323 TALLOC_FREE(frame);
324 return NULL;
327 do {
328 blob.length += 1000;
329 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
330 if (!blob.data) {
331 errno = ENOMEM;
332 TALLOC_FREE(frame);
333 return NULL;
335 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length);
336 blob.length = length;
337 } while (length == -1 && errno == ERANGE);
338 if (length == -1 && errno == ENOATTR) {
339 TALLOC_FREE(frame);
340 return NULL;
342 if (length != -1) {
343 acl = fake_acls_blob2acl(&blob, mem_ctx);
345 TALLOC_FREE(frame);
346 return acl;
349 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle,
350 struct files_struct *fsp,
351 SMB_ACL_TYPE_T type,
352 SMB_ACL_T theacl)
354 int ret;
355 const char *name = NULL;
356 TALLOC_CTX *frame = talloc_stackframe();
357 DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
358 if (!blob.data) {
359 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
360 TALLOC_FREE(frame);
361 errno = EINVAL;
362 return -1;
365 switch (type) {
366 case SMB_ACL_TYPE_ACCESS:
367 name = FAKE_ACL_ACCESS_XATTR;
368 break;
369 case SMB_ACL_TYPE_DEFAULT:
370 name = FAKE_ACL_DEFAULT_XATTR;
371 break;
372 default:
373 errno = EINVAL;
374 return -1;
377 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
378 TALLOC_FREE(frame);
379 return ret;
382 static int fake_acls_sys_acl_delete_def_fd(vfs_handle_struct *handle,
383 struct files_struct *fsp)
385 int ret;
386 const char *name = FAKE_ACL_DEFAULT_XATTR;
388 if (!fsp->fsp_flags.is_directory) {
389 errno = EINVAL;
390 return -1;
393 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
394 if (ret == -1 && errno == ENOATTR) {
395 ret = 0;
396 errno = 0;
399 return ret;
402 static int fake_acls_lchown(vfs_handle_struct *handle,
403 const struct smb_filename *smb_fname,
404 uid_t uid,
405 gid_t gid)
407 int ret;
408 uint8_t id_buf[4];
409 if (uid != -1) {
410 uid_t current_uid = get_current_uid(handle->conn);
412 if (current_uid != 0 && current_uid != uid) {
413 return EACCES;
416 /* This isn't quite right (calling setxattr not
417 * lsetxattr), but for the test purposes of this
418 * module (fake NT ACLs from windows clients), it is
419 * close enough. We removed the l*xattr functions
420 * because linux doesn't support using them, but we
421 * could fake them in xattr_tdb if we really wanted
422 * to.
424 SIVAL(id_buf, 0, uid);
425 ret = SMB_VFS_NEXT_FSETXATTR(handle,
426 smb_fname->fsp,
427 FAKE_UID,
428 id_buf,
429 sizeof(id_buf),
431 if (ret != 0) {
432 return ret;
435 if (gid != -1) {
436 SIVAL(id_buf, 0, gid);
437 ret = SMB_VFS_NEXT_FSETXATTR(handle,
438 smb_fname->fsp,
439 FAKE_GID,
440 id_buf,
441 sizeof(id_buf),
443 if (ret != 0) {
444 return ret;
447 return 0;
450 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
452 int ret;
453 uint8_t id_buf[4];
454 if (uid != -1) {
455 uid_t current_uid = get_current_uid(handle->conn);
457 if (current_uid != 0 && current_uid != uid) {
458 return EACCES;
461 SIVAL(id_buf, 0, uid);
462 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
463 if (ret != 0) {
464 return ret;
467 if (gid != -1) {
468 SIVAL(id_buf, 0, gid);
469 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
470 if (ret != 0) {
471 return ret;
474 return 0;
478 * Implement the chmod uid/mask/other mode changes on a fake ACL.
481 static int fake_acl_process_chmod(SMB_ACL_T *pp_the_acl,
482 uid_t owner,
483 mode_t mode)
485 bool got_mask = false;
486 int entry_id = SMB_ACL_FIRST_ENTRY;
487 mode_t umode = 0;
488 mode_t mmode = 0;
489 mode_t omode = 0;
490 int ret = -1;
491 SMB_ACL_T the_acl = *pp_the_acl;
493 /* Split the mode into u/mask/other masks. */
494 umode = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
495 mmode = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
496 omode = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
498 while (1) {
499 SMB_ACL_ENTRY_T entry;
500 SMB_ACL_TAG_T tagtype;
501 SMB_ACL_PERMSET_T permset;
502 uid_t *puid = NULL;
504 ret = sys_acl_get_entry(the_acl,
505 entry_id,
506 &entry);
507 if (ret == 0) {
508 /* End of ACL */
509 break;
511 if (ret == -1) {
512 return -1;
515 ret = sys_acl_get_tag_type(entry, &tagtype);
516 if (ret == -1) {
517 return -1;
519 ret = sys_acl_get_permset(entry, &permset);
520 if (ret == -1) {
521 return -1;
523 switch (tagtype) {
524 case SMB_ACL_USER_OBJ:
525 ret = map_acl_perms_to_permset(umode, &permset);
526 if (ret == -1) {
527 return -1;
529 break;
530 case SMB_ACL_USER:
531 puid = (uid_t *)sys_acl_get_qualifier(entry);
532 if (puid == NULL) {
533 return -1;
535 if (owner != *puid) {
536 break;
538 ret = map_acl_perms_to_permset(umode, &permset);
539 if (ret == -1) {
540 return -1;
542 break;
543 case SMB_ACL_GROUP_OBJ:
544 case SMB_ACL_GROUP:
545 /* Ignore all group entries. */
546 break;
547 case SMB_ACL_MASK:
548 ret = map_acl_perms_to_permset(mmode, &permset);
549 if (ret == -1) {
550 return -1;
552 got_mask = true;
553 break;
554 case SMB_ACL_OTHER:
555 ret = map_acl_perms_to_permset(omode, &permset);
556 if (ret == -1) {
557 return -1;
559 break;
560 default:
561 errno = EINVAL;
562 return -1;
564 ret = sys_acl_set_permset(entry, permset);
565 if (ret == -1) {
566 return -1;
568 /* Move to next entry. */
569 entry_id = SMB_ACL_NEXT_ENTRY;
573 * If we didn't see a mask entry, add one.
576 if (!got_mask) {
577 SMB_ACL_ENTRY_T mask_entry;
578 uint32_t mask_perm = 0;
579 SMB_ACL_PERMSET_T mask_permset = &mask_perm;
580 ret = sys_acl_create_entry(&the_acl, &mask_entry);
581 if (ret == -1) {
582 return -1;
584 ret = map_acl_perms_to_permset(mmode, &mask_permset);
585 if (ret == -1) {
586 return -1;
588 ret = sys_acl_set_permset(mask_entry, mask_permset);
589 if (ret == -1) {
590 return -1;
592 ret = sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK);
593 if (ret == -1) {
594 return -1;
596 /* In case we were realloced and moved. */
597 *pp_the_acl = the_acl;
600 return 0;
603 static int fake_acls_fchmod(vfs_handle_struct *handle,
604 files_struct *fsp,
605 mode_t mode)
607 TALLOC_CTX *frame = talloc_stackframe();
608 int ret = -1;
609 SMB_ACL_T the_acl = NULL;
612 * Passthrough first to preserve the
613 * S_ISUID | S_ISGID | S_ISVTX
614 * bits.
617 ret = SMB_VFS_NEXT_FCHMOD(handle,
618 fsp,
619 mode);
620 if (ret == -1) {
621 TALLOC_FREE(frame);
622 return -1;
625 the_acl = fake_acls_sys_acl_get_fd(handle,
626 fsp,
627 SMB_ACL_TYPE_ACCESS,
628 talloc_tos());
629 if (the_acl == NULL) {
630 TALLOC_FREE(frame);
631 if (errno == ENOATTR) {
632 /* No ACL on this file. Just passthrough. */
633 return 0;
635 return -1;
637 ret = fake_acl_process_chmod(&the_acl,
638 fsp->fsp_name->st.st_ex_uid,
639 mode);
640 if (ret == -1) {
641 TALLOC_FREE(frame);
642 return -1;
644 ret = fake_acls_sys_acl_set_fd(handle,
645 fsp,
646 SMB_ACL_TYPE_ACCESS,
647 the_acl);
648 TALLOC_FREE(frame);
649 return ret;
652 static int fake_acls_connect(struct vfs_handle_struct *handle,
653 const char *service,
654 const char *user)
656 struct in_pathref_data *prd = NULL;
657 int ret;
659 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
660 if (ret < 0) {
661 return ret;
664 * Create a struct can tell us if we're recursing
665 * into openat_pathref_fsp() in this module. This will
666 * go away once we have SMB_VFS_STATX() and we will
667 * have a way for a caller to as for specific stat
668 * fields in a granular way. Then we will know exactly
669 * what fields the caller wants, so we won't have to
670 * fill in everything.
672 prd = talloc_zero(handle->conn, struct in_pathref_data);
673 if (prd == NULL) {
674 return -1;
676 SMB_VFS_HANDLE_SET_DATA(handle,
677 prd,
678 NULL,
679 struct in_pathref_data,
680 return -1);
681 return 0;
684 static struct vfs_fn_pointers vfs_fake_acls_fns = {
685 .connect_fn = fake_acls_connect,
686 .stat_fn = fake_acls_stat,
687 .lstat_fn = fake_acls_lstat,
688 .fstat_fn = fake_acls_fstat,
689 .fchmod_fn = fake_acls_fchmod,
690 .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
691 .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
692 .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
693 .sys_acl_delete_def_fd_fn = fake_acls_sys_acl_delete_def_fd,
694 .lchown_fn = fake_acls_lchown,
695 .fchown_fn = fake_acls_fchown,
699 static_decl_vfs;
700 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
702 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
703 &vfs_fake_acls_fns);