libsmbconf: Document smbconf_transaction_cancel().
[Samba.git] / source3 / modules / vfs_acl_common.c
blob84d666aa8bb8b543b54a518b93bfcabc576213f2
1 /*
2 * Store Windows ACLs in data store - common functions.
3 * #included into modules/vfs_acl_xattr.c and modules/vfs_acl_tdb.c
5 * Copyright (C) Volker Lendecke, 2008
6 * Copyright (C) Jeremy Allison, 2009
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "../libcli/security/security.h"
25 #include "../librpc/gen_ndr/ndr_security.h"
27 static NTSTATUS create_acl_blob(const struct security_descriptor *psd,
28 DATA_BLOB *pblob,
29 uint16_t hash_type,
30 uint8_t hash[XATTR_SD_HASH_SIZE]);
32 static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
33 vfs_handle_struct *handle,
34 files_struct *fsp,
35 const char *name,
36 DATA_BLOB *pblob);
38 static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle,
39 files_struct *fsp,
40 DATA_BLOB *pblob);
42 #define HASH_SECURITY_INFO (SECINFO_OWNER | \
43 SECINFO_GROUP | \
44 SECINFO_DACL | \
45 SECINFO_SACL)
47 /*******************************************************************
48 Hash a security descriptor.
49 *******************************************************************/
51 static NTSTATUS hash_sd_sha256(struct security_descriptor *psd,
52 uint8_t *hash)
54 DATA_BLOB blob;
55 SHA256_CTX tctx;
56 NTSTATUS status;
58 memset(hash, '\0', XATTR_SD_HASH_SIZE);
59 status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash);
60 if (!NT_STATUS_IS_OK(status)) {
61 return status;
64 SHA256_Init(&tctx);
65 SHA256_Update(&tctx, blob.data, blob.length);
66 SHA256_Final(hash, &tctx);
68 return NT_STATUS_OK;
71 /*******************************************************************
72 Parse out a struct security_descriptor from a DATA_BLOB.
73 *******************************************************************/
75 static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob,
76 struct security_descriptor **ppdesc,
77 uint16_t *p_hash_type,
78 uint8_t hash[XATTR_SD_HASH_SIZE])
80 TALLOC_CTX *ctx = talloc_tos();
81 struct xattr_NTACL xacl;
82 enum ndr_err_code ndr_err;
83 size_t sd_size;
85 ndr_err = ndr_pull_struct_blob(pblob, ctx, &xacl,
86 (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
88 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
89 DEBUG(5, ("parse_acl_blob: ndr_pull_xattr_NTACL failed: %s\n",
90 ndr_errstr(ndr_err)));
91 return ndr_map_error2ntstatus(ndr_err);
94 switch (xacl.version) {
95 case 2:
96 *ppdesc = make_sec_desc(ctx, SD_REVISION,
97 xacl.info.sd_hs2->sd->type | SEC_DESC_SELF_RELATIVE,
98 xacl.info.sd_hs2->sd->owner_sid,
99 xacl.info.sd_hs2->sd->group_sid,
100 xacl.info.sd_hs2->sd->sacl,
101 xacl.info.sd_hs2->sd->dacl,
102 &sd_size);
103 /* No hash - null out. */
104 *p_hash_type = XATTR_SD_HASH_TYPE_NONE;
105 memset(hash, '\0', XATTR_SD_HASH_SIZE);
106 break;
107 case 3:
108 *ppdesc = make_sec_desc(ctx, SD_REVISION,
109 xacl.info.sd_hs3->sd->type | SEC_DESC_SELF_RELATIVE,
110 xacl.info.sd_hs3->sd->owner_sid,
111 xacl.info.sd_hs3->sd->group_sid,
112 xacl.info.sd_hs3->sd->sacl,
113 xacl.info.sd_hs3->sd->dacl,
114 &sd_size);
115 *p_hash_type = xacl.info.sd_hs3->hash_type;
116 /* Current version 3. */
117 memcpy(hash, xacl.info.sd_hs3->hash, XATTR_SD_HASH_SIZE);
118 break;
119 default:
120 return NT_STATUS_REVISION_MISMATCH;
123 TALLOC_FREE(xacl.info.sd);
125 return (*ppdesc != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
128 /*******************************************************************
129 Create a DATA_BLOB from a security descriptor.
130 *******************************************************************/
132 static NTSTATUS create_acl_blob(const struct security_descriptor *psd,
133 DATA_BLOB *pblob,
134 uint16_t hash_type,
135 uint8_t hash[XATTR_SD_HASH_SIZE])
137 struct xattr_NTACL xacl;
138 struct security_descriptor_hash_v3 sd_hs3;
139 enum ndr_err_code ndr_err;
140 TALLOC_CTX *ctx = talloc_tos();
142 ZERO_STRUCT(xacl);
143 ZERO_STRUCT(sd_hs3);
145 xacl.version = 3;
146 xacl.info.sd_hs3 = &sd_hs3;
147 xacl.info.sd_hs3->sd = CONST_DISCARD(struct security_descriptor *, psd);
148 xacl.info.sd_hs3->hash_type = hash_type;
149 memcpy(&xacl.info.sd_hs3->hash[0], hash, XATTR_SD_HASH_SIZE);
151 ndr_err = ndr_push_struct_blob(
152 pblob, ctx, &xacl,
153 (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
155 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
156 DEBUG(5, ("create_acl_blob: ndr_push_xattr_NTACL failed: %s\n",
157 ndr_errstr(ndr_err)));
158 return ndr_map_error2ntstatus(ndr_err);
161 return NT_STATUS_OK;
164 /*******************************************************************
165 Add in 3 inheritable components for a non-inheritable directory ACL.
166 CREATOR_OWNER/CREATOR_GROUP/WORLD.
167 *******************************************************************/
169 static void add_directory_inheritable_components(vfs_handle_struct *handle,
170 const char *name,
171 SMB_STRUCT_STAT *psbuf,
172 struct security_descriptor *psd)
174 struct connection_struct *conn = handle->conn;
175 int num_aces = (psd->dacl ? psd->dacl->num_aces : 0);
176 struct smb_filename smb_fname;
177 enum security_ace_type acltype;
178 uint32_t access_mask;
179 mode_t dir_mode;
180 mode_t file_mode;
181 mode_t mode;
182 struct security_ace *new_ace_list = TALLOC_ZERO_ARRAY(talloc_tos(),
183 struct security_ace,
184 num_aces + 3);
186 if (new_ace_list == NULL) {
187 return;
190 /* Fake a quick smb_filename. */
191 ZERO_STRUCT(smb_fname);
192 smb_fname.st = *psbuf;
193 smb_fname.base_name = CONST_DISCARD(char *, name);
195 dir_mode = unix_mode(conn,
196 FILE_ATTRIBUTE_DIRECTORY, &smb_fname, NULL);
197 file_mode = unix_mode(conn,
198 FILE_ATTRIBUTE_ARCHIVE, &smb_fname, NULL);
200 mode = dir_mode | file_mode;
202 DEBUG(10, ("add_directory_inheritable_components: directory %s, "
203 "mode = 0%o\n",
204 name,
205 (unsigned int)mode ));
207 if (num_aces) {
208 memcpy(new_ace_list, psd->dacl->aces,
209 num_aces * sizeof(struct security_ace));
211 access_mask = map_canon_ace_perms(SNUM(conn), &acltype,
212 mode & 0700, false);
214 init_sec_ace(&new_ace_list[num_aces],
215 &global_sid_Creator_Owner,
216 acltype,
217 access_mask,
218 SEC_ACE_FLAG_CONTAINER_INHERIT|
219 SEC_ACE_FLAG_OBJECT_INHERIT|
220 SEC_ACE_FLAG_INHERIT_ONLY);
221 access_mask = map_canon_ace_perms(SNUM(conn), &acltype,
222 (mode << 3) & 0700, false);
223 init_sec_ace(&new_ace_list[num_aces+1],
224 &global_sid_Creator_Group,
225 acltype,
226 access_mask,
227 SEC_ACE_FLAG_CONTAINER_INHERIT|
228 SEC_ACE_FLAG_OBJECT_INHERIT|
229 SEC_ACE_FLAG_INHERIT_ONLY);
230 access_mask = map_canon_ace_perms(SNUM(conn), &acltype,
231 (mode << 6) & 0700, false);
232 init_sec_ace(&new_ace_list[num_aces+2],
233 &global_sid_World,
234 acltype,
235 access_mask,
236 SEC_ACE_FLAG_CONTAINER_INHERIT|
237 SEC_ACE_FLAG_OBJECT_INHERIT|
238 SEC_ACE_FLAG_INHERIT_ONLY);
239 psd->dacl->aces = new_ace_list;
240 psd->dacl->num_aces += 3;
243 /*******************************************************************
244 Pull a DATA_BLOB from an xattr given a pathname.
245 If the hash doesn't match, or doesn't exist - return the underlying
246 filesystem sd.
247 *******************************************************************/
249 static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle,
250 files_struct *fsp,
251 const char *name,
252 uint32_t security_info,
253 struct security_descriptor **ppdesc)
255 DATA_BLOB blob;
256 NTSTATUS status;
257 uint16_t hash_type = XATTR_SD_HASH_TYPE_NONE;
258 uint8_t hash[XATTR_SD_HASH_SIZE];
259 uint8_t hash_tmp[XATTR_SD_HASH_SIZE];
260 struct security_descriptor *psd = NULL;
261 struct security_descriptor *pdesc_next = NULL;
262 bool ignore_file_system_acl = lp_parm_bool(SNUM(handle->conn),
263 ACL_MODULE_NAME,
264 "ignore system acls",
265 false);
267 if (fsp && name == NULL) {
268 name = fsp->fsp_name->base_name;
271 DEBUG(10, ("get_nt_acl_internal: name=%s\n", name));
273 /* Get the full underlying sd for the hash
274 or to return as backup. */
275 if (fsp) {
276 status = SMB_VFS_NEXT_FGET_NT_ACL(handle,
277 fsp,
278 HASH_SECURITY_INFO,
279 &pdesc_next);
280 } else {
281 status = SMB_VFS_NEXT_GET_NT_ACL(handle,
282 name,
283 HASH_SECURITY_INFO,
284 &pdesc_next);
287 if (!NT_STATUS_IS_OK(status)) {
288 DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s "
289 "returned %s\n",
290 name,
291 nt_errstr(status)));
292 return status;
295 status = get_acl_blob(talloc_tos(), handle, fsp, name, &blob);
296 if (!NT_STATUS_IS_OK(status)) {
297 DEBUG(10, ("get_nt_acl_internal: get_acl_blob returned %s\n",
298 nt_errstr(status)));
299 psd = pdesc_next;
300 goto out;
303 status = parse_acl_blob(&blob, &psd,
304 &hash_type, &hash[0]);
305 if (!NT_STATUS_IS_OK(status)) {
306 DEBUG(10, ("parse_acl_blob returned %s\n",
307 nt_errstr(status)));
308 psd = pdesc_next;
309 goto out;
312 /* Ensure the hash type is one we know. */
313 switch (hash_type) {
314 case XATTR_SD_HASH_TYPE_NONE:
315 /* No hash, just return blob sd. */
316 goto out;
317 case XATTR_SD_HASH_TYPE_SHA256:
318 break;
319 default:
320 DEBUG(10, ("get_nt_acl_internal: ACL blob revision "
321 "mismatch (%u) for file %s\n",
322 (unsigned int)hash_type,
323 name));
324 TALLOC_FREE(psd);
325 psd = pdesc_next;
326 goto out;
329 if (ignore_file_system_acl) {
330 goto out;
333 status = hash_sd_sha256(pdesc_next, hash_tmp);
334 if (!NT_STATUS_IS_OK(status)) {
335 TALLOC_FREE(psd);
336 psd = pdesc_next;
337 goto out;
340 if (memcmp(&hash[0], &hash_tmp[0], XATTR_SD_HASH_SIZE) == 0) {
341 /* Hash matches, return blob sd. */
342 DEBUG(10, ("get_nt_acl_internal: blob hash "
343 "matches for file %s\n",
344 name ));
345 goto out;
348 /* Hash doesn't match, return underlying sd. */
349 TALLOC_FREE(psd);
350 psd = pdesc_next;
352 out:
354 if (psd != pdesc_next) {
355 /* We're returning the blob, throw
356 * away the filesystem SD. */
357 TALLOC_FREE(pdesc_next);
358 } else {
359 SMB_STRUCT_STAT sbuf;
360 SMB_STRUCT_STAT *psbuf = &sbuf;
361 bool is_directory = false;
363 * We're returning the underlying ACL from the
364 * filesystem. If it's a directory, and has no
365 * inheritable ACE entries we have to fake them.
367 if (fsp) {
368 status = vfs_stat_fsp(fsp);
369 if (!NT_STATUS_IS_OK(status)) {
370 return status;
372 psbuf = &fsp->fsp_name->st;
373 } else {
374 int ret = vfs_stat_smb_fname(handle->conn,
375 name,
376 &sbuf);
377 if (ret == -1) {
378 return map_nt_error_from_unix(errno);
381 is_directory = S_ISDIR(sbuf.st_ex_mode);
383 if (ignore_file_system_acl) {
384 TALLOC_FREE(pdesc_next);
385 status = make_default_filesystem_acl(talloc_tos(),
386 name,
387 psbuf,
388 &psd);
389 if (!NT_STATUS_IS_OK(status)) {
390 return status;
392 } else {
393 if (is_directory &&
394 !sd_has_inheritable_components(psd,
395 true)) {
396 add_directory_inheritable_components(handle,
397 name,
398 psbuf,
399 psd);
401 /* The underlying POSIX module always sets
402 the ~SEC_DESC_DACL_PROTECTED bit, as ACLs
403 can't be inherited in this way under POSIX.
404 Remove it for Windows-style ACLs. */
405 psd->type &= ~SEC_DESC_DACL_PROTECTED;
409 if (!(security_info & SECINFO_OWNER)) {
410 psd->owner_sid = NULL;
412 if (!(security_info & SECINFO_GROUP)) {
413 psd->group_sid = NULL;
415 if (!(security_info & SECINFO_DACL)) {
416 psd->dacl = NULL;
418 if (!(security_info & SECINFO_SACL)) {
419 psd->sacl = NULL;
422 TALLOC_FREE(blob.data);
423 *ppdesc = psd;
425 if (DEBUGLEVEL >= 10) {
426 DEBUG(10,("get_nt_acl_internal: returning acl for %s is:\n",
427 name ));
428 NDR_PRINT_DEBUG(security_descriptor, psd);
431 return NT_STATUS_OK;
434 /*********************************************************************
435 Create a default ACL by inheriting from the parent. If no inheritance
436 from the parent available, don't set anything. This will leave the actual
437 permissions the new file or directory already got from the filesystem
438 as the NT ACL when read.
439 *********************************************************************/
441 static NTSTATUS inherit_new_acl(vfs_handle_struct *handle,
442 files_struct *fsp,
443 struct security_descriptor *parent_desc,
444 bool is_directory)
446 TALLOC_CTX *ctx = talloc_tos();
447 NTSTATUS status = NT_STATUS_OK;
448 struct security_descriptor *psd = NULL;
449 struct dom_sid *owner_sid = NULL;
450 struct dom_sid *group_sid = NULL;
451 bool inherit_owner = lp_inherit_owner(SNUM(handle->conn));
452 size_t size;
454 if (!sd_has_inheritable_components(parent_desc, is_directory)) {
455 return NT_STATUS_OK;
458 /* Create an inherited descriptor from the parent. */
460 if (DEBUGLEVEL >= 10) {
461 DEBUG(10,("inherit_new_acl: parent acl for %s is:\n",
462 fsp_str_dbg(fsp) ));
463 NDR_PRINT_DEBUG(security_descriptor, parent_desc);
466 /* Inherit from parent descriptor if "inherit owner" set. */
467 if (inherit_owner) {
468 owner_sid = parent_desc->owner_sid;
469 group_sid = parent_desc->group_sid;
472 if (owner_sid == NULL) {
473 owner_sid = &handle->conn->session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
475 if (group_sid == NULL) {
476 group_sid = &handle->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX];
479 status = se_create_child_secdesc(ctx,
480 &psd,
481 &size,
482 parent_desc,
483 owner_sid,
484 group_sid,
485 is_directory);
486 if (!NT_STATUS_IS_OK(status)) {
487 return status;
490 if (DEBUGLEVEL >= 10) {
491 DEBUG(10,("inherit_new_acl: child acl for %s is:\n",
492 fsp_str_dbg(fsp) ));
493 NDR_PRINT_DEBUG(security_descriptor, psd);
496 if (inherit_owner) {
497 /* We need to be root to force this. */
498 become_root();
500 status = SMB_VFS_FSET_NT_ACL(fsp,
501 (SECINFO_OWNER |
502 SECINFO_GROUP |
503 SECINFO_DACL),
504 psd);
505 if (inherit_owner) {
506 unbecome_root();
508 return status;
511 static NTSTATUS get_parent_acl_common(vfs_handle_struct *handle,
512 const char *path,
513 struct security_descriptor **pp_parent_desc)
515 char *parent_name = NULL;
516 NTSTATUS status;
518 if (!parent_dirname(talloc_tos(), path, &parent_name, NULL)) {
519 return NT_STATUS_NO_MEMORY;
522 status = get_nt_acl_internal(handle,
523 NULL,
524 parent_name,
525 (SECINFO_OWNER |
526 SECINFO_GROUP |
527 SECINFO_DACL),
528 pp_parent_desc);
530 if (!NT_STATUS_IS_OK(status)) {
531 DEBUG(10,("get_parent_acl_common: get_nt_acl_internal "
532 "on directory %s for "
533 "path %s returned %s\n",
534 parent_name,
535 path,
536 nt_errstr(status) ));
538 return status;
541 static NTSTATUS check_parent_acl_common(vfs_handle_struct *handle,
542 const char *path,
543 uint32_t access_mask,
544 struct security_descriptor **pp_parent_desc)
546 char *parent_name = NULL;
547 struct security_descriptor *parent_desc = NULL;
548 uint32_t access_granted = 0;
549 NTSTATUS status;
551 status = get_parent_acl_common(handle, path, &parent_desc);
552 if (!NT_STATUS_IS_OK(status)) {
553 return status;
555 if (pp_parent_desc) {
556 *pp_parent_desc = parent_desc;
558 status = smb1_file_se_access_check(handle->conn,
559 parent_desc,
560 get_current_nttok(handle->conn),
561 access_mask,
562 &access_granted);
563 if(!NT_STATUS_IS_OK(status)) {
564 DEBUG(10,("check_parent_acl_common: access check "
565 "on directory %s for "
566 "path %s for mask 0x%x returned %s\n",
567 parent_name,
568 path,
569 access_mask,
570 nt_errstr(status) ));
571 return status;
573 return NT_STATUS_OK;
576 /*********************************************************************
577 Check ACL on open. For new files inherit from parent directory.
578 *********************************************************************/
580 static int open_acl_common(vfs_handle_struct *handle,
581 struct smb_filename *smb_fname,
582 files_struct *fsp,
583 int flags,
584 mode_t mode)
586 uint32_t access_granted = 0;
587 struct security_descriptor *pdesc = NULL;
588 bool file_existed = true;
589 char *fname = NULL;
590 NTSTATUS status;
592 if (fsp->base_fsp) {
593 /* Stream open. Base filename open already did the ACL check. */
594 DEBUG(10,("open_acl_common: stream open on %s\n",
595 fsp_str_dbg(fsp) ));
596 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
599 status = get_full_smb_filename(talloc_tos(), smb_fname,
600 &fname);
601 if (!NT_STATUS_IS_OK(status)) {
602 goto err;
605 status = get_nt_acl_internal(handle,
606 NULL,
607 fname,
608 (SECINFO_OWNER |
609 SECINFO_GROUP |
610 SECINFO_DACL),
611 &pdesc);
612 if (NT_STATUS_IS_OK(status)) {
613 /* See if we can access it. */
614 status = smb1_file_se_access_check(handle->conn,
615 pdesc,
616 get_current_nttok(handle->conn),
617 fsp->access_mask,
618 &access_granted);
619 if (!NT_STATUS_IS_OK(status)) {
620 DEBUG(10,("open_acl_xattr: %s open "
621 "refused with error %s\n",
622 fsp_str_dbg(fsp),
623 nt_errstr(status) ));
624 goto err;
626 } else if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
627 file_existed = false;
629 * If O_CREAT is true then we're trying to create a file.
630 * Check the parent directory ACL will allow this.
632 if (flags & O_CREAT) {
633 struct security_descriptor *parent_desc = NULL;
634 struct security_descriptor **pp_psd = NULL;
636 status = check_parent_acl_common(handle, fname,
637 SEC_DIR_ADD_FILE, &parent_desc);
638 if (!NT_STATUS_IS_OK(status)) {
639 goto err;
642 /* Cache the parent security descriptor for
643 * later use. */
645 pp_psd = VFS_ADD_FSP_EXTENSION(handle,
646 fsp,
647 struct security_descriptor *,
648 NULL);
649 if (!pp_psd) {
650 status = NT_STATUS_NO_MEMORY;
651 goto err;
654 *pp_psd = parent_desc;
655 status = NT_STATUS_OK;
659 DEBUG(10,("open_acl_xattr: get_nt_acl_attr_internal for "
660 "%s returned %s\n",
661 fsp_str_dbg(fsp),
662 nt_errstr(status) ));
664 fsp->fh->fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
665 return fsp->fh->fd;
667 err:
669 errno = map_errno_from_nt_status(status);
670 return -1;
673 static int mkdir_acl_common(vfs_handle_struct *handle, const char *path, mode_t mode)
675 int ret;
676 NTSTATUS status;
677 SMB_STRUCT_STAT sbuf;
679 ret = vfs_stat_smb_fname(handle->conn, path, &sbuf);
680 if (ret == -1 && errno == ENOENT) {
681 /* We're creating a new directory. */
682 status = check_parent_acl_common(handle, path,
683 SEC_DIR_ADD_SUBDIR, NULL);
684 if (!NT_STATUS_IS_OK(status)) {
685 errno = map_errno_from_nt_status(status);
686 return -1;
690 return SMB_VFS_NEXT_MKDIR(handle, path, mode);
693 /*********************************************************************
694 Fetch a security descriptor given an fsp.
695 *********************************************************************/
697 static NTSTATUS fget_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp,
698 uint32_t security_info, struct security_descriptor **ppdesc)
700 return get_nt_acl_internal(handle, fsp,
701 NULL, security_info, ppdesc);
704 /*********************************************************************
705 Fetch a security descriptor given a pathname.
706 *********************************************************************/
708 static NTSTATUS get_nt_acl_common(vfs_handle_struct *handle,
709 const char *name, uint32_t security_info, struct security_descriptor **ppdesc)
711 return get_nt_acl_internal(handle, NULL,
712 name, security_info, ppdesc);
715 /*********************************************************************
716 Store a security descriptor given an fsp.
717 *********************************************************************/
719 static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp,
720 uint32_t security_info_sent, const struct security_descriptor *orig_psd)
722 NTSTATUS status;
723 DATA_BLOB blob;
724 struct security_descriptor *pdesc_next = NULL;
725 struct security_descriptor *psd = NULL;
726 uint8_t hash[XATTR_SD_HASH_SIZE];
728 if (DEBUGLEVEL >= 10) {
729 DEBUG(10,("fset_nt_acl_xattr: incoming sd for file %s\n",
730 fsp_str_dbg(fsp)));
731 NDR_PRINT_DEBUG(security_descriptor,
732 CONST_DISCARD(struct security_descriptor *,orig_psd));
735 status = get_nt_acl_internal(handle, fsp,
736 NULL,
737 SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL,
738 &psd);
740 if (!NT_STATUS_IS_OK(status)) {
741 return status;
744 psd->revision = orig_psd->revision;
745 /* All our SD's are self relative. */
746 psd->type = orig_psd->type | SEC_DESC_SELF_RELATIVE;
748 if ((security_info_sent & SECINFO_OWNER) && (orig_psd->owner_sid != NULL)) {
749 psd->owner_sid = orig_psd->owner_sid;
751 if ((security_info_sent & SECINFO_GROUP) && (orig_psd->group_sid != NULL)) {
752 psd->group_sid = orig_psd->group_sid;
754 if (security_info_sent & SECINFO_DACL) {
755 psd->dacl = orig_psd->dacl;
756 psd->type |= SEC_DESC_DACL_PRESENT;
758 if (security_info_sent & SECINFO_SACL) {
759 psd->sacl = orig_psd->sacl;
760 psd->type |= SEC_DESC_SACL_PRESENT;
763 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
764 if (!NT_STATUS_IS_OK(status)) {
765 return status;
768 /* Get the full underlying sd, then hash. */
769 status = SMB_VFS_NEXT_FGET_NT_ACL(handle,
770 fsp,
771 HASH_SECURITY_INFO,
772 &pdesc_next);
774 if (!NT_STATUS_IS_OK(status)) {
775 return status;
778 status = hash_sd_sha256(pdesc_next, hash);
779 if (!NT_STATUS_IS_OK(status)) {
780 return status;
783 if (DEBUGLEVEL >= 10) {
784 DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n",
785 fsp_str_dbg(fsp)));
786 NDR_PRINT_DEBUG(security_descriptor,
787 CONST_DISCARD(struct security_descriptor *,psd));
789 create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash);
790 store_acl_blob_fsp(handle, fsp, &blob);
792 return NT_STATUS_OK;
795 static SMB_STRUCT_DIR *opendir_acl_common(vfs_handle_struct *handle,
796 const char *fname, const char *mask, uint32 attr)
798 NTSTATUS status = check_parent_acl_common(handle, fname,
799 SEC_DIR_LIST, NULL);
801 if (!NT_STATUS_IS_OK(status)) {
802 errno = map_errno_from_nt_status(status);
803 return NULL;
805 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
808 static int acl_common_remove_object(vfs_handle_struct *handle,
809 const char *path,
810 bool is_directory)
812 connection_struct *conn = handle->conn;
813 struct file_id id;
814 files_struct *fsp = NULL;
815 int ret = 0;
816 char *parent_dir = NULL;
817 const char *final_component = NULL;
818 struct smb_filename local_fname;
819 int saved_errno = 0;
821 if (!parent_dirname(talloc_tos(), path,
822 &parent_dir, &final_component)) {
823 saved_errno = ENOMEM;
824 goto out;
827 DEBUG(10,("acl_common_remove_object: removing %s %s/%s\n",
828 is_directory ? "directory" : "file",
829 parent_dir, final_component ));
831 /* cd into the parent dir to pin it. */
832 ret = SMB_VFS_CHDIR(conn, parent_dir);
833 if (ret == -1) {
834 saved_errno = errno;
835 goto out;
838 ZERO_STRUCT(local_fname);
839 local_fname.base_name = CONST_DISCARD(char *,final_component);
841 /* Must use lstat here. */
842 ret = SMB_VFS_LSTAT(conn, &local_fname);
843 if (ret == -1) {
844 saved_errno = errno;
845 goto out;
848 /* Ensure we have this file open with DELETE access. */
849 id = vfs_file_id_from_sbuf(conn, &local_fname.st);
850 for (fsp = file_find_di_first(conn->sconn, id); fsp;
851 file_find_di_next(fsp)) {
852 if (fsp->access_mask & DELETE_ACCESS &&
853 fsp->delete_on_close) {
854 /* We did open this for delete,
855 * allow the delete as root.
857 break;
861 if (!fsp) {
862 DEBUG(10,("acl_common_remove_object: %s %s/%s "
863 "not an open file\n",
864 is_directory ? "directory" : "file",
865 parent_dir, final_component ));
866 saved_errno = EACCES;
867 goto out;
870 become_root();
871 if (is_directory) {
872 ret = SMB_VFS_NEXT_RMDIR(handle, final_component);
873 } else {
874 ret = SMB_VFS_NEXT_UNLINK(handle, &local_fname);
876 unbecome_root();
878 if (ret == -1) {
879 saved_errno = errno;
882 out:
884 TALLOC_FREE(parent_dir);
886 vfs_ChDir(conn, conn->connectpath);
887 if (saved_errno) {
888 errno = saved_errno;
890 return ret;
893 static int rmdir_acl_common(struct vfs_handle_struct *handle,
894 const char *path)
896 int ret;
898 ret = SMB_VFS_NEXT_RMDIR(handle, path);
899 if (!(ret == -1 && (errno == EACCES || errno == EPERM))) {
900 DEBUG(10,("rmdir_acl_common: unlink of %s failed %s\n",
901 path,
902 strerror(errno) ));
903 return ret;
906 return acl_common_remove_object(handle,
907 path,
908 true);
911 static NTSTATUS create_file_acl_common(struct vfs_handle_struct *handle,
912 struct smb_request *req,
913 uint16_t root_dir_fid,
914 struct smb_filename *smb_fname,
915 uint32_t access_mask,
916 uint32_t share_access,
917 uint32_t create_disposition,
918 uint32_t create_options,
919 uint32_t file_attributes,
920 uint32_t oplock_request,
921 uint64_t allocation_size,
922 uint32_t private_flags,
923 struct security_descriptor *sd,
924 struct ea_list *ea_list,
925 files_struct **result,
926 int *pinfo)
928 NTSTATUS status, status1;
929 files_struct *fsp = NULL;
930 int info;
931 struct security_descriptor *parent_sd = NULL;
932 struct security_descriptor **pp_parent_sd = NULL;
934 status = SMB_VFS_NEXT_CREATE_FILE(handle,
935 req,
936 root_dir_fid,
937 smb_fname,
938 access_mask,
939 share_access,
940 create_disposition,
941 create_options,
942 file_attributes,
943 oplock_request,
944 allocation_size,
945 private_flags,
947 ea_list,
948 result,
949 &info);
951 if (!NT_STATUS_IS_OK(status)) {
952 goto out;
955 if (info != FILE_WAS_CREATED) {
956 /* File/directory was opened, not created. */
957 goto out;
960 fsp = *result;
962 if (fsp == NULL) {
963 /* Only handle success. */
964 goto out;
967 if (sd) {
968 /* Security descriptor already set. */
969 goto out;
972 if (fsp->base_fsp) {
973 /* Stream open. */
974 goto out;
977 /* See if we have a cached parent sd, if so, use it. */
978 pp_parent_sd = (struct security_descriptor **)VFS_FETCH_FSP_EXTENSION(handle, fsp);
979 if (!pp_parent_sd) {
980 /* Must be a directory, fetch again (sigh). */
981 status = get_parent_acl_common(handle,
982 fsp->fsp_name->base_name,
983 &parent_sd);
984 if (!NT_STATUS_IS_OK(status)) {
985 goto out;
987 } else {
988 parent_sd = *pp_parent_sd;
991 if (!parent_sd) {
992 goto err;
995 /* New directory - inherit from parent. */
996 status1 = inherit_new_acl(handle, fsp, parent_sd, fsp->is_directory);
998 if (!NT_STATUS_IS_OK(status1)) {
999 DEBUG(1,("create_file_acl_common: error setting "
1000 "sd for %s (%s)\n",
1001 fsp_str_dbg(fsp),
1002 nt_errstr(status1) ));
1005 out:
1007 if (fsp) {
1008 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1011 if (NT_STATUS_IS_OK(status) && pinfo) {
1012 *pinfo = info;
1014 return status;
1016 err:
1018 smb_panic("create_file_acl_common: logic error.\n");
1019 /* NOTREACHED */
1020 return status;
1023 static int unlink_acl_common(struct vfs_handle_struct *handle,
1024 const struct smb_filename *smb_fname)
1026 int ret;
1028 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1029 if (!(ret == -1 && (errno == EACCES || errno == EPERM))) {
1030 DEBUG(10,("unlink_acl_common: unlink of %s failed %s\n",
1031 smb_fname->base_name,
1032 strerror(errno) ));
1033 return ret;
1035 /* Don't do anything fancy for streams. */
1036 if (smb_fname->stream_name) {
1037 return ret;
1040 return acl_common_remove_object(handle,
1041 smb_fname->base_name,
1042 false);
1045 static int chmod_acl_module_common(struct vfs_handle_struct *handle,
1046 const char *path, mode_t mode)
1048 if (lp_posix_pathnames()) {
1049 /* Only allow this on POSIX pathnames. */
1050 return SMB_VFS_NEXT_CHMOD(handle, path, mode);
1052 return 0;
1055 static int fchmod_acl_module_common(struct vfs_handle_struct *handle,
1056 struct files_struct *fsp, mode_t mode)
1058 if (fsp->posix_open) {
1059 /* Only allow this on POSIX opens. */
1060 return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
1062 return 0;
1065 static int chmod_acl_acl_module_common(struct vfs_handle_struct *handle,
1066 const char *name, mode_t mode)
1068 if (lp_posix_pathnames()) {
1069 /* Only allow this on POSIX pathnames. */
1070 return SMB_VFS_NEXT_CHMOD_ACL(handle, name, mode);
1072 return 0;
1075 static int fchmod_acl_acl_module_common(struct vfs_handle_struct *handle,
1076 struct files_struct *fsp, mode_t mode)
1078 if (fsp->posix_open) {
1079 /* Only allow this on POSIX opens. */
1080 return SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, mode);
1082 return 0;