X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fmodules%2Fvfs_acl_common.c;h=e04b672cf9ae7ddb2f561ba7f85217d49d9426c9;hb=HEAD;hp=0c0cf83bdee495a00d5526e3d20a097c775601bc;hpb=cd1335e67dbfce0b6894ff209aa805d0314578da;p=samba.git diff --git a/source3/modules/vfs_acl_common.c b/source3/modules/vfs_acl_common.c index 0c0cf83bdee..e04b672cf9a 100644 --- a/source3/modules/vfs_acl_common.c +++ b/source3/modules/vfs_acl_common.c @@ -4,6 +4,7 @@ * * Copyright (C) Volker Lendecke, 2008 * Copyright (C) Jeremy Allison, 2009 + * Copyright (C) Ralph Böhme, 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,32 +20,62 @@ * along with this program; if not, see . */ +#include "includes.h" +#include "vfs_acl_common.h" #include "smbd/smbd.h" #include "system/filesys.h" +#include "librpc/gen_ndr/ndr_xattr.h" #include "../libcli/security/security.h" #include "../librpc/gen_ndr/ndr_security.h" #include "../lib/util/bitmap.h" +#include "passdb/lookup_sid.h" + +#include +#include static NTSTATUS create_acl_blob(const struct security_descriptor *psd, DATA_BLOB *pblob, uint16_t hash_type, uint8_t hash[XATTR_SD_HASH_SIZE]); -static NTSTATUS get_acl_blob(TALLOC_CTX *ctx, - vfs_handle_struct *handle, - files_struct *fsp, - const char *name, - DATA_BLOB *pblob); - -static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle, - files_struct *fsp, - DATA_BLOB *pblob); - #define HASH_SECURITY_INFO (SECINFO_OWNER | \ SECINFO_GROUP | \ SECINFO_DACL | \ SECINFO_SACL) +bool init_acl_common_config(vfs_handle_struct *handle, + const char *module_name) +{ + struct acl_common_config *config = NULL; + const struct enum_list *default_acl_style_list = NULL; + + default_acl_style_list = get_default_acl_style_list(); + + config = talloc_zero(handle->conn, struct acl_common_config); + if (config == NULL) { + DBG_ERR("talloc_zero() failed\n"); + errno = ENOMEM; + return false; + } + + config->ignore_system_acls = lp_parm_bool(SNUM(handle->conn), + module_name, + "ignore system acls", + false); + config->default_acl_style = lp_parm_enum(SNUM(handle->conn), + module_name, + "default acl style", + default_acl_style_list, + DEFAULT_ACL_POSIX); + + SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, + struct acl_common_config, + return false); + + return true; +} + + /******************************************************************* Hash a security descriptor. *******************************************************************/ @@ -52,13 +83,17 @@ static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle, static NTSTATUS hash_blob_sha256(DATA_BLOB blob, uint8_t *hash) { - SHA256_CTX tctx; + int rc; - memset(hash, '\0', XATTR_SD_HASH_SIZE); + ZERO_ARRAY_LEN(hash, XATTR_SD_HASH_SIZE); - samba_SHA256_Init(&tctx); - samba_SHA256_Update(&tctx, blob.data, blob.length); - samba_SHA256_Final(hash, &tctx); + rc = gnutls_hash_fast(GNUTLS_DIG_SHA256, + blob.data, + blob.length, + hash); + if (rc < 0) { + return NT_STATUS_INTERNAL_ERROR; + } return NT_STATUS_OK; } @@ -102,8 +137,8 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(5, ("parse_acl_blob: ndr_pull_xattr_NTACL failed: %s\n", - ndr_errstr(ndr_err))); + DBG_INFO("ndr_pull_xattr_NTACL failed: %s\n", + ndr_errstr(ndr_err)); TALLOC_FREE(frame); return ndr_map_error2ntstatus(ndr_err); } @@ -199,8 +234,8 @@ static NTSTATUS create_acl_blob(const struct security_descriptor *psd, (ndr_push_flags_fn_t)ndr_push_xattr_NTACL); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(5, ("create_acl_blob: ndr_push_xattr_NTACL failed: %s\n", - ndr_errstr(ndr_err))); + DBG_INFO("ndr_push_xattr_NTACL failed: %s\n", + ndr_errstr(ndr_err)); return ndr_map_error2ntstatus(ndr_err); } @@ -224,9 +259,6 @@ static NTSTATUS create_sys_acl_blob(const struct security_descriptor *psd, struct security_descriptor_hash_v4 sd_hs4; enum ndr_err_code ndr_err; TALLOC_CTX *ctx = talloc_tos(); - NTTIME nttime_now; - struct timeval now = timeval_current(); - nttime_now = timeval_to_nttime(&now); ZERO_STRUCT(xacl); ZERO_STRUCT(sd_hs4); @@ -237,7 +269,6 @@ static NTSTATUS create_sys_acl_blob(const struct security_descriptor *psd, xacl.info.sd_hs4->hash_type = hash_type; memcpy(&xacl.info.sd_hs4->hash[0], hash, XATTR_SD_HASH_SIZE); xacl.info.sd_hs4->description = description; - xacl.info.sd_hs4->time = nttime_now; memcpy(&xacl.info.sd_hs4->sys_acl_hash[0], sys_acl_hash, XATTR_SD_HASH_SIZE); ndr_err = ndr_push_struct_blob( @@ -245,8 +276,8 @@ static NTSTATUS create_sys_acl_blob(const struct security_descriptor *psd, (ndr_push_flags_fn_t)ndr_push_xattr_NTACL); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(5, ("create_acl_blob: ndr_push_xattr_NTACL failed: %s\n", - ndr_errstr(ndr_err))); + DBG_INFO("ndr_push_xattr_NTACL failed: %s\n", + ndr_errstr(ndr_err)); return ndr_map_error2ntstatus(ndr_err); } @@ -280,7 +311,7 @@ static NTSTATUS add_directory_inheritable_components(vfs_handle_struct *handle, } else { /* * make_sec_acl() at the bottom of this function - * dupliates new_ace_list + * duplicates new_ace_list */ new_ace_list = talloc_zero_array(talloc_tos(), struct security_ace, @@ -303,10 +334,7 @@ static NTSTATUS add_directory_inheritable_components(vfs_handle_struct *handle, mode = dir_mode | file_mode; - DEBUG(10, ("add_directory_inheritable_components: directory %s, " - "mode = 0%o\n", - name, - (unsigned int)mode )); + DBG_DEBUG("directory %s, mode = 0%o\n", name, (unsigned int)mode); if (num_aces) { memcpy(new_ace_list, psd->dacl->aces, @@ -358,20 +386,32 @@ static NTSTATUS add_directory_inheritable_components(vfs_handle_struct *handle, return NT_STATUS_OK; } -/******************************************************************* - Pull a DATA_BLOB from an xattr given a pathname. - If the hash doesn't match, or doesn't exist - return the underlying - filesystem sd. -*******************************************************************/ - -static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, - files_struct *fsp, - const struct smb_filename *smb_fname, - uint32_t security_info, - TALLOC_CTX *mem_ctx, - struct security_descriptor **ppdesc) +/** + * Validate an ACL blob + * + * This validates an ACL blob against the underlying filesystem ACL. If this + * function returns NT_STATUS_OK ppsd can be + * + * 1. the ACL from the blob (psd_from_fs=false), or + * 2. the ACL from the fs (psd_from_fs=true), or + * 3. NULL (!) + * + * If the return value is anything else then NT_STATUS_OK, ppsd is set to NULL + * and psd_from_fs set to false. + * + * Returning the underlying filesystem ACL in case no. 2 is really just an + * optimisation, because some validations have to fetch the filesystem ACL as + * part of the validation, so we already have it available and callers might + * need it as well. + **/ +static NTSTATUS validate_nt_acl_blob(TALLOC_CTX *mem_ctx, + vfs_handle_struct *handle, + struct files_struct *fsp, + const struct smb_filename *smb_fname, + const DATA_BLOB *blob, + struct security_descriptor **ppsd, + bool *psd_is_from_fs) { - DATA_BLOB blob = data_blob_null; NTSTATUS status; uint16_t hash_type = XATTR_SD_HASH_TYPE_NONE; uint16_t xattr_version = 0; @@ -380,81 +420,62 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, uint8_t hash_tmp[XATTR_SD_HASH_SIZE]; uint8_t sys_acl_hash_tmp[XATTR_SD_HASH_SIZE]; struct security_descriptor *psd = NULL; - struct security_descriptor *pdesc_next = NULL; - const char *name = NULL; - bool ignore_file_system_acl = lp_parm_bool(SNUM(handle->conn), - ACL_MODULE_NAME, - "ignore system acls", - false); - TALLOC_CTX *frame = talloc_stackframe(); + struct security_descriptor *psd_blob = NULL; + struct security_descriptor *psd_fs = NULL; + char *sys_acl_blob_description = NULL; + DATA_BLOB sys_acl_blob = { 0 }; + struct acl_common_config *config = NULL; - if (fsp && smb_fname == NULL) { - name = fsp->fsp_name->base_name; - } else { - name = smb_fname->base_name; - } + *ppsd = NULL; + *psd_is_from_fs = false; - DEBUG(10, ("get_nt_acl_internal: name=%s\n", name)); + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct acl_common_config, + return NT_STATUS_UNSUCCESSFUL); - status = get_acl_blob(frame, handle, fsp, name, &blob); + status = parse_acl_blob(blob, + mem_ctx, + &psd_blob, + &hash_type, + &xattr_version, + &hash[0], + &sys_acl_hash[0]); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("get_nt_acl_internal: get_acl_blob returned %s\n", - nt_errstr(status))); - psd = NULL; - goto out; - } else { - status = parse_acl_blob(&blob, mem_ctx, &psd, - &hash_type, &xattr_version, &hash[0], &sys_acl_hash[0]); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("parse_acl_blob returned %s\n", - nt_errstr(status))); - psd = NULL; - goto out; - } + DBG_DEBUG("parse_acl_blob returned %s\n", nt_errstr(status)); + goto fail; } - /* Ensure we don't leak psd if we don't choose it. - * - * We don't allocate it onto frame as it is preferred not to - * steal from a talloc pool. - */ - talloc_steal(frame, psd); - /* determine which type of xattr we got */ switch (xattr_version) { case 1: case 2: - /* These xattr types are unilatteral, they do not + /* These xattr types are unilateral, they do not * require confirmation of the hash. In particular, * the NTVFS file server uses version 1, but * 'samba-tool ntacl' can set these as well */ - goto out; + *ppsd = psd_blob; + return NT_STATUS_OK; case 3: case 4: - if (ignore_file_system_acl) { - goto out; + if (config->ignore_system_acls) { + *ppsd = psd_blob; + return NT_STATUS_OK; } break; default: - DEBUG(10, ("get_nt_acl_internal: ACL blob revision " - "mismatch (%u) for file %s\n", - (unsigned int)hash_type, - name)); - TALLOC_FREE(psd); - psd = NULL; - goto out; + DBG_DEBUG("ACL blob revision mismatch (%u) for file %s\n", + (unsigned int)hash_type, smb_fname->base_name); + TALLOC_FREE(psd_blob); + return NT_STATUS_OK; } /* determine which type of xattr we got */ if (hash_type != XATTR_SD_HASH_TYPE_SHA256) { - DEBUG(10, ("get_nt_acl_internal: ACL blob hash type " - "(%u) unexpected for file %s\n", - (unsigned int)hash_type, - name)); - TALLOC_FREE(psd); - psd = NULL; - goto out; + DBG_DEBUG("ACL blob hash type (%u) unexpected for file %s\n", + (unsigned int)hash_type, smb_fname->base_name); + TALLOC_FREE(psd_blob); + return NT_STATUS_OK; } /* determine which type of xattr we got */ @@ -462,224 +483,209 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, case 4: { int ret; - char *sys_acl_blob_description; - DATA_BLOB sys_acl_blob; - if (fsp) { - /* Get the full underlying sd, then hash. */ - ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, - fsp, - frame, - &sys_acl_blob_description, - &sys_acl_blob); - } else { - /* Get the full underlying sd, then hash. */ - ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(handle, - name, - frame, - &sys_acl_blob_description, - &sys_acl_blob); - } - + /* Get the full underlying sd, then hash. */ + ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, + fsp, + mem_ctx, + &sys_acl_blob_description, + &sys_acl_blob); /* If we fail to get the ACL blob (for some reason) then this * is not fatal, we just work based on the NT ACL only */ if (ret == 0) { status = hash_blob_sha256(sys_acl_blob, sys_acl_hash_tmp); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; + goto fail; } + TALLOC_FREE(sys_acl_blob_description); + TALLOC_FREE(sys_acl_blob.data); + if (memcmp(&sys_acl_hash[0], &sys_acl_hash_tmp[0], XATTR_SD_HASH_SIZE) == 0) { /* Hash matches, return blob sd. */ - DEBUG(10, ("get_nt_acl_internal: blob hash " - "matches for file %s\n", - name )); - goto out; + DBG_DEBUG("blob hash matches for file %s\n", + smb_fname->base_name); + *ppsd = psd_blob; + return NT_STATUS_OK; } } /* Otherwise, fall though and see if the NT ACL hash matches */ + FALL_THROUGH; } case 3: /* Get the full underlying sd for the hash or to return as backup. */ - if (fsp) { - status = SMB_VFS_NEXT_FGET_NT_ACL(handle, - fsp, - HASH_SECURITY_INFO, - mem_ctx, - &pdesc_next); - } else { - status = SMB_VFS_NEXT_GET_NT_ACL(handle, - smb_fname, - HASH_SECURITY_INFO, - mem_ctx, - &pdesc_next); - } - + status = SMB_VFS_NEXT_FGET_NT_ACL(handle, + fsp, + HASH_SECURITY_INFO, + mem_ctx, + &psd_fs); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s " - "returned %s\n", - name, - nt_errstr(status))); - TALLOC_FREE(frame); - return status; + DBG_DEBUG("get_next_acl for file %s returned %s\n", + smb_fname->base_name, nt_errstr(status)); + goto fail; } - /* Ensure we don't leak psd_next if we don't choose it. - * - * We don't allocate it onto frame as it is preferred not to - * steal from a talloc pool. - */ - talloc_steal(frame, pdesc_next); - - status = hash_sd_sha256(pdesc_next, hash_tmp); + status = hash_sd_sha256(psd_fs, hash_tmp); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(psd); - psd = pdesc_next; - goto out; + TALLOC_FREE(psd_blob); + *ppsd = psd_fs; + *psd_is_from_fs = true; + return NT_STATUS_OK; } if (memcmp(&hash[0], &hash_tmp[0], XATTR_SD_HASH_SIZE) == 0) { /* Hash matches, return blob sd. */ - DEBUG(10, ("get_nt_acl_internal: blob hash " - "matches for file %s\n", - name )); - goto out; + DBG_DEBUG("blob hash matches for file %s\n", + smb_fname->base_name); + *ppsd = psd_blob; + return NT_STATUS_OK; } /* Hash doesn't match, return underlying sd. */ - DEBUG(10, ("get_nt_acl_internal: blob hash " - "does not match for file %s - returning " - "file system SD mapping.\n", - name )); + DBG_DEBUG("blob hash does not match for file %s - returning " + "file system SD mapping.\n", + smb_fname->base_name); if (DEBUGLEVEL >= 10) { - DEBUG(10,("get_nt_acl_internal: acl for blob hash for %s is:\n", - name )); - NDR_PRINT_DEBUG(security_descriptor, pdesc_next); + DBG_DEBUG("acl for blob hash for %s is:\n", + smb_fname->base_name); + NDR_PRINT_DEBUG(security_descriptor, psd_fs); } - TALLOC_FREE(psd); - psd = pdesc_next; + TALLOC_FREE(psd_blob); + *ppsd = psd_fs; + *psd_is_from_fs = true; + } + + return NT_STATUS_OK; + +fail: + TALLOC_FREE(psd); + TALLOC_FREE(psd_blob); + TALLOC_FREE(psd_fs); + TALLOC_FREE(sys_acl_blob_description); + TALLOC_FREE(sys_acl_blob.data); + return status; +} + +/******************************************************************* + Pull a DATA_BLOB from an xattr given an fsp. + If the hash doesn't match, or doesn't exist - return the underlying + filesystem sd. +*******************************************************************/ + +NTSTATUS fget_nt_acl_common( + NTSTATUS (*fget_acl_blob_fn)(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + files_struct *fsp, + DATA_BLOB *pblob), + vfs_handle_struct *handle, + files_struct *fsp, + uint32_t security_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **ppdesc) +{ + DATA_BLOB blob = data_blob_null; + NTSTATUS status; + struct security_descriptor *psd = NULL; + const struct smb_filename *smb_fname = fsp->fsp_name; + bool psd_is_from_fs = false; + struct acl_common_config *config = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct acl_common_config, + return NT_STATUS_UNSUCCESSFUL); + + DBG_DEBUG("name=%s\n", smb_fname->base_name); + + status = fget_acl_blob_fn(mem_ctx, handle, fsp, &blob); + if (NT_STATUS_IS_OK(status)) { + status = validate_nt_acl_blob(mem_ctx, + handle, + fsp, + smb_fname, + &blob, + &psd, + &psd_is_from_fs); + TALLOC_FREE(blob.data); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("ACL validation for [%s] failed\n", + smb_fname->base_name); + goto fail; + } } - out: if (psd == NULL) { /* Get the full underlying sd, as we failed to get the * blob for the hash, or the revision/hash type wasn't * known */ - if (fsp) { + + if (config->ignore_system_acls) { + status = vfs_stat_fsp(fsp); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = make_default_filesystem_acl( + mem_ctx, + config->default_acl_style, + smb_fname->base_name, + &fsp->fsp_name->st, + &psd); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + } else { status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, mem_ctx, - &pdesc_next); - } else { - status = SMB_VFS_NEXT_GET_NT_ACL(handle, - smb_fname, - security_info, - mem_ctx, - &pdesc_next); + &psd); + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("get_next_acl for file %s " + "returned %s\n", + smb_fname->base_name, + nt_errstr(status)); + goto fail; + } + + psd_is_from_fs = true; } + } + if (psd_is_from_fs) { + status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s " - "returned %s\n", - name, - nt_errstr(status))); - TALLOC_FREE(frame); - return status; + goto fail; } - /* Ensure we don't leak psd_next if we don't choose it. - * - * We don't allocate it onto frame as it is preferred not to - * steal from a talloc pool. - */ - talloc_steal(frame, pdesc_next); - psd = pdesc_next; - } - - if (psd != pdesc_next) { - /* We're returning the blob, throw - * away the filesystem SD. */ - TALLOC_FREE(pdesc_next); - } else { - SMB_STRUCT_STAT sbuf; - SMB_STRUCT_STAT *psbuf = &sbuf; - bool is_directory = false; /* * We're returning the underlying ACL from the * filesystem. If it's a directory, and has no * inheritable ACE entries we have to fake them. */ - if (fsp) { - status = vfs_stat_fsp(fsp); + + if (fsp->fsp_flags.is_directory && + !sd_has_inheritable_components(psd, true)) { + status = add_directory_inheritable_components( + handle, + smb_fname->base_name, + &fsp->fsp_name->st, + psd); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; - } - psbuf = &fsp->fsp_name->st; - } else { - /* - * https://bugzilla.samba.org/show_bug.cgi?id=11249 - * - * We are currently guaranteed that 'name' here is - * a smb_fname->base_name, which *cannot* contain - * a stream name (':'). vfs_stat_smb_fname() splits - * a name into a base name + stream name, which - * when we get here we know we've already done. - * So we have to call the stat or lstat VFS - * calls directly here. Else, a base_name that - * contains a ':' (from a demangled name) will - * get split again. - * - * FIXME. - * This uglyness will go away once smb_fname - * is fully plumbed through the VFS. - */ - int ret = vfs_stat_smb_basename(handle->conn, - name, - &sbuf); - if (ret == -1) { - TALLOC_FREE(frame); - return map_nt_error_from_unix(errno); + goto fail; } } - is_directory = S_ISDIR(psbuf->st_ex_mode); - - if (ignore_file_system_acl) { - TALLOC_FREE(pdesc_next); - status = make_default_filesystem_acl(mem_ctx, - name, - psbuf, - &psd); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; - } - } else { - if (is_directory && - !sd_has_inheritable_components(psd, - true)) { - status = add_directory_inheritable_components( - handle, - name, - psbuf, - psd); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; - } - } - /* The underlying POSIX module always sets - the ~SEC_DESC_DACL_PROTECTED bit, as ACLs - can't be inherited in this way under POSIX. - Remove it for Windows-style ACLs. */ - psd->type &= ~SEC_DESC_DACL_PROTECTED; - } + + /* + * The underlying POSIX module always sets the + * ~SEC_DESC_DACL_PROTECTED bit, as ACLs can't be inherited in + * this way under POSIX. Remove it for Windows-style ACLs. + */ + psd->type &= ~SEC_DESC_DACL_PROTECTED; } if (!(security_info & SECINFO_OWNER)) { @@ -697,59 +703,132 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, psd->sacl = NULL; } - TALLOC_FREE(blob.data); - if (DEBUGLEVEL >= 10) { - DEBUG(10,("get_nt_acl_internal: returning acl for %s is:\n", - name )); + DBG_DEBUG("returning acl for %s is:\n", + smb_fname->base_name); NDR_PRINT_DEBUG(security_descriptor, psd); } - /* The VFS API is that the ACL is expected to be on mem_ctx */ - *ppdesc = talloc_move(mem_ctx, &psd); + *ppdesc = psd; - TALLOC_FREE(frame); return NT_STATUS_OK; + +fail: + TALLOC_FREE(psd); + return status; } /********************************************************************* - Fetch a security descriptor given an fsp. + Set the underlying ACL (e.g. POSIX ACLS, POSIX owner, etc) *********************************************************************/ - -static NTSTATUS fget_nt_acl_common(vfs_handle_struct *handle, - files_struct *fsp, - uint32_t security_info, - TALLOC_CTX *mem_ctx, - struct security_descriptor **ppdesc) +static NTSTATUS set_underlying_acl(vfs_handle_struct *handle, files_struct *fsp, + struct security_descriptor *psd, + uint32_t security_info_sent, + bool chown_needed) { - return get_nt_acl_internal(handle, fsp, - NULL, security_info, mem_ctx, ppdesc); + NTSTATUS status; + const struct security_token *token = NULL; + struct dom_sid_buf buf; + + status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd); + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + return status; + } + + /* We got access denied here. If we're already root, + or we didn't need to do a chown, or the fsp isn't + open with WRITE_OWNER access, just return. */ + if (get_current_uid(handle->conn) == 0 || !chown_needed) { + return NT_STATUS_ACCESS_DENIED; + } + status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * Only allow take-ownership, not give-ownership. That's the way Windows + * implements SEC_STD_WRITE_OWNER. MS-FSA 2.1.5.16 just states: If + * InputBuffer.OwnerSid is not a valid owner SID for a file in the + * objectstore, as determined in an implementation specific manner, the + * object store MUST return STATUS_INVALID_OWNER. + */ + token = get_current_nttok(fsp->conn); + if (!security_token_is_sid(token, psd->owner_sid)) { + return NT_STATUS_INVALID_OWNER; + } + + DBG_DEBUG("overriding chown on file %s for sid %s\n", + fsp_str_dbg(fsp), + dom_sid_str_buf(psd->owner_sid, &buf)); + + /* Ok, we failed to chown and we have + SEC_STD_WRITE_OWNER access - override. */ + become_root(); + status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd); + unbecome_root(); + + return status; } /********************************************************************* - Fetch a security descriptor given a pathname. + Store a v3 security descriptor *********************************************************************/ - -static NTSTATUS get_nt_acl_common(vfs_handle_struct *handle, - const struct smb_filename *smb_fname, - uint32_t security_info, - TALLOC_CTX *mem_ctx, - struct security_descriptor **ppdesc) +static NTSTATUS store_v3_blob( + NTSTATUS (*store_acl_blob_fsp_fn)(vfs_handle_struct *handle, + files_struct *fsp, + DATA_BLOB *pblob), + vfs_handle_struct *handle, files_struct *fsp, + struct security_descriptor *psd, + struct security_descriptor *pdesc_next, + uint8_t hash[XATTR_SD_HASH_SIZE]) { - return get_nt_acl_internal(handle, - NULL, - smb_fname, - security_info, - mem_ctx, - ppdesc); + NTSTATUS status; + DATA_BLOB blob; + + if (DEBUGLEVEL >= 10) { + DBG_DEBUG("storing xattr sd for file %s\n", + fsp_str_dbg(fsp)); + NDR_PRINT_DEBUG( + security_descriptor, + discard_const_p(struct security_descriptor, psd)); + + if (pdesc_next != NULL) { + DBG_DEBUG("storing xattr sd based on \n"); + NDR_PRINT_DEBUG( + security_descriptor, + discard_const_p(struct security_descriptor, + pdesc_next)); + } else { + DBG_DEBUG("ignoring underlying sd\n"); + } + } + status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("create_acl_blob failed\n"); + return status; + } + + status = store_acl_blob_fsp_fn(handle, fsp, &blob); + return status; } /********************************************************************* Store a security descriptor given an fsp. *********************************************************************/ -static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, - uint32_t security_info_sent, const struct security_descriptor *orig_psd) +NTSTATUS fset_nt_acl_common( + NTSTATUS (*fget_acl_blob_fn)(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + files_struct *fsp, + DATA_BLOB *pblob), + NTSTATUS (*store_acl_blob_fsp_fn)(vfs_handle_struct *handle, + files_struct *fsp, + DATA_BLOB *pblob), + const char *module_name, + vfs_handle_struct *handle, files_struct *fsp, + uint32_t security_info_sent, + const struct security_descriptor *orig_psd) { NTSTATUS status; int ret; @@ -761,16 +840,17 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, bool chown_needed = false; char *sys_acl_description; TALLOC_CTX *frame = talloc_stackframe(); + bool ignore_file_system_acl = lp_parm_bool( + SNUM(handle->conn), module_name, "ignore system acls", false); + struct acl_common_fsp_ext *ext = NULL; if (DEBUGLEVEL >= 10) { - DEBUG(10,("fset_nt_acl_xattr: incoming sd for file %s\n", - fsp_str_dbg(fsp))); + DBG_DEBUG("incoming sd for file %s\n", fsp_str_dbg(fsp)); NDR_PRINT_DEBUG(security_descriptor, discard_const_p(struct security_descriptor, orig_psd)); } - status = get_nt_acl_internal(handle, fsp, - NULL, + status = fget_nt_acl_common(fget_acl_blob_fn, handle, fsp, SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL, frame, &psd); @@ -781,8 +861,11 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, } psd->revision = orig_psd->revision; - /* All our SD's are self relative. */ - psd->type = orig_psd->type | SEC_DESC_SELF_RELATIVE; + if (security_info_sent & SECINFO_DACL) { + psd->type = orig_psd->type; + /* All our SD's are self relative. */ + psd->type |= SEC_DESC_SELF_RELATIVE; + } if ((security_info_sent & SECINFO_OWNER) && (orig_psd->owner_sid != NULL)) { if (!dom_sid_equal(orig_psd->owner_sid, psd->owner_sid)) { @@ -816,38 +899,32 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, psd->type |= SEC_DESC_SACL_PRESENT; } - status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd); - if (!NT_STATUS_IS_OK(status)) { - if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { - TALLOC_FREE(frame); - return status; - } - /* We got access denied here. If we're already root, - or we didn't need to do a chown, or the fsp isn't - open with WRITE_OWNER access, just return. */ - if (get_current_uid(handle->conn) == 0 || - chown_needed == false || - !(fsp->access_mask & SEC_STD_WRITE_OWNER)) { - TALLOC_FREE(frame); - return NT_STATUS_ACCESS_DENIED; + ext = VFS_ADD_FSP_EXTENSION(handle, + fsp, + struct acl_common_fsp_ext, + NULL); + ext->setting_nt_acl = true; + + if (ignore_file_system_acl) { + if (chown_needed) { + /* send only ownership stuff to lower layer */ + security_info_sent &= (SECINFO_OWNER | SECINFO_GROUP); + status = set_underlying_acl(handle, fsp, psd, + security_info_sent, true); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } } + ZERO_ARRAY(hash); + status = store_v3_blob(store_acl_blob_fsp_fn, handle, fsp, psd, + NULL, hash); + goto done; + } - DEBUG(10,("fset_nt_acl_common: overriding chown on file %s " - "for sid %s\n", - fsp_str_dbg(fsp), - sid_string_tos(psd->owner_sid) - )); - - /* Ok, we failed to chown and we have - SEC_STD_WRITE_OWNER access - override. */ - become_root(); - status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, - security_info_sent, psd); - unbecome_root(); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; - } + status = set_underlying_acl(handle, fsp, psd, security_info_sent, + chown_needed); + if (!NT_STATUS_IS_OK(status)) { + goto done; } /* Get the full underlying sd, then hash. */ @@ -858,14 +935,12 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, &pdesc_next); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; + goto done; } status = hash_sd_sha256(pdesc_next, hash); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; + goto done; } /* Get the full underlying sd, then hash. */ @@ -878,117 +953,110 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, /* If we fail to get the ACL blob (for some reason) then this * is not fatal, we just work based on the NT ACL only */ if (ret != 0) { - if (DEBUGLEVEL >= 10) { - DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n", - fsp_str_dbg(fsp))); - NDR_PRINT_DEBUG(security_descriptor, - discard_const_p(struct security_descriptor, psd)); - - DEBUG(10,("fset_nt_acl_xattr: storing has in xattr sd based on \n")); - NDR_PRINT_DEBUG(security_descriptor, - discard_const_p(struct security_descriptor, pdesc_next)); - } - status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("fset_nt_acl_xattr: create_acl_blob failed\n")); - TALLOC_FREE(frame); - return status; - } - - status = store_acl_blob_fsp(handle, fsp, &blob); + status = store_v3_blob(store_acl_blob_fsp_fn, handle, fsp, psd, + pdesc_next, hash); - TALLOC_FREE(frame); - return status; + goto done; } status = hash_blob_sha256(sys_acl_blob, sys_acl_hash); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); - return status; + goto done; } if (DEBUGLEVEL >= 10) { - DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s based on system ACL\n", - fsp_str_dbg(fsp))); + DBG_DEBUG("storing xattr sd for file %s based on system ACL\n", + fsp_str_dbg(fsp)); NDR_PRINT_DEBUG(security_descriptor, discard_const_p(struct security_descriptor, psd)); - DEBUG(10,("fset_nt_acl_xattr: storing hash in xattr sd based on system ACL and:\n")); + DBG_DEBUG("storing hash in xattr sd based on system ACL and:\n"); NDR_PRINT_DEBUG(security_descriptor, discard_const_p(struct security_descriptor, pdesc_next)); } /* We store hashes of both the sys ACL blob and the NT - * security desciptor mapped from that ACL so as to improve - * our chances against some inadvertant change breaking the + * security descriptor mapped from that ACL so as to improve + * our chances against some inadvertent change breaking the * hash used */ status = create_sys_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash, sys_acl_description, sys_acl_hash); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("fset_nt_acl_xattr: create_sys_acl_blob failed\n")); - TALLOC_FREE(frame); - return status; + DBG_DEBUG("create_sys_acl_blob failed\n"); + goto done; } - status = store_acl_blob_fsp(handle, fsp, &blob); + status = store_acl_blob_fsp_fn(handle, fsp, &blob); +done: + VFS_REMOVE_FSP_EXTENSION(handle, fsp); TALLOC_FREE(frame); return status; } static int acl_common_remove_object(vfs_handle_struct *handle, - const char *path, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname, bool is_directory) { connection_struct *conn = handle->conn; struct file_id id; files_struct *fsp = NULL; int ret = 0; - char *parent_dir = NULL; - const char *final_component = NULL; - struct smb_filename local_fname; + struct smb_filename *full_fname = NULL; + struct smb_filename *local_fname = NULL; + struct smb_filename *parent_dir_fname = NULL; int saved_errno = 0; - char *saved_dir = NULL; + struct smb_filename *saved_dir_fname = NULL; + NTSTATUS status; - saved_dir = vfs_GetWd(talloc_tos(),conn); - if (!saved_dir) { + saved_dir_fname = vfs_GetWd(talloc_tos(),conn); + if (saved_dir_fname == NULL) { saved_errno = errno; goto out; } - if (!parent_dirname(talloc_tos(), path, - &parent_dir, &final_component)) { - saved_errno = ENOMEM; + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { goto out; } - DEBUG(10,("acl_common_remove_object: removing %s %s/%s\n", - is_directory ? "directory" : "file", - parent_dir, final_component )); + status = SMB_VFS_PARENT_PATHNAME(conn, + talloc_tos(), + full_fname, + &parent_dir_fname, + &local_fname); + if (!NT_STATUS_IS_OK(status)) { + saved_errno = map_errno_from_nt_status(status); + goto out; + } + + DBG_DEBUG("removing %s %s\n", is_directory ? "directory" : "file", + smb_fname_str_dbg(full_fname)); /* cd into the parent dir to pin it. */ - ret = vfs_ChDir(conn, parent_dir); + ret = vfs_ChDir(conn, parent_dir_fname); if (ret == -1) { saved_errno = errno; goto out; } - ZERO_STRUCT(local_fname); - local_fname.base_name = discard_const_p(char, final_component); - /* Must use lstat here. */ - ret = SMB_VFS_LSTAT(conn, &local_fname); + ret = SMB_VFS_LSTAT(conn, local_fname); if (ret == -1) { saved_errno = errno; goto out; } /* Ensure we have this file open with DELETE access. */ - id = vfs_file_id_from_sbuf(conn, &local_fname.st); - for (fsp = file_find_di_first(conn->sconn, id); fsp; - fsp = file_find_di_next(fsp)) { + id = vfs_file_id_from_sbuf(conn, &local_fname->st); + for (fsp = file_find_di_first(conn->sconn, id, true); fsp; + fsp = file_find_di_next(fsp, true)) { if (fsp->access_mask & DELETE_ACCESS && - fsp->delete_on_close) { + fsp->fsp_flags.delete_on_close) + { /* We did open this for delete, * allow the delete as root. */ @@ -997,19 +1065,24 @@ static int acl_common_remove_object(vfs_handle_struct *handle, } if (!fsp) { - DEBUG(10,("acl_common_remove_object: %s %s/%s " - "not an open file\n", - is_directory ? "directory" : "file", - parent_dir, final_component )); + DBG_DEBUG("%s %s not an open file\n", + is_directory ? "directory" : "file", + smb_fname_str_dbg(full_fname)); saved_errno = EACCES; goto out; } become_root(); if (is_directory) { - ret = SMB_VFS_NEXT_RMDIR(handle, &local_fname); + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp, + smb_fname, + AT_REMOVEDIR); } else { - ret = SMB_VFS_NEXT_UNLINK(handle, &local_fname); + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp, + smb_fname, + 0); } unbecome_root(); @@ -1019,10 +1092,12 @@ static int acl_common_remove_object(vfs_handle_struct *handle, out: - TALLOC_FREE(parent_dir); + TALLOC_FREE(parent_dir_fname); + TALLOC_FREE(full_fname); - if (saved_dir) { - vfs_ChDir(conn, saved_dir); + if (saved_dir_fname) { + vfs_ChDir(conn, saved_dir_fname); + TALLOC_FREE(saved_dir_fname); } if (saved_errno) { errno = saved_errno; @@ -1030,13 +1105,17 @@ static int acl_common_remove_object(vfs_handle_struct *handle, return ret; } -static int rmdir_acl_common(struct vfs_handle_struct *handle, - const struct smb_filename *smb_fname) +int rmdir_acl_common(struct vfs_handle_struct *handle, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname) { int ret; /* Try the normal rmdir first. */ - ret = SMB_VFS_NEXT_RMDIR(handle, smb_fname); + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp, + smb_fname, + AT_REMOVEDIR); if (ret == 0) { return 0; } @@ -1044,23 +1123,29 @@ static int rmdir_acl_common(struct vfs_handle_struct *handle, /* Failed due to access denied, see if we need to root override. */ return acl_common_remove_object(handle, - smb_fname->base_name, + dirfsp, + smb_fname, true); } - DEBUG(10,("rmdir_acl_common: unlink of %s failed %s\n", - smb_fname->base_name, - strerror(errno) )); + DBG_DEBUG("unlink of %s failed %s\n", + smb_fname->base_name, + strerror(errno)); return -1; } -static int unlink_acl_common(struct vfs_handle_struct *handle, - const struct smb_filename *smb_fname) +int unlink_acl_common(struct vfs_handle_struct *handle, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + int flags) { int ret; /* Try the normal unlink first. */ - ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname); + ret = SMB_VFS_NEXT_UNLINKAT(handle, + dirfsp, + smb_fname, + flags); if (ret == 0) { return 0; } @@ -1073,52 +1158,24 @@ static int unlink_acl_common(struct vfs_handle_struct *handle, return -1; } return acl_common_remove_object(handle, - smb_fname->base_name, + dirfsp, + smb_fname, false); } - DEBUG(10,("unlink_acl_common: unlink of %s failed %s\n", - smb_fname->base_name, - strerror(errno) )); + DBG_DEBUG("unlink of %s failed %s\n", + smb_fname->base_name, + strerror(errno)); return -1; } -static int chmod_acl_module_common(struct vfs_handle_struct *handle, - const char *path, mode_t mode) +int fchmod_acl_module_common(struct vfs_handle_struct *handle, + struct files_struct *fsp, mode_t mode) { - if (lp_posix_pathnames()) { - /* Only allow this on POSIX pathnames. */ - return SMB_VFS_NEXT_CHMOD(handle, path, mode); - } - return 0; -} - -static int fchmod_acl_module_common(struct vfs_handle_struct *handle, - struct files_struct *fsp, mode_t mode) -{ - if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) { + if (fsp->posix_flags & FSP_POSIX_FLAGS_PATHNAMES + || fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) { /* Only allow this on POSIX opens. */ return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); } return 0; } - -static int chmod_acl_acl_module_common(struct vfs_handle_struct *handle, - const char *name, mode_t mode) -{ - if (lp_posix_pathnames()) { - /* Only allow this on POSIX pathnames. */ - return SMB_VFS_NEXT_CHMOD_ACL(handle, name, mode); - } - return 0; -} - -static int fchmod_acl_acl_module_common(struct vfs_handle_struct *handle, - struct files_struct *fsp, mode_t mode) -{ - if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) { - /* Only allow this on POSIX opens. */ - return SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, mode); - } - return 0; -}