diff options
-rw-r--r-- | source3/modules/vfs_acl_common.c | 301 |
1 files changed, 250 insertions, 51 deletions
diff --git a/source3/modules/vfs_acl_common.c b/source3/modules/vfs_acl_common.c index e1555c7d9e..5d4327a628 100644 --- a/source3/modules/vfs_acl_common.c +++ b/source3/modules/vfs_acl_common.c @@ -87,9 +87,11 @@ static NTSTATUS hash_sd_sha256(struct security_descriptor *psd, static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, TALLOC_CTX *mem_ctx, - struct security_descriptor **ppdesc, - uint16_t *p_hash_type, - uint8_t hash[XATTR_SD_HASH_SIZE]) + struct security_descriptor **ppdesc, + uint16_t *p_hash_type, + uint16_t *p_version, + uint8_t hash[XATTR_SD_HASH_SIZE], + uint8_t sys_acl_hash[XATTR_SD_HASH_SIZE]) { struct xattr_NTACL xacl; enum ndr_err_code ndr_err; @@ -106,6 +108,8 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, return ndr_map_error2ntstatus(ndr_err); } + *p_version = xacl.version; + switch (xacl.version) { case 1: *ppdesc = make_sec_desc(mem_ctx, SD_REVISION, @@ -140,9 +144,22 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, xacl.info.sd_hs3->sd->dacl, &sd_size); *p_hash_type = xacl.info.sd_hs3->hash_type; - /* Current version 3. */ + /* Current version 3 (if no sys acl hash available). */ memcpy(hash, xacl.info.sd_hs3->hash, XATTR_SD_HASH_SIZE); break; + case 4: + *ppdesc = make_sec_desc(mem_ctx, SD_REVISION, + xacl.info.sd_hs4->sd->type | SEC_DESC_SELF_RELATIVE, + xacl.info.sd_hs4->sd->owner_sid, + xacl.info.sd_hs4->sd->group_sid, + xacl.info.sd_hs4->sd->sacl, + xacl.info.sd_hs4->sd->dacl, + &sd_size); + *p_hash_type = xacl.info.sd_hs4->hash_type; + /* Current version 4. */ + memcpy(hash, xacl.info.sd_hs4->hash, XATTR_SD_HASH_SIZE); + memcpy(sys_acl_hash, xacl.info.sd_hs4->sys_acl_hash, XATTR_SD_HASH_SIZE); + break; default: TALLOC_FREE(frame); return NT_STATUS_REVISION_MISMATCH; @@ -154,7 +171,8 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, } /******************************************************************* - Create a DATA_BLOB from a security descriptor. + Create a DATA_BLOB from a hash of the security descriptor storead at + the system layer and the NT ACL we wish to preserve *******************************************************************/ static NTSTATUS create_acl_blob(const struct security_descriptor *psd, @@ -190,6 +208,52 @@ static NTSTATUS create_acl_blob(const struct security_descriptor *psd, } /******************************************************************* + Create a DATA_BLOB from a hash of the security descriptors + (system and NT) stored at the system layer and the NT ACL we wish + to preserve. +*******************************************************************/ + +static NTSTATUS create_sys_acl_blob(const struct security_descriptor *psd, + DATA_BLOB *pblob, + uint16_t hash_type, + uint8_t hash[XATTR_SD_HASH_SIZE], + const char *description, + uint8_t sys_acl_hash[XATTR_SD_HASH_SIZE]) +{ + struct xattr_NTACL xacl; + 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); + + xacl.version = 4; + xacl.info.sd_hs4 = &sd_hs4; + xacl.info.sd_hs4->sd = discard_const_p(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( + pblob, ctx, &xacl, + (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))); + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + +/******************************************************************* Add in 3 inheritable components for a non-inheritable directory ACL. CREATOR_OWNER/CREATOR_GROUP/WORLD. *******************************************************************/ @@ -307,14 +371,18 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, DATA_BLOB blob = data_blob_null; NTSTATUS status; uint16_t hash_type = XATTR_SD_HASH_TYPE_NONE; + uint16_t xattr_version = 0; uint8_t hash[XATTR_SD_HASH_SIZE]; + uint8_t sys_acl_hash[XATTR_SD_HASH_SIZE]; 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; bool ignore_file_system_acl = lp_parm_bool(SNUM(handle->conn), ACL_MODULE_NAME, "ignore system acls", false); + TALLOC_CTX *frame = talloc_stackframe(); if (fsp && name == NULL) { name = fsp->fsp_name->base_name; @@ -343,10 +411,18 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, "returned %s\n", name, nt_errstr(status))); + TALLOC_FREE(frame); return status; } - status = get_acl_blob(talloc_tos(), handle, fsp, name, &blob); + /* 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 = get_acl_blob(frame, handle, fsp, name, &blob); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("get_nt_acl_internal: get_acl_blob returned %s\n", nt_errstr(status))); @@ -355,7 +431,7 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, } status = parse_acl_blob(&blob, mem_ctx, &psd, - &hash_type, &hash[0]); + &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))); @@ -363,57 +439,125 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, goto out; } - /* Ensure the hash type is one we know. */ - switch (hash_type) { - case XATTR_SD_HASH_TYPE_NONE: - /* No hash, just return blob sd. */ - goto out; - case XATTR_SD_HASH_TYPE_SHA256: - 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 = pdesc_next; + /* 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 + * 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; + case 3: + case 4: + if (ignore_file_system_acl) { goto out; - } + } - if (ignore_file_system_acl) { + 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 = pdesc_next; goto out; } - status = hash_sd_sha256(pdesc_next, hash_tmp); - if (!NT_STATUS_IS_OK(status)) { + /* 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 = pdesc_next; goto out; } - 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; - } + /* determine which type of xattr we got */ + switch (xattr_version) { + 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); + } + + /* 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; + } - /* 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 )); + 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; + } + } - 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); + /* Otherwise, fall though and see if the NT ACL hash matches */ } + case 3: + status = hash_sd_sha256(pdesc_next, hash_tmp); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(psd); + psd = pdesc_next; + goto out; + } + + 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; + } - TALLOC_FREE(psd); - psd = pdesc_next; + /* 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 )); + + 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); + } + TALLOC_FREE(psd); + psd = pdesc_next; + } out: if (psd != pdesc_next) { @@ -432,6 +576,7 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, if (fsp) { status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } psbuf = &fsp->fsp_name->st; @@ -440,6 +585,7 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, name, &sbuf); if (ret == -1) { + TALLOC_FREE(frame); return map_nt_error_from_unix(errno); } } @@ -452,6 +598,7 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, psbuf, &psd); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } } else { @@ -464,6 +611,7 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, psbuf, psd); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } } @@ -491,7 +639,6 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, } TALLOC_FREE(blob.data); - *ppdesc = psd; if (DEBUGLEVEL >= 10) { DEBUG(10,("get_nt_acl_internal: returning acl for %s is:\n", @@ -499,6 +646,10 @@ static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, 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); + + TALLOC_FREE(frame); return NT_STATUS_OK; } @@ -538,11 +689,14 @@ 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 status; - DATA_BLOB blob; + int ret; + DATA_BLOB blob, sys_acl_blob; struct security_descriptor *pdesc_next = NULL; struct security_descriptor *psd = NULL; uint8_t hash[XATTR_SD_HASH_SIZE]; + uint8_t sys_acl_hash[XATTR_SD_HASH_SIZE]; bool chown_needed = false; + char *sys_acl_description; TALLOC_CTX *frame = talloc_stackframe(); if (DEBUGLEVEL >= 10) { @@ -642,19 +796,64 @@ static NTSTATUS fset_nt_acl_common(vfs_handle_struct *handle, files_struct *fsp, return status; } + /* Get the full underlying sd, then hash. */ + ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, + fsp, + frame, + &sys_acl_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) { + 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); + + TALLOC_FREE(frame); + return status; + } + + status = hash_blob_sha256(sys_acl_blob, sys_acl_hash); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + if (DEBUGLEVEL >= 10) { - DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n", + DEBUG(10,("fset_nt_acl_xattr: 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)); + discard_const_p(struct security_descriptor, psd)); - DEBUG(10,("fset_nt_acl_xattr: storing has in xattr sd based on \n")); + DEBUG(10,("fset_nt_acl_xattr: storing hash in xattr sd based on system ACL and:\n")); NDR_PRINT_DEBUG(security_descriptor, - discard_const_p(struct security_descriptor, pdesc_next)); + discard_const_p(struct security_descriptor, pdesc_next)); } - status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash); + + /* 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 + * 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_acl_blob failed\n")); + DEBUG(10, ("fset_nt_acl_xattr: create_sys_acl_blob failed\n")); TALLOC_FREE(frame); return status; } |